@clude/sdk 3.0.4 → 3.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/dist/cli/index.js +1792 -154
- package/dist/mcp/server.js +515 -27
- package/dist/sdk/index.js +517 -29
- package/package.json +8 -10
- package/supabase-schema.sql +107 -0
package/dist/mcp/server.js
CHANGED
|
@@ -964,13 +964,18 @@ async function initDatabase2() {
|
|
|
964
964
|
is_active BOOLEAN DEFAULT TRUE,
|
|
965
965
|
metadata JSONB DEFAULT '{}',
|
|
966
966
|
owner_wallet TEXT,
|
|
967
|
-
privy_did TEXT
|
|
967
|
+
privy_did TEXT,
|
|
968
|
+
email TEXT
|
|
968
969
|
);
|
|
969
970
|
|
|
971
|
+
-- Backfill: older deployments may have agent_keys without the email column.
|
|
972
|
+
ALTER TABLE agent_keys ADD COLUMN IF NOT EXISTS email TEXT;
|
|
973
|
+
|
|
970
974
|
CREATE INDEX IF NOT EXISTS idx_agent_keys_api_key ON agent_keys(api_key);
|
|
971
975
|
CREATE INDEX IF NOT EXISTS idx_agent_keys_owner ON agent_keys(owner_wallet);
|
|
972
976
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_owner_unique ON agent_keys(owner_wallet) WHERE owner_wallet IS NOT NULL;
|
|
973
977
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_privy_did ON agent_keys(privy_did) WHERE privy_did IS NOT NULL;
|
|
978
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_email ON agent_keys(email) WHERE email IS NOT NULL AND is_active = true;
|
|
974
979
|
|
|
975
980
|
-- Cortex recall performance: owner_wallet scoped queries
|
|
976
981
|
CREATE INDEX IF NOT EXISTS idx_cortex_owner_recall ON memories(owner_wallet, decay_factor DESC, created_at DESC);
|
|
@@ -1404,6 +1409,19 @@ async function initDatabase2() {
|
|
|
1404
1409
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
1405
1410
|
);
|
|
1406
1411
|
CREATE INDEX IF NOT EXISTS idx_chat_usage_wallet ON chat_usage(wallet_address, created_at DESC);
|
|
1412
|
+
|
|
1413
|
+
-- Wiki pack installations: which packs (Workspace, Compliance, Sales)
|
|
1414
|
+
-- each wallet has installed. Drives the topic rail in /wiki and
|
|
1415
|
+
-- the auto-categorisation rules applied to incoming memories.
|
|
1416
|
+
CREATE TABLE IF NOT EXISTS wiki_pack_installations (
|
|
1417
|
+
id BIGSERIAL PRIMARY KEY,
|
|
1418
|
+
owner_wallet TEXT NOT NULL,
|
|
1419
|
+
pack_id TEXT NOT NULL,
|
|
1420
|
+
installed_at TIMESTAMPTZ DEFAULT NOW(),
|
|
1421
|
+
UNIQUE (owner_wallet, pack_id)
|
|
1422
|
+
);
|
|
1423
|
+
CREATE INDEX IF NOT EXISTS idx_wiki_pack_installations_owner
|
|
1424
|
+
ON wiki_pack_installations(owner_wallet);
|
|
1407
1425
|
`
|
|
1408
1426
|
});
|
|
1409
1427
|
if (error) {
|
|
@@ -1687,11 +1705,13 @@ var init_openrouter_client = __esm({
|
|
|
1687
1705
|
OPENROUTER_API_URL = "https://openrouter.ai/api/v1";
|
|
1688
1706
|
OPENROUTER_MODELS = {
|
|
1689
1707
|
// Frontier (Anthropic)
|
|
1708
|
+
"claude-opus-4.7": "anthropic/claude-opus-4.7",
|
|
1690
1709
|
"claude-opus-4.6": "anthropic/claude-opus-4.6",
|
|
1691
1710
|
"claude-sonnet-4.6": "anthropic/claude-sonnet-4.6",
|
|
1692
1711
|
"claude-opus-4.5": "anthropic/claude-opus-4.5",
|
|
1693
1712
|
"claude-sonnet-4.5": "anthropic/claude-sonnet-4.5",
|
|
1694
1713
|
// Frontier (Other providers)
|
|
1714
|
+
"gpt-5.5": "openai/gpt-5.5",
|
|
1695
1715
|
"gpt-5.4": "openai/gpt-5.4",
|
|
1696
1716
|
"grok-4.1": "x-ai/grok-4.1-fast",
|
|
1697
1717
|
"gemini-3-pro": "google/gemini-3-pro-preview",
|
|
@@ -1852,8 +1872,8 @@ function deriveRegistryPDA(authority) {
|
|
|
1852
1872
|
);
|
|
1853
1873
|
}
|
|
1854
1874
|
function anchorDiscriminator(name) {
|
|
1855
|
-
const { createHash:
|
|
1856
|
-
const hash =
|
|
1875
|
+
const { createHash: createHash4 } = require("crypto");
|
|
1876
|
+
const hash = createHash4("sha256").update(`global:${name}`).digest();
|
|
1857
1877
|
return hash.subarray(0, 8);
|
|
1858
1878
|
}
|
|
1859
1879
|
function memoryTypeToU8(type) {
|
|
@@ -1925,6 +1945,120 @@ var init_solana_client = __esm({
|
|
|
1925
1945
|
}
|
|
1926
1946
|
});
|
|
1927
1947
|
|
|
1948
|
+
// packages/tokenization/src/content-hash.ts
|
|
1949
|
+
function stableStringify(value) {
|
|
1950
|
+
if (value === null) return "null";
|
|
1951
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1952
|
+
return JSON.stringify(value);
|
|
1953
|
+
}
|
|
1954
|
+
if (Array.isArray(value)) {
|
|
1955
|
+
return "[" + value.map(stableStringify).join(",") + "]";
|
|
1956
|
+
}
|
|
1957
|
+
if (typeof value === "object") {
|
|
1958
|
+
const obj = value;
|
|
1959
|
+
const keys = Object.keys(obj).sort();
|
|
1960
|
+
const pairs = keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k]));
|
|
1961
|
+
return "{" + pairs.join(",") + "}";
|
|
1962
|
+
}
|
|
1963
|
+
throw new Error(`stableStringify: unsupported value of type ${typeof value}`);
|
|
1964
|
+
}
|
|
1965
|
+
function normaliseString(s) {
|
|
1966
|
+
return s.normalize("NFC").trim();
|
|
1967
|
+
}
|
|
1968
|
+
function normaliseNullable(s) {
|
|
1969
|
+
return s === null ? null : normaliseString(s);
|
|
1970
|
+
}
|
|
1971
|
+
function normaliseTags(tags) {
|
|
1972
|
+
const trimmed = tags.map(normaliseString).filter((t) => t.length > 0);
|
|
1973
|
+
return Array.from(new Set(trimmed)).sort();
|
|
1974
|
+
}
|
|
1975
|
+
function canonicaliseMemory(input) {
|
|
1976
|
+
const normalised = {
|
|
1977
|
+
algorithm: HASH_ALGORITHM,
|
|
1978
|
+
content: normaliseString(input.content),
|
|
1979
|
+
created_at: input.created_at,
|
|
1980
|
+
memory_type: input.memory_type,
|
|
1981
|
+
owner_wallet: normaliseNullable(input.owner_wallet),
|
|
1982
|
+
related_user: normaliseNullable(input.related_user),
|
|
1983
|
+
related_wallet: normaliseNullable(input.related_wallet),
|
|
1984
|
+
source: normaliseNullable(input.source),
|
|
1985
|
+
tags: normaliseTags(input.tags)
|
|
1986
|
+
};
|
|
1987
|
+
return stableStringify(normalised);
|
|
1988
|
+
}
|
|
1989
|
+
function memoryContentHash(input) {
|
|
1990
|
+
return (0, import_node_crypto.createHash)("sha256").update(canonicaliseMemory(input), "utf8").digest("hex");
|
|
1991
|
+
}
|
|
1992
|
+
var import_node_crypto, HASH_ALGORITHM;
|
|
1993
|
+
var init_content_hash = __esm({
|
|
1994
|
+
"packages/tokenization/src/content-hash.ts"() {
|
|
1995
|
+
"use strict";
|
|
1996
|
+
import_node_crypto = require("node:crypto");
|
|
1997
|
+
HASH_ALGORITHM = "memory-hash-v1";
|
|
1998
|
+
}
|
|
1999
|
+
});
|
|
2000
|
+
|
|
2001
|
+
// packages/tokenization/src/pack-merkle.ts
|
|
2002
|
+
var init_pack_merkle = __esm({
|
|
2003
|
+
"packages/tokenization/src/pack-merkle.ts"() {
|
|
2004
|
+
"use strict";
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
// packages/tokenization/src/mint-client.ts
|
|
2009
|
+
var init_mint_client = __esm({
|
|
2010
|
+
"packages/tokenization/src/mint-client.ts"() {
|
|
2011
|
+
"use strict";
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
|
|
2015
|
+
// packages/tokenization/src/tokenize-memory.ts
|
|
2016
|
+
var init_tokenize_memory = __esm({
|
|
2017
|
+
"packages/tokenization/src/tokenize-memory.ts"() {
|
|
2018
|
+
"use strict";
|
|
2019
|
+
init_content_hash();
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
|
|
2023
|
+
// packages/tokenization/src/tokenize-batch.ts
|
|
2024
|
+
var init_tokenize_batch = __esm({
|
|
2025
|
+
"packages/tokenization/src/tokenize-batch.ts"() {
|
|
2026
|
+
"use strict";
|
|
2027
|
+
init_content_hash();
|
|
2028
|
+
init_pack_merkle();
|
|
2029
|
+
}
|
|
2030
|
+
});
|
|
2031
|
+
|
|
2032
|
+
// packages/tokenization/src/tokenize-pack.ts
|
|
2033
|
+
var init_tokenize_pack = __esm({
|
|
2034
|
+
"packages/tokenization/src/tokenize-pack.ts"() {
|
|
2035
|
+
"use strict";
|
|
2036
|
+
init_pack_merkle();
|
|
2037
|
+
}
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
// packages/tokenization/src/verify.ts
|
|
2041
|
+
var init_verify = __esm({
|
|
2042
|
+
"packages/tokenization/src/verify.ts"() {
|
|
2043
|
+
"use strict";
|
|
2044
|
+
init_pack_merkle();
|
|
2045
|
+
}
|
|
2046
|
+
});
|
|
2047
|
+
|
|
2048
|
+
// packages/tokenization/src/index.ts
|
|
2049
|
+
var init_src = __esm({
|
|
2050
|
+
"packages/tokenization/src/index.ts"() {
|
|
2051
|
+
"use strict";
|
|
2052
|
+
init_content_hash();
|
|
2053
|
+
init_pack_merkle();
|
|
2054
|
+
init_mint_client();
|
|
2055
|
+
init_tokenize_memory();
|
|
2056
|
+
init_tokenize_batch();
|
|
2057
|
+
init_tokenize_pack();
|
|
2058
|
+
init_verify();
|
|
2059
|
+
}
|
|
2060
|
+
});
|
|
2061
|
+
|
|
1928
2062
|
// packages/shared/src/core/embeddings.ts
|
|
1929
2063
|
function getCachedEmbedding(text) {
|
|
1930
2064
|
const key = text.slice(0, 500).toLowerCase().trim();
|
|
@@ -2099,6 +2233,221 @@ var init_embeddings = __esm({
|
|
|
2099
2233
|
}
|
|
2100
2234
|
});
|
|
2101
2235
|
|
|
2236
|
+
// packages/shared/src/wiki-packs.ts
|
|
2237
|
+
function getPackManifest(id) {
|
|
2238
|
+
return ALL_PACK_MANIFESTS.find((p) => p.id === id);
|
|
2239
|
+
}
|
|
2240
|
+
function matchesKeyword(text, phrase) {
|
|
2241
|
+
if (!phrase) return false;
|
|
2242
|
+
const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2243
|
+
const re = new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i");
|
|
2244
|
+
return re.test(text);
|
|
2245
|
+
}
|
|
2246
|
+
function autoCategorizeTags(opts) {
|
|
2247
|
+
const haystack = [
|
|
2248
|
+
opts.content || "",
|
|
2249
|
+
opts.summary || "",
|
|
2250
|
+
...opts.existingTags || []
|
|
2251
|
+
].join(" ");
|
|
2252
|
+
const matched = new Set(opts.existingTags || []);
|
|
2253
|
+
const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...opts.installedPackIds]);
|
|
2254
|
+
for (const packId of ids) {
|
|
2255
|
+
const pack = getPackManifest(packId);
|
|
2256
|
+
if (!pack) continue;
|
|
2257
|
+
for (const rule of pack.rules) {
|
|
2258
|
+
if (matched.has(rule.topicId)) continue;
|
|
2259
|
+
const hasMatch = rule.keywords.some((kw) => matchesKeyword(haystack, kw));
|
|
2260
|
+
if (!hasMatch) continue;
|
|
2261
|
+
const isVetoed = (rule.excludeKeywords || []).some((kw) => matchesKeyword(haystack, kw));
|
|
2262
|
+
if (isVetoed) continue;
|
|
2263
|
+
matched.add(rule.topicId);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
return Array.from(matched);
|
|
2267
|
+
}
|
|
2268
|
+
function topicEmbedSources(installedPackIds) {
|
|
2269
|
+
const out = [];
|
|
2270
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2271
|
+
const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...installedPackIds]);
|
|
2272
|
+
for (const packId of ids) {
|
|
2273
|
+
const pack = getPackManifest(packId);
|
|
2274
|
+
if (!pack) continue;
|
|
2275
|
+
for (const t of pack.topics) {
|
|
2276
|
+
if (seen.has(t.id)) continue;
|
|
2277
|
+
seen.add(t.id);
|
|
2278
|
+
const sectionTitles = (t.sectionTemplates || []).map((s) => s.title).join(". ");
|
|
2279
|
+
const text = [t.name, t.summary, sectionTitles].filter(Boolean).join(" \u2014 ");
|
|
2280
|
+
out.push({ topicId: t.id, packId, text });
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
return out;
|
|
2284
|
+
}
|
|
2285
|
+
function cosineSimilarity2(a, b) {
|
|
2286
|
+
if (a.length !== b.length || a.length === 0) return 0;
|
|
2287
|
+
let dot = 0;
|
|
2288
|
+
let normA = 0;
|
|
2289
|
+
let normB = 0;
|
|
2290
|
+
for (let i = 0; i < a.length; i++) {
|
|
2291
|
+
dot += a[i] * b[i];
|
|
2292
|
+
normA += a[i] * a[i];
|
|
2293
|
+
normB += b[i] * b[i];
|
|
2294
|
+
}
|
|
2295
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
2296
|
+
return denom === 0 ? 0 : dot / denom;
|
|
2297
|
+
}
|
|
2298
|
+
function semanticTagMatches(memoryEmbedding, topicEmbeddings, threshold = EMBEDDING_TAG_THRESHOLD) {
|
|
2299
|
+
const matched = [];
|
|
2300
|
+
for (const { topicId, embedding } of topicEmbeddings) {
|
|
2301
|
+
const sim = cosineSimilarity2(memoryEmbedding, embedding);
|
|
2302
|
+
if (sim >= threshold) matched.push(topicId);
|
|
2303
|
+
}
|
|
2304
|
+
return matched;
|
|
2305
|
+
}
|
|
2306
|
+
var WORKSPACE_PACK, COMPLIANCE_PACK, SALES_PACK, ALL_PACK_MANIFESTS, DEFAULT_PACK_ID, EMBEDDING_TAG_THRESHOLD;
|
|
2307
|
+
var init_wiki_packs = __esm({
|
|
2308
|
+
"packages/shared/src/wiki-packs.ts"() {
|
|
2309
|
+
"use strict";
|
|
2310
|
+
WORKSPACE_PACK = {
|
|
2311
|
+
id: "workspace",
|
|
2312
|
+
name: "Workspace Essentials",
|
|
2313
|
+
vendor: "Clude",
|
|
2314
|
+
vertical: "General",
|
|
2315
|
+
version: "1.0.0",
|
|
2316
|
+
installedByDefault: true,
|
|
2317
|
+
description: "The default knowledge-worker pack. Roadmaps, decisions, customer research, hiring, team process \u2014 everything you'd want a colleague to be able to skim.",
|
|
2318
|
+
topics: [
|
|
2319
|
+
{ id: "q3-roadmap", name: "Q3 Roadmap", cluster: "architecture", color: "#2244FF", summary: "What ships this quarter, what got cut, and the calls behind those decisions." },
|
|
2320
|
+
{ id: "auth-migration", name: "Auth Migration", cluster: "architecture", color: "#2244FF", summary: "Moving from session cookies to JWT \u2014 incidents, fixes, and the rollback plan." },
|
|
2321
|
+
{ id: "customer-research", name: "Customer Research", cluster: "research", color: "#F59E0B", summary: "Patterns surfaced across customer calls." },
|
|
2322
|
+
{ id: "pricing-model", name: "Pricing Model", cluster: "product", color: "#10B981", summary: "Per-token vs per-seat \u2014 the active disagreement." },
|
|
2323
|
+
{ id: "demo-day-prep", name: "Demo Day Prep", cluster: "product", color: "#10B981", summary: "What works in the live demo, what breaks." },
|
|
2324
|
+
{ id: "hiring", name: "Hiring Pipeline", cluster: "product", color: "#10B981", summary: "Candidates in flight and interview-signal patterns." },
|
|
2325
|
+
{ id: "team-process", name: "Team Process", cluster: "self", color: "#8B5CF6", summary: "What's working in how the team operates and what isn't." },
|
|
2326
|
+
{ id: "design-decisions", name: "Design Decisions", cluster: "research", color: "#F59E0B", summary: "API shapes, naming choices, and the reasons behind them." }
|
|
2327
|
+
],
|
|
2328
|
+
rules: [
|
|
2329
|
+
{ topicId: "q3-roadmap", keywords: ["roadmap", "q3", "quarter", "sprint plan", "priority"] },
|
|
2330
|
+
{ topicId: "auth-migration", keywords: ["auth", "jwt", "session cookie", "oauth", "sso", "token migration"] },
|
|
2331
|
+
{ topicId: "customer-research", keywords: ["customer call", "interview", "feedback", "user research", "persona"] },
|
|
2332
|
+
{ topicId: "pricing-model", keywords: ["pricing", "per-token", "per-seat", "subscription", "billing tier"] },
|
|
2333
|
+
{ topicId: "demo-day-prep", keywords: ["demo", "rehearsal", "investor", "pitch"] },
|
|
2334
|
+
{
|
|
2335
|
+
topicId: "hiring",
|
|
2336
|
+
keywords: ["candidate", "interview", "hire", "phone screen", "onsite", "offer"],
|
|
2337
|
+
// Suppress when "hire" is in the wrong context — got fired, rejected an offer,
|
|
2338
|
+
// hire-purchase, etc. The classic substring-match false positive.
|
|
2339
|
+
excludeKeywords: ["fired me", "i was fired", "hire purchase", "rejected my offer", "rejected the offer"]
|
|
2340
|
+
},
|
|
2341
|
+
{ topicId: "team-process", keywords: ["standup", "retro", "sprint", "1:1", "process", "team"] },
|
|
2342
|
+
{ topicId: "design-decisions", keywords: ["api design", "schema", "naming", "rfc", "design doc"] }
|
|
2343
|
+
]
|
|
2344
|
+
};
|
|
2345
|
+
COMPLIANCE_PACK = {
|
|
2346
|
+
id: "compliance",
|
|
2347
|
+
name: "Clude Compliance",
|
|
2348
|
+
vendor: "Clude",
|
|
2349
|
+
vertical: "Compliance",
|
|
2350
|
+
version: "1.0.0",
|
|
2351
|
+
description: "Auto-organises every compliance-relevant decision your agents make into an audit-ready wiki. Audit logs, evidence collection, regulator asks, policy decisions \u2014 each with a cryptographic receipt anchored to Solana.",
|
|
2352
|
+
topics: [
|
|
2353
|
+
{
|
|
2354
|
+
id: "audit-logs",
|
|
2355
|
+
name: "Audit Logs",
|
|
2356
|
+
cluster: "architecture",
|
|
2357
|
+
color: "#0EA5E9",
|
|
2358
|
+
summary: "Every flagged agent action with attribution, timestamp, and a Solana-anchored hash a regulator can verify without trusting us.",
|
|
2359
|
+
sectionTemplates: [
|
|
2360
|
+
{ id: "recent-events", title: "Recent flagged events", kind: "overview" },
|
|
2361
|
+
{ id: "anomalies", title: "Anomalies & escalations", kind: "concern" },
|
|
2362
|
+
{ id: "retention", title: "Retention policy", kind: "decision" }
|
|
2363
|
+
]
|
|
2364
|
+
},
|
|
2365
|
+
{
|
|
2366
|
+
id: "evidence",
|
|
2367
|
+
name: "Evidence Collection",
|
|
2368
|
+
cluster: "product",
|
|
2369
|
+
color: "#06B6D4",
|
|
2370
|
+
summary: "Documentation gathered for SOC2, HIPAA, and ISO 27001 \u2014 what we have, what we still need, and what auditors have already accepted.",
|
|
2371
|
+
sectionTemplates: [
|
|
2372
|
+
{ id: "have", title: "What we have", kind: "highlight" },
|
|
2373
|
+
{ id: "gaps", title: "Gaps to fill", kind: "action" },
|
|
2374
|
+
{ id: "accepted", title: "What auditors accepted", kind: "decision" }
|
|
2375
|
+
]
|
|
2376
|
+
},
|
|
2377
|
+
{
|
|
2378
|
+
id: "regulator-asks",
|
|
2379
|
+
name: "Regulator Asks",
|
|
2380
|
+
cluster: "research",
|
|
2381
|
+
color: "#F59E0B",
|
|
2382
|
+
summary: "Specific requests from regulators \u2014 owner, deadline, response status. Nothing falls through the cracks.",
|
|
2383
|
+
sectionTemplates: [
|
|
2384
|
+
{ id: "open", title: "Open requests", kind: "action" },
|
|
2385
|
+
{ id: "responded", title: "Responded", kind: "decision" },
|
|
2386
|
+
{ id: "patterns", title: "Recurring patterns", kind: "highlight" }
|
|
2387
|
+
]
|
|
2388
|
+
},
|
|
2389
|
+
{
|
|
2390
|
+
id: "policy-decisions",
|
|
2391
|
+
name: "Policy Decisions",
|
|
2392
|
+
cluster: "self",
|
|
2393
|
+
color: "#8B5CF6",
|
|
2394
|
+
summary: "Calls made about what the agents are allowed to do \u2014 data handling, escalation triggers, redaction rules \u2014 each anchored on-chain.",
|
|
2395
|
+
sectionTemplates: [
|
|
2396
|
+
{ id: "standing", title: "Standing policy", kind: "decision" },
|
|
2397
|
+
{ id: "changes", title: "Recent changes", kind: "overview" },
|
|
2398
|
+
{ id: "open", title: "Open questions", kind: "question" }
|
|
2399
|
+
]
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
id: "soc2-status",
|
|
2403
|
+
name: "SOC2 Status",
|
|
2404
|
+
cluster: "product",
|
|
2405
|
+
color: "#10B981",
|
|
2406
|
+
summary: "Where we are in the SOC2 audit cycle, what controls are passing, what still needs evidence.",
|
|
2407
|
+
sectionTemplates: [
|
|
2408
|
+
{ id: "controls", title: "Control status", kind: "overview" },
|
|
2409
|
+
{ id: "next", title: "Up next", kind: "action" }
|
|
2410
|
+
]
|
|
2411
|
+
}
|
|
2412
|
+
],
|
|
2413
|
+
rules: [
|
|
2414
|
+
{ topicId: "audit-logs", keywords: ["audit", "audit log", "trail", "attribution", "flagged"] },
|
|
2415
|
+
{ topicId: "evidence", keywords: ["evidence", "soc2", "hipaa", "iso 27001", "compliance evidence", "control"] },
|
|
2416
|
+
{ topicId: "regulator-asks", keywords: ["regulator", "subpoena", "compliance request", "data request"] },
|
|
2417
|
+
{ topicId: "policy-decisions", keywords: ["policy", "redaction", "pii", "escalation rule", "data handling"] },
|
|
2418
|
+
{ topicId: "soc2-status", keywords: ["soc2", "audit cycle", "control test"] }
|
|
2419
|
+
]
|
|
2420
|
+
};
|
|
2421
|
+
SALES_PACK = {
|
|
2422
|
+
id: "sales",
|
|
2423
|
+
name: "Sales Intelligence",
|
|
2424
|
+
vendor: "Clude",
|
|
2425
|
+
vertical: "Sales",
|
|
2426
|
+
version: "1.0.0",
|
|
2427
|
+
description: "Auto-organises pipeline conversations, deal blockers, objection patterns, and post-call follow-ups. Built for AEs who hate CRM data entry.",
|
|
2428
|
+
topics: [
|
|
2429
|
+
{ id: "pipeline", name: "Pipeline", cluster: "product", color: "#10B981", summary: "Active deals, stages, blockers." },
|
|
2430
|
+
{ id: "objections", name: "Objections", cluster: "research", color: "#F59E0B", summary: "Patterns across discovery calls." },
|
|
2431
|
+
{ id: "follow-ups", name: "Follow-ups", cluster: "product", color: "#10B981", summary: "What you committed to send and to whom." },
|
|
2432
|
+
{ id: "champions", name: "Champions", cluster: "self", color: "#8B5CF6", summary: "Who is championing your product internally at each account." }
|
|
2433
|
+
],
|
|
2434
|
+
rules: [
|
|
2435
|
+
{ topicId: "pipeline", keywords: ["deal", "opportunity", "stage", "close date", "mql", "sql"] },
|
|
2436
|
+
{ topicId: "objections", keywords: ["objection", "concern raised", "pushback", "competitor mentioned"] },
|
|
2437
|
+
{ topicId: "follow-ups", keywords: ["follow up", "send", "next step", "committed to"] },
|
|
2438
|
+
{ topicId: "champions", keywords: ["champion", "sponsor", "advocate", "introduced me to"] }
|
|
2439
|
+
]
|
|
2440
|
+
};
|
|
2441
|
+
ALL_PACK_MANIFESTS = [
|
|
2442
|
+
WORKSPACE_PACK,
|
|
2443
|
+
COMPLIANCE_PACK,
|
|
2444
|
+
SALES_PACK
|
|
2445
|
+
];
|
|
2446
|
+
DEFAULT_PACK_ID = "workspace";
|
|
2447
|
+
EMBEDDING_TAG_THRESHOLD = 0.78;
|
|
2448
|
+
}
|
|
2449
|
+
});
|
|
2450
|
+
|
|
2102
2451
|
// packages/brain/src/experimental/config.ts
|
|
2103
2452
|
function envBool(key, fallback) {
|
|
2104
2453
|
const val = process.env[key];
|
|
@@ -2540,6 +2889,7 @@ __export(memory_exports, {
|
|
|
2540
2889
|
getSelfModel: () => getSelfModel,
|
|
2541
2890
|
hydrateMemories: () => hydrateMemories,
|
|
2542
2891
|
inferConcepts: () => inferConcepts,
|
|
2892
|
+
invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
|
|
2543
2893
|
isValidHashId: () => isValidHashId,
|
|
2544
2894
|
listMemories: () => listMemories,
|
|
2545
2895
|
markJepaQueried: () => markJepaQueried,
|
|
@@ -2642,13 +2992,83 @@ function isDuplicateWrite(source, summary) {
|
|
|
2642
2992
|
}
|
|
2643
2993
|
return false;
|
|
2644
2994
|
}
|
|
2995
|
+
async function getInstalledPackIdsCached(ownerWallet) {
|
|
2996
|
+
const key = ownerWallet ?? "__no_owner__";
|
|
2997
|
+
const now = Date.now();
|
|
2998
|
+
const cached = installedPacksCache.get(key);
|
|
2999
|
+
if (cached && cached.expiresAt > now) return cached.ids;
|
|
3000
|
+
let ids = [DEFAULT_PACK_ID];
|
|
3001
|
+
if (ownerWallet) {
|
|
3002
|
+
try {
|
|
3003
|
+
const db = getDb();
|
|
3004
|
+
const { data, error } = await db.from("wiki_pack_installations").select("pack_id").eq("owner_wallet", ownerWallet);
|
|
3005
|
+
if (!error && data) {
|
|
3006
|
+
const fromDb = data.map((r) => r.pack_id);
|
|
3007
|
+
ids = Array.from(/* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...fromDb]));
|
|
3008
|
+
}
|
|
3009
|
+
} catch (err) {
|
|
3010
|
+
log10.debug({ err }, "Failed to fetch installed packs (using default only)");
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
installedPacksCache.set(key, { ids, expiresAt: now + INSTALLED_PACKS_TTL_MS });
|
|
3014
|
+
if (installedPacksCache.size > 200) {
|
|
3015
|
+
for (const [k, v] of installedPacksCache) {
|
|
3016
|
+
if (v.expiresAt <= now) installedPacksCache.delete(k);
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
return ids;
|
|
3020
|
+
}
|
|
3021
|
+
function invalidateInstalledPacksCache(ownerWallet) {
|
|
3022
|
+
if (ownerWallet) installedPacksCache.delete(ownerWallet);
|
|
3023
|
+
else installedPacksCache.clear();
|
|
3024
|
+
}
|
|
3025
|
+
async function ensureTopicEmbeddings(installedPackIds) {
|
|
3026
|
+
const sources = topicEmbedSources(installedPackIds);
|
|
3027
|
+
const missing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
|
|
3028
|
+
if (missing.length === 0) {
|
|
3029
|
+
return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
|
|
3030
|
+
}
|
|
3031
|
+
if (topicEmbeddingPopulationLock) {
|
|
3032
|
+
await topicEmbeddingPopulationLock;
|
|
3033
|
+
}
|
|
3034
|
+
const stillMissing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
|
|
3035
|
+
if (stillMissing.length === 0) {
|
|
3036
|
+
return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
|
|
3037
|
+
}
|
|
3038
|
+
topicEmbeddingPopulationLock = (async () => {
|
|
3039
|
+
try {
|
|
3040
|
+
const texts = stillMissing.map((s) => s.text);
|
|
3041
|
+
const embeddings = await generateEmbeddings(texts);
|
|
3042
|
+
stillMissing.forEach((s, i) => {
|
|
3043
|
+
const emb = embeddings[i];
|
|
3044
|
+
if (emb) topicEmbeddingCache.set(s.topicId, emb);
|
|
3045
|
+
});
|
|
3046
|
+
} catch (err) {
|
|
3047
|
+
log10.warn({ err }, "Failed to populate topic embeddings cache");
|
|
3048
|
+
} finally {
|
|
3049
|
+
topicEmbeddingPopulationLock = null;
|
|
3050
|
+
}
|
|
3051
|
+
})();
|
|
3052
|
+
await topicEmbeddingPopulationLock;
|
|
3053
|
+
return new Map(
|
|
3054
|
+
sources.filter((s) => topicEmbeddingCache.has(s.topicId)).map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)])
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
2645
3057
|
async function storeMemory(opts) {
|
|
2646
3058
|
if (opts.source.startsWith("shiro_") && isDuplicateWrite(opts.source, opts.summary)) {
|
|
2647
3059
|
log10.debug({ source: opts.source, summary: opts.summary.slice(0, 60) }, "Skipping duplicate memory write");
|
|
2648
3060
|
return null;
|
|
2649
3061
|
}
|
|
2650
3062
|
const db = getDb();
|
|
3063
|
+
const ownerWallet = getOwnerWallet();
|
|
2651
3064
|
const concepts = opts.concepts || inferConcepts(opts.summary, opts.source, opts.tags || []);
|
|
3065
|
+
const installedPackIds = await getInstalledPackIdsCached(ownerWallet);
|
|
3066
|
+
const taggedTags = autoCategorizeTags({
|
|
3067
|
+
content: opts.content,
|
|
3068
|
+
summary: opts.summary,
|
|
3069
|
+
existingTags: opts.tags || [],
|
|
3070
|
+
installedPackIds
|
|
3071
|
+
});
|
|
2652
3072
|
const hashId = generateHashId();
|
|
2653
3073
|
try {
|
|
2654
3074
|
const plaintextContent = opts.content.slice(0, MEMORY_MAX_CONTENT_LENGTH);
|
|
@@ -2659,7 +3079,7 @@ async function storeMemory(opts) {
|
|
|
2659
3079
|
memory_type: opts.type,
|
|
2660
3080
|
content: storedContent,
|
|
2661
3081
|
summary: opts.summary.slice(0, MEMORY_MAX_SUMMARY_LENGTH),
|
|
2662
|
-
tags:
|
|
3082
|
+
tags: taggedTags,
|
|
2663
3083
|
concepts,
|
|
2664
3084
|
emotional_valence: clamp(opts.emotionalValence ?? 0, -1, 1),
|
|
2665
3085
|
importance: clamp(opts.importance ?? 0.5, 0, 1),
|
|
@@ -2672,8 +3092,8 @@ async function storeMemory(opts) {
|
|
|
2672
3092
|
compacted: false,
|
|
2673
3093
|
encrypted: shouldEncrypt,
|
|
2674
3094
|
encryption_pubkey: shouldEncrypt ? getEncryptionPubkey() : null,
|
|
2675
|
-
owner_wallet:
|
|
2676
|
-
}).select("id, hash_id").single();
|
|
3095
|
+
owner_wallet: ownerWallet || null
|
|
3096
|
+
}).select("id, hash_id, content, memory_type, owner_wallet, created_at, tags, source, related_user, related_wallet").single();
|
|
2677
3097
|
if (error) {
|
|
2678
3098
|
log10.error({ error: error.message }, "Failed to store memory");
|
|
2679
3099
|
return null;
|
|
@@ -2691,7 +3111,9 @@ async function storeMemory(opts) {
|
|
|
2691
3111
|
memoryType: opts.type,
|
|
2692
3112
|
source: opts.source
|
|
2693
3113
|
});
|
|
2694
|
-
commitMemoryToChain(data.id, opts).catch(
|
|
3114
|
+
commitMemoryToChain(data.id, opts, data).catch(
|
|
3115
|
+
(err) => log10.warn({ err }, "On-chain memory commit failed")
|
|
3116
|
+
);
|
|
2695
3117
|
embedMemory(data.id, opts).catch((err) => log10.warn({ err }, "Embedding generation failed"));
|
|
2696
3118
|
autoLinkMemory(data.id, opts).catch((err) => log10.warn({ err }, "Auto-linking failed"));
|
|
2697
3119
|
extractAndLinkEntitiesForMemory(data.id, opts).catch((err) => log10.debug({ err }, "Entity extraction failed"));
|
|
@@ -2701,9 +3123,19 @@ async function storeMemory(opts) {
|
|
|
2701
3123
|
return null;
|
|
2702
3124
|
}
|
|
2703
3125
|
}
|
|
2704
|
-
async function commitMemoryToChain(memoryId, opts) {
|
|
2705
|
-
if (
|
|
2706
|
-
const
|
|
3126
|
+
async function commitMemoryToChain(memoryId, opts, row) {
|
|
3127
|
+
if (TOKENISATION_SKIP_SOURCES.has(opts.source)) return;
|
|
3128
|
+
const canonicalHash = memoryContentHash({
|
|
3129
|
+
content: row.content,
|
|
3130
|
+
memory_type: row.memory_type,
|
|
3131
|
+
owner_wallet: row.owner_wallet,
|
|
3132
|
+
created_at: row.created_at,
|
|
3133
|
+
tags: row.tags ?? [],
|
|
3134
|
+
source: row.source,
|
|
3135
|
+
related_user: row.related_user,
|
|
3136
|
+
related_wallet: row.related_wallet
|
|
3137
|
+
});
|
|
3138
|
+
const contentHashBuf = Buffer.from(canonicalHash, "hex");
|
|
2707
3139
|
let signature = null;
|
|
2708
3140
|
if (isRegistryEnabled()) {
|
|
2709
3141
|
const encrypted = isEncryptionEnabled();
|
|
@@ -2716,22 +3148,61 @@ async function commitMemoryToChain(memoryId, opts) {
|
|
|
2716
3148
|
);
|
|
2717
3149
|
}
|
|
2718
3150
|
if (!signature) {
|
|
2719
|
-
const
|
|
2720
|
-
const memo = `clude:v1:sha256:${contentHashHex}`;
|
|
3151
|
+
const memo = `clude:v1:sha256:${canonicalHash}`;
|
|
2721
3152
|
signature = await writeMemo(memo);
|
|
2722
3153
|
}
|
|
2723
|
-
if (!signature) return;
|
|
2724
3154
|
const db = getDb();
|
|
2725
|
-
|
|
2726
|
-
|
|
3155
|
+
if (!signature) {
|
|
3156
|
+
await db.from("memories").update({ content_hash: canonicalHash, tokenization_status: "failed" }).eq("id", memoryId);
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
await db.from("memories").update({
|
|
3160
|
+
solana_signature: signature,
|
|
3161
|
+
content_hash: canonicalHash,
|
|
3162
|
+
cnft_address: signature,
|
|
3163
|
+
// PDA-based v0.1 — assetId = tx sig; LightMintClient (v0.2) will use real cNFT mints
|
|
3164
|
+
cnft_tx_sig: signature,
|
|
3165
|
+
cnft_tree: null,
|
|
3166
|
+
cnft_leaf_index: null,
|
|
3167
|
+
tokenization_status: "minted",
|
|
3168
|
+
tokenized_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3169
|
+
}).eq("id", memoryId);
|
|
3170
|
+
log10.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain (PMP + legacy)");
|
|
2727
3171
|
}
|
|
2728
3172
|
async function embedMemory(memoryId, opts) {
|
|
2729
3173
|
if (!isEmbeddingEnabled()) return;
|
|
2730
3174
|
const db = getDb();
|
|
2731
3175
|
const embeddings = await generateEmbeddings([opts.summary]);
|
|
2732
3176
|
const summaryEmbedding = embeddings[0];
|
|
2733
|
-
if (summaryEmbedding) {
|
|
2734
|
-
|
|
3177
|
+
if (!summaryEmbedding) {
|
|
3178
|
+
log10.debug({ memoryId }, "No embedding generated; skipping semantic tagging");
|
|
3179
|
+
return;
|
|
3180
|
+
}
|
|
3181
|
+
await db.from("memories").update({ embedding: JSON.stringify(summaryEmbedding) }).eq("id", memoryId);
|
|
3182
|
+
try {
|
|
3183
|
+
const ownerWallet = getOwnerWallet();
|
|
3184
|
+
const installedPacks = await getInstalledPackIdsCached(ownerWallet);
|
|
3185
|
+
const topicEmbeddings = await ensureTopicEmbeddings(installedPacks);
|
|
3186
|
+
if (topicEmbeddings.size > 0) {
|
|
3187
|
+
const semantic = semanticTagMatches(
|
|
3188
|
+
summaryEmbedding,
|
|
3189
|
+
Array.from(topicEmbeddings, ([topicId, embedding]) => ({ topicId, embedding }))
|
|
3190
|
+
);
|
|
3191
|
+
if (semantic.length > 0) {
|
|
3192
|
+
const { data: row } = await db.from("memories").select("tags").eq("id", memoryId).maybeSingle();
|
|
3193
|
+
const existing = row?.tags ?? [];
|
|
3194
|
+
const merged = Array.from(/* @__PURE__ */ new Set([...existing, ...semantic]));
|
|
3195
|
+
if (merged.length !== existing.length) {
|
|
3196
|
+
await db.from("memories").update({ tags: merged }).eq("id", memoryId);
|
|
3197
|
+
log10.debug({
|
|
3198
|
+
memoryId,
|
|
3199
|
+
added: semantic.filter((t) => !existing.includes(t))
|
|
3200
|
+
}, "Semantic pack tags appended");
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
} catch (err) {
|
|
3205
|
+
log10.debug({ err, memoryId }, "Semantic tagging skipped");
|
|
2735
3206
|
}
|
|
2736
3207
|
log10.debug({ memoryId }, "Memory embedded");
|
|
2737
3208
|
}
|
|
@@ -3453,25 +3924,31 @@ async function getMemoryStats() {
|
|
|
3453
3924
|
embeddedCount: 0
|
|
3454
3925
|
};
|
|
3455
3926
|
try {
|
|
3456
|
-
let countQuery = db.from("memories").select("id", { count: "exact", head: true })
|
|
3927
|
+
let countQuery = db.from("memories").select("id", { count: "exact", head: true });
|
|
3457
3928
|
countQuery = scopeToOwner(countQuery);
|
|
3458
3929
|
const { count: totalCount } = await countQuery;
|
|
3459
3930
|
stats.total = totalCount || 0;
|
|
3460
|
-
let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).
|
|
3931
|
+
let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).not("embedding", "is", null);
|
|
3461
3932
|
embeddedQuery = scopeToOwner(embeddedQuery);
|
|
3462
3933
|
const { count: embCount } = await embeddedQuery;
|
|
3463
3934
|
stats.embeddedCount = embCount || 0;
|
|
3464
|
-
const
|
|
3935
|
+
const TYPES = ["episodic", "semantic", "procedural", "self_model", "introspective"];
|
|
3936
|
+
await Promise.all(TYPES.map(async (type) => {
|
|
3937
|
+
let q = db.from("memories").select("id", { count: "exact", head: true }).eq("memory_type", type);
|
|
3938
|
+
q = scopeToOwner(q);
|
|
3939
|
+
const { count: count2 } = await q;
|
|
3940
|
+
stats.byType[type] = count2 || 0;
|
|
3941
|
+
}));
|
|
3942
|
+
const PAGE_SIZE = 1e3;
|
|
3943
|
+
const MAX_PAGES = 20;
|
|
3465
3944
|
let allMemories = [];
|
|
3466
|
-
let page = 0;
|
|
3467
|
-
|
|
3468
|
-
let pageQuery = db.from("memories").select("memory_type, importance, decay_factor, created_at, related_user, tags, concepts").gt("decay_factor", MEMORY_MIN_DECAY).range(page * PAGE_SIZE, (page + 1) * PAGE_SIZE - 1);
|
|
3945
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
3946
|
+
let pageQuery = db.from("memories").select("importance, decay_factor, created_at, related_user, tags, concepts").order("id", { ascending: true }).range(page * PAGE_SIZE, (page + 1) * PAGE_SIZE - 1);
|
|
3469
3947
|
pageQuery = scopeToOwner(pageQuery);
|
|
3470
3948
|
const { data: pageData } = await pageQuery;
|
|
3471
3949
|
if (!pageData || pageData.length === 0) break;
|
|
3472
3950
|
allMemories = allMemories.concat(pageData);
|
|
3473
3951
|
if (pageData.length < PAGE_SIZE) break;
|
|
3474
|
-
page++;
|
|
3475
3952
|
}
|
|
3476
3953
|
if (allMemories.length > 0) {
|
|
3477
3954
|
let impSum = 0;
|
|
@@ -3480,8 +3957,6 @@ async function getMemoryStats() {
|
|
|
3480
3957
|
const conceptCounts = {};
|
|
3481
3958
|
const users = /* @__PURE__ */ new Set();
|
|
3482
3959
|
for (const m of allMemories) {
|
|
3483
|
-
const type = m.memory_type;
|
|
3484
|
-
if (type in stats.byType) stats.byType[type]++;
|
|
3485
3960
|
impSum += m.importance;
|
|
3486
3961
|
decaySum += m.decay_factor;
|
|
3487
3962
|
if (m.related_user) users.add(m.related_user);
|
|
@@ -3669,7 +4144,7 @@ function moodToValence(mood) {
|
|
|
3669
4144
|
return 0;
|
|
3670
4145
|
}
|
|
3671
4146
|
}
|
|
3672
|
-
var import_crypto2, EMBED_CACHE_MAX, embeddingCache, _ownerWallet, SCOPE_BOT_OWN, HASH_ID_PREFIX, log10, DEDUP_TTL_MS, dedupCache, STOPWORDS;
|
|
4147
|
+
var import_crypto2, EMBED_CACHE_MAX, embeddingCache, _ownerWallet, SCOPE_BOT_OWN, HASH_ID_PREFIX, log10, DEDUP_TTL_MS, dedupCache, installedPacksCache, INSTALLED_PACKS_TTL_MS, topicEmbeddingCache, topicEmbeddingPopulationLock, TOKENISATION_SKIP_SOURCES, STOPWORDS;
|
|
3673
4148
|
var init_memory = __esm({
|
|
3674
4149
|
"packages/brain/src/memory/memory.ts"() {
|
|
3675
4150
|
"use strict";
|
|
@@ -3678,7 +4153,9 @@ var init_memory = __esm({
|
|
|
3678
4153
|
init_utils();
|
|
3679
4154
|
init_claude_client();
|
|
3680
4155
|
init_solana_client();
|
|
4156
|
+
init_src();
|
|
3681
4157
|
init_embeddings();
|
|
4158
|
+
init_wiki_packs();
|
|
3682
4159
|
init_config2();
|
|
3683
4160
|
init_bm25_search();
|
|
3684
4161
|
init_openrouter_client();
|
|
@@ -3695,6 +4172,16 @@ var init_memory = __esm({
|
|
|
3695
4172
|
log10 = createChildLogger("memory");
|
|
3696
4173
|
DEDUP_TTL_MS = 10 * 60 * 1e3;
|
|
3697
4174
|
dedupCache = /* @__PURE__ */ new Map();
|
|
4175
|
+
installedPacksCache = /* @__PURE__ */ new Map();
|
|
4176
|
+
INSTALLED_PACKS_TTL_MS = 6e4;
|
|
4177
|
+
topicEmbeddingCache = /* @__PURE__ */ new Map();
|
|
4178
|
+
topicEmbeddingPopulationLock = null;
|
|
4179
|
+
TOKENISATION_SKIP_SOURCES = /* @__PURE__ */ new Set([
|
|
4180
|
+
"demo",
|
|
4181
|
+
"demo-maas",
|
|
4182
|
+
"locomo-benchmark",
|
|
4183
|
+
"longmemeval-benchmark"
|
|
4184
|
+
]);
|
|
3698
4185
|
STOPWORDS = /* @__PURE__ */ new Set([
|
|
3699
4186
|
"the",
|
|
3700
4187
|
"a",
|
|
@@ -3800,6 +4287,7 @@ __export(memory_exports2, {
|
|
|
3800
4287
|
getSelfModel: () => getSelfModel,
|
|
3801
4288
|
hydrateMemories: () => hydrateMemories,
|
|
3802
4289
|
inferConcepts: () => inferConcepts,
|
|
4290
|
+
invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
|
|
3803
4291
|
isValidHashId: () => isValidHashId,
|
|
3804
4292
|
listMemories: () => listMemories,
|
|
3805
4293
|
moodToValence: () => moodToValence,
|