@muxoai/sdk 1.2.3 → 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 +22 -0
- package/.claude-plugin/marketplace.json +32 -0
- package/.codex/config.toml +12 -0
- package/ARCHITECTURE.md +121 -0
- package/README.md +85 -31
- package/dist/clerk.d.ts +35 -0
- package/dist/clerk.js +201 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +2380 -0
- package/dist/composio-catalog.d.ts +234 -0
- package/dist/composio-catalog.js +1894 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +115 -0
- package/dist/durable-workflow.d.ts +45 -0
- package/dist/durable-workflow.js +90 -0
- package/dist/engine.d.ts +465 -0
- package/dist/engine.js +2560 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/mcp-schema.d.ts +286 -0
- package/dist/mcp-schema.js +2559 -0
- package/dist/mcp.d.ts +22 -0
- package/dist/mcp.js +1356 -0
- package/dist/memory-graph.d.ts +21 -0
- package/dist/memory-graph.js +173 -0
- package/dist/nl.d.ts +34 -0
- package/dist/nl.js +756 -0
- 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 +18 -0
- package/dist/providers/composio-instant.js +1169 -0
- package/dist/providers/composio-sdk.d.ts +162 -0
- package/dist/providers/composio-sdk.js +1395 -0
- package/dist/providers/microsoft-governance.d.ts +152 -0
- package/dist/providers/microsoft-governance.js +1062 -0
- package/dist/providers/stripe-billing.d.ts +83 -0
- package/dist/providers/stripe-billing.js +611 -0
- package/dist/providers/stripe-projects.d.ts +508 -0
- package/dist/providers/stripe-projects.js +3188 -0
- package/dist/providers/supermemory.d.ts +160 -0
- package/dist/providers/supermemory.js +778 -0
- package/dist/provisioning-managed.d.ts +50 -0
- package/dist/provisioning-managed.js +155 -0
- package/dist/sdk.d.ts +951 -0
- package/dist/sdk.js +748 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +3575 -0
- package/dist/store.d.ts +348 -0
- package/dist/store.js +2323 -0
- package/dist/types.d.ts +593 -0
- package/dist/types.js +1 -0
- package/dist/ui/console-html.d.ts +64 -0
- package/dist/ui/console-html.js +3857 -0
- 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 +68 -7
- package/plugins/muxo/.app.json +8 -0
- package/plugins/muxo/.claude-plugin/plugin.json +16 -0
- package/plugins/muxo/.codex-plugin/plugin.json +64 -0
- package/plugins/muxo/.cursor-plugin/plugin.json +30 -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 +97 -0
- package/plugins/muxo/hooks/user-prompt-submit.sh +58 -0
- package/plugins/muxo/mcp.json +28 -0
- package/plugins/muxo/plugin.json +116 -0
- package/plugins/muxo/skills/muxo/SKILL.md +153 -0
- package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
- package/plugins/muxo/skills/muxo/references/cli.md +63 -0
- package/plugins/muxo/skills/muxo/references/mcp.md +37 -0
- package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
- package/plugins/muxo/skills/setup/SKILL.md +42 -0
- package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
- package/src/catalogues/stripe-projects-catalog.json +9972 -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/vendor/microsoft-governance/package.json +64 -0
- package/index.d.ts +0 -177
- package/index.js +0 -1
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
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
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Supermemory v5 Contextual Brain & Context-Aware Tool Call Adapter.
|
|
23
|
+
* Uses deterministic per-workspace namespaces (`/ns/muxo-v5-<hash>/...`)
|
|
24
|
+
* and strict secret redaction so credentials never enter external memory.
|
|
25
|
+
*/
|
|
26
|
+
export class SupermemoryAdapter {
|
|
27
|
+
config;
|
|
28
|
+
client;
|
|
29
|
+
byokClients = new Map();
|
|
30
|
+
unavailableUntil = 0;
|
|
31
|
+
constructor(config) {
|
|
32
|
+
this.config = config;
|
|
33
|
+
if (config.supermemoryApiKey) {
|
|
34
|
+
this.client = new Supermemory({
|
|
35
|
+
apiKey: config.supermemoryApiKey,
|
|
36
|
+
baseUrl: config.supermemoryBaseUrl,
|
|
37
|
+
maxRetries: 0,
|
|
38
|
+
timeoutInSeconds: 2,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.client = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
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);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Derives a strict Supermemory v5 namespace (`^[a-zA-Z0-9_:-]+$`) scoped to a workspace.
|
|
97
|
+
*/
|
|
98
|
+
namespaceForWorkspace(workspaceId) {
|
|
99
|
+
const hash = crypto
|
|
100
|
+
.createHash("sha256")
|
|
101
|
+
.update(`muxo-v5-ns:${workspaceId}`)
|
|
102
|
+
.digest("hex")
|
|
103
|
+
.slice(0, 24);
|
|
104
|
+
return `muxo-v5-${hash}`;
|
|
105
|
+
}
|
|
106
|
+
getWorkspaceNamespace(workspaceId) {
|
|
107
|
+
return this.namespaceForWorkspace(workspaceId);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Strips any API keys, bearer tokens, connection strings, or .env secret assignments
|
|
111
|
+
* before content is stored in Supermemory (AGENTS.md Invariant #11 & #12).
|
|
112
|
+
*/
|
|
113
|
+
sanitizeContent(raw) {
|
|
114
|
+
return raw
|
|
115
|
+
.replace(/\b(sk_live_|sk_test_|mk_live_|mk_test_|sm_|ak_|uak_|rk_live_|rk_test_|whsec_|ghp_|xoxb-|xoxp-)[A-Za-z0-9_\-]{6,}/g, "[REDACTED_KEY]")
|
|
116
|
+
.replace(/\b(postgres(?:ql)?|mysql|redis|rediss|mongodb(?:\+srv)?):\/\/[^\s"'`]+/gi, "$1://[REDACTED_CONNECTION_URI]")
|
|
117
|
+
.replace(/\b([A-Z0-9_]*(?:SECRET|TOKEN|PASSWORD|API_KEY|PRIVATE_KEY|CONNECTION_STRING)[A-Z0-9_]*)\s*=\s*[^\s\n]+/g, "$1=[REDACTED]");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Fetches the workspace's static + dynamic profile and optionally semantic search results
|
|
121
|
+
* in a single fail-open call. Supports customer BYOK `supermemory` keys via `apiKeyOverride`.
|
|
122
|
+
*/
|
|
123
|
+
async getContextForWorkspace(workspaceIdOrParams, optionalQuery, optionalApiKeyOverride) {
|
|
124
|
+
const params = typeof workspaceIdOrParams === "string"
|
|
125
|
+
? {
|
|
126
|
+
workspaceId: workspaceIdOrParams,
|
|
127
|
+
query: optionalQuery,
|
|
128
|
+
limit: 5,
|
|
129
|
+
apiKeyOverride: optionalApiKeyOverride,
|
|
130
|
+
}
|
|
131
|
+
: workspaceIdOrParams;
|
|
132
|
+
const namespace = this.namespaceForWorkspace(params.workspaceId);
|
|
133
|
+
const { client, fundingSource } = this.resolveClient(params.apiKeyOverride);
|
|
134
|
+
if (!client) {
|
|
135
|
+
return {
|
|
136
|
+
enabled: false,
|
|
137
|
+
namespace,
|
|
138
|
+
fundingSource,
|
|
139
|
+
staticFacts: [],
|
|
140
|
+
dynamicContext: [],
|
|
141
|
+
recalledMemories: [],
|
|
142
|
+
formattedPromptContext: "Supermemory v5 disabled (no API key configured).",
|
|
143
|
+
profileMarkdown: "",
|
|
144
|
+
profile: { static: [], dynamic: [], preferences: [] },
|
|
145
|
+
relevantMemories: [],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const limit = Math.min(10, Math.max(1, Number(params.limit || 5)));
|
|
149
|
+
let staticFacts = [];
|
|
150
|
+
let dynamicFacts = [];
|
|
151
|
+
let preferences = [];
|
|
152
|
+
const relevantMemories = [];
|
|
153
|
+
const [profileOutcome, searchOutcome] = await Promise.allSettled([
|
|
154
|
+
this.withTimeout(() => client.profile(namespace, {
|
|
155
|
+
...(params.query ? { q: params.query } : {}),
|
|
156
|
+
}), 1000),
|
|
157
|
+
params.query && params.query.trim().length > 0
|
|
158
|
+
? this.withTimeout(() => client.search(namespace, {
|
|
159
|
+
query: params.query.trim(),
|
|
160
|
+
limit,
|
|
161
|
+
searchMode: "hybrid",
|
|
162
|
+
include: { documents: true, forgotten: false, related: false },
|
|
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),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const promptLines = [
|
|
192
|
+
`[Supermemory Brain — Namespace: ${namespace}]`,
|
|
193
|
+
];
|
|
194
|
+
if (staticFacts.length > 0) {
|
|
195
|
+
promptLines.push(`Static Facts: ${staticFacts.join(" | ")}`);
|
|
196
|
+
}
|
|
197
|
+
if (dynamicFacts.length > 0) {
|
|
198
|
+
promptLines.push(`Dynamic Context: ${dynamicFacts.join(" | ")}`);
|
|
199
|
+
}
|
|
200
|
+
if (relevantMemories.length > 0) {
|
|
201
|
+
promptLines.push(`Recalled Memories: ${relevantMemories.map((m) => `${m.title}: ${m.snippet}`).join(" | ")}`);
|
|
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
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
enabled: true,
|
|
229
|
+
namespace,
|
|
230
|
+
fundingSource,
|
|
231
|
+
staticFacts,
|
|
232
|
+
dynamicContext: dynamicFacts,
|
|
233
|
+
recalledMemories: relevantMemories,
|
|
234
|
+
formattedPromptContext: promptLines.join("\n"),
|
|
235
|
+
profileMarkdown: mdLines.join("\n"),
|
|
236
|
+
profile: {
|
|
237
|
+
static: staticFacts,
|
|
238
|
+
dynamic: dynamicFacts,
|
|
239
|
+
preferences,
|
|
240
|
+
},
|
|
241
|
+
relevantMemories,
|
|
242
|
+
activeDefaults: inferred.activeDefaults,
|
|
243
|
+
activeGuardrails: inferred.activeGuardrails,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
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.
|
|
249
|
+
*/
|
|
250
|
+
async addMemory(params) {
|
|
251
|
+
const namespace = this.namespaceForWorkspace(params.workspaceId);
|
|
252
|
+
const sanitizedContent = this.sanitizeContent(params.content);
|
|
253
|
+
const docId = params.documentId || `mem_${crypto.randomBytes(8).toString("hex")}`;
|
|
254
|
+
const { client, fundingSource } = this.resolveClient(params.apiKeyOverride);
|
|
255
|
+
if (!client) {
|
|
256
|
+
return {
|
|
257
|
+
documentId: docId,
|
|
258
|
+
namespace,
|
|
259
|
+
status: "local_only",
|
|
260
|
+
sanitizedContent,
|
|
261
|
+
fundingSource,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const res = await this.withTimeout(() => client.add(namespace, {
|
|
265
|
+
id: docId,
|
|
266
|
+
content: `${params.title}\n\n${sanitizedContent}`,
|
|
267
|
+
taskType: "memory",
|
|
268
|
+
dreaming: params.dreaming || "instant",
|
|
269
|
+
metadata: {
|
|
270
|
+
title: params.title,
|
|
271
|
+
muxoWorkspaceId: params.workspaceId,
|
|
272
|
+
muxoKind: params.kind,
|
|
273
|
+
...(params.metadata || {}),
|
|
274
|
+
},
|
|
275
|
+
}), 1500);
|
|
276
|
+
return {
|
|
277
|
+
documentId: String(res?.id || docId),
|
|
278
|
+
namespace,
|
|
279
|
+
status: String(res?.status || "queued"),
|
|
280
|
+
sanitizedContent,
|
|
281
|
+
fundingSource,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
async recordExecutionMemory(params) {
|
|
285
|
+
try {
|
|
286
|
+
const added = await this.addMemory({
|
|
287
|
+
workspaceId: params.workspaceId,
|
|
288
|
+
title: `Execution: ${params.toolSlug} (${params.status})`,
|
|
289
|
+
content: params.summary,
|
|
290
|
+
kind: "execution_outcome",
|
|
291
|
+
apiKeyOverride: params.apiKeyOverride,
|
|
292
|
+
metadata: {
|
|
293
|
+
toolSlug: params.toolSlug,
|
|
294
|
+
lane: params.lane,
|
|
295
|
+
riskLevel: params.riskLevel,
|
|
296
|
+
status: params.status,
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
return added ? added.documentId : null;
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Deletes/forgets a memory document from the workspace's Supermemory v5 namespace.
|
|
307
|
+
*/
|
|
308
|
+
async deleteMemory(workspaceId, documentId, apiKeyOverride) {
|
|
309
|
+
const namespace = this.namespaceForWorkspace(workspaceId);
|
|
310
|
+
const { client } = this.resolveClient(apiKeyOverride);
|
|
311
|
+
if (!client) {
|
|
312
|
+
return { deleted: true, namespace };
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
const res = await this.withTimeout(() => client.documents.delete(namespace, {
|
|
316
|
+
ids: [documentId],
|
|
317
|
+
}), 1200);
|
|
318
|
+
return {
|
|
319
|
+
deleted: (res?.count || 0) >= 1,
|
|
320
|
+
namespace,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
return { deleted: false, namespace };
|
|
325
|
+
}
|
|
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
|
+
};
|
|
778
|
+
}
|