@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.
@@ -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: createHash3 } = require("crypto");
1856
- const hash = createHash3("sha256").update(`global:${name}`).digest();
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: opts.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: getOwnerWallet() || null
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((err) => log10.warn({ err }, "On-chain memory commit failed"));
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 (opts.source === "demo" || opts.source === "demo-maas" || opts.source === "locomo-benchmark" || opts.source === "longmemeval-benchmark") return;
2706
- const contentHashBuf = (0, import_crypto2.createHash)("sha256").update(opts.content).digest();
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 contentHashHex = contentHashBuf.toString("hex");
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
- await db.from("memories").update({ solana_signature: signature }).eq("id", memoryId);
2726
- log10.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain");
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
- await db.from("memories").update({ embedding: JSON.stringify(summaryEmbedding) }).eq("id", memoryId);
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 }).gt("decay_factor", MEMORY_MIN_DECAY);
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 }).gt("decay_factor", MEMORY_MIN_DECAY).not("embedding", "is", null);
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 PAGE_SIZE = 5e3;
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
- while (true) {
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,