@clude/sdk 3.0.3 → 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/sdk/index.js CHANGED
@@ -348,13 +348,18 @@ async function initDatabase() {
348
348
  is_active BOOLEAN DEFAULT TRUE,
349
349
  metadata JSONB DEFAULT '{}',
350
350
  owner_wallet TEXT,
351
- privy_did TEXT
351
+ privy_did TEXT,
352
+ email TEXT
352
353
  );
353
354
 
355
+ -- Backfill: older deployments may have agent_keys without the email column.
356
+ ALTER TABLE agent_keys ADD COLUMN IF NOT EXISTS email TEXT;
357
+
354
358
  CREATE INDEX IF NOT EXISTS idx_agent_keys_api_key ON agent_keys(api_key);
355
359
  CREATE INDEX IF NOT EXISTS idx_agent_keys_owner ON agent_keys(owner_wallet);
356
360
  CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_owner_unique ON agent_keys(owner_wallet) WHERE owner_wallet IS NOT NULL;
357
361
  CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_privy_did ON agent_keys(privy_did) WHERE privy_did IS NOT NULL;
362
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_email ON agent_keys(email) WHERE email IS NOT NULL AND is_active = true;
358
363
 
359
364
  -- Cortex recall performance: owner_wallet scoped queries
360
365
  CREATE INDEX IF NOT EXISTS idx_cortex_owner_recall ON memories(owner_wallet, decay_factor DESC, created_at DESC);
@@ -788,6 +793,19 @@ async function initDatabase() {
788
793
  created_at TIMESTAMPTZ DEFAULT NOW()
789
794
  );
790
795
  CREATE INDEX IF NOT EXISTS idx_chat_usage_wallet ON chat_usage(wallet_address, created_at DESC);
796
+
797
+ -- Wiki pack installations: which packs (Workspace, Compliance, Sales)
798
+ -- each wallet has installed. Drives the topic rail in /wiki and
799
+ -- the auto-categorisation rules applied to incoming memories.
800
+ CREATE TABLE IF NOT EXISTS wiki_pack_installations (
801
+ id BIGSERIAL PRIMARY KEY,
802
+ owner_wallet TEXT NOT NULL,
803
+ pack_id TEXT NOT NULL,
804
+ installed_at TIMESTAMPTZ DEFAULT NOW(),
805
+ UNIQUE (owner_wallet, pack_id)
806
+ );
807
+ CREATE INDEX IF NOT EXISTS idx_wiki_pack_installations_owner
808
+ ON wiki_pack_installations(owner_wallet);
791
809
  `
792
810
  });
793
811
  if (error) {
@@ -1050,11 +1068,13 @@ var init_openrouter_client = __esm({
1050
1068
  OPENROUTER_API_URL = "https://openrouter.ai/api/v1";
1051
1069
  OPENROUTER_MODELS = {
1052
1070
  // Frontier (Anthropic)
1071
+ "claude-opus-4.7": "anthropic/claude-opus-4.7",
1053
1072
  "claude-opus-4.6": "anthropic/claude-opus-4.6",
1054
1073
  "claude-sonnet-4.6": "anthropic/claude-sonnet-4.6",
1055
1074
  "claude-opus-4.5": "anthropic/claude-opus-4.5",
1056
1075
  "claude-sonnet-4.5": "anthropic/claude-sonnet-4.5",
1057
1076
  // Frontier (Other providers)
1077
+ "gpt-5.5": "openai/gpt-5.5",
1058
1078
  "gpt-5.4": "openai/gpt-5.4",
1059
1079
  "grok-4.1": "x-ai/grok-4.1-fast",
1060
1080
  "gemini-3-pro": "google/gemini-3-pro-preview",
@@ -1674,8 +1694,8 @@ function deriveRegistryPDA(authority) {
1674
1694
  );
1675
1695
  }
1676
1696
  function anchorDiscriminator(name) {
1677
- const { createHash: createHash2 } = require("crypto");
1678
- const hash = createHash2("sha256").update(`global:${name}`).digest();
1697
+ const { createHash: createHash3 } = require("crypto");
1698
+ const hash = createHash3("sha256").update(`global:${name}`).digest();
1679
1699
  return hash.subarray(0, 8);
1680
1700
  }
1681
1701
  async function initializeRegistry() {
@@ -1890,6 +1910,335 @@ var init_utils = __esm({
1890
1910
  }
1891
1911
  });
1892
1912
 
1913
+ // packages/tokenization/src/content-hash.ts
1914
+ function stableStringify(value) {
1915
+ if (value === null) return "null";
1916
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1917
+ return JSON.stringify(value);
1918
+ }
1919
+ if (Array.isArray(value)) {
1920
+ return "[" + value.map(stableStringify).join(",") + "]";
1921
+ }
1922
+ if (typeof value === "object") {
1923
+ const obj = value;
1924
+ const keys = Object.keys(obj).sort();
1925
+ const pairs = keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k]));
1926
+ return "{" + pairs.join(",") + "}";
1927
+ }
1928
+ throw new Error(`stableStringify: unsupported value of type ${typeof value}`);
1929
+ }
1930
+ function normaliseString(s) {
1931
+ return s.normalize("NFC").trim();
1932
+ }
1933
+ function normaliseNullable(s) {
1934
+ return s === null ? null : normaliseString(s);
1935
+ }
1936
+ function normaliseTags(tags) {
1937
+ const trimmed = tags.map(normaliseString).filter((t) => t.length > 0);
1938
+ return Array.from(new Set(trimmed)).sort();
1939
+ }
1940
+ function canonicaliseMemory(input) {
1941
+ const normalised = {
1942
+ algorithm: HASH_ALGORITHM,
1943
+ content: normaliseString(input.content),
1944
+ created_at: input.created_at,
1945
+ memory_type: input.memory_type,
1946
+ owner_wallet: normaliseNullable(input.owner_wallet),
1947
+ related_user: normaliseNullable(input.related_user),
1948
+ related_wallet: normaliseNullable(input.related_wallet),
1949
+ source: normaliseNullable(input.source),
1950
+ tags: normaliseTags(input.tags)
1951
+ };
1952
+ return stableStringify(normalised);
1953
+ }
1954
+ function memoryContentHash(input) {
1955
+ return (0, import_node_crypto.createHash)("sha256").update(canonicaliseMemory(input), "utf8").digest("hex");
1956
+ }
1957
+ var import_node_crypto, HASH_ALGORITHM;
1958
+ var init_content_hash = __esm({
1959
+ "packages/tokenization/src/content-hash.ts"() {
1960
+ "use strict";
1961
+ import_node_crypto = require("node:crypto");
1962
+ HASH_ALGORITHM = "memory-hash-v1";
1963
+ }
1964
+ });
1965
+
1966
+ // packages/tokenization/src/pack-merkle.ts
1967
+ var init_pack_merkle = __esm({
1968
+ "packages/tokenization/src/pack-merkle.ts"() {
1969
+ "use strict";
1970
+ }
1971
+ });
1972
+
1973
+ // packages/tokenization/src/mint-client.ts
1974
+ var init_mint_client = __esm({
1975
+ "packages/tokenization/src/mint-client.ts"() {
1976
+ "use strict";
1977
+ }
1978
+ });
1979
+
1980
+ // packages/tokenization/src/tokenize-memory.ts
1981
+ var init_tokenize_memory = __esm({
1982
+ "packages/tokenization/src/tokenize-memory.ts"() {
1983
+ "use strict";
1984
+ init_content_hash();
1985
+ }
1986
+ });
1987
+
1988
+ // packages/tokenization/src/tokenize-batch.ts
1989
+ var init_tokenize_batch = __esm({
1990
+ "packages/tokenization/src/tokenize-batch.ts"() {
1991
+ "use strict";
1992
+ init_content_hash();
1993
+ init_pack_merkle();
1994
+ }
1995
+ });
1996
+
1997
+ // packages/tokenization/src/tokenize-pack.ts
1998
+ var init_tokenize_pack = __esm({
1999
+ "packages/tokenization/src/tokenize-pack.ts"() {
2000
+ "use strict";
2001
+ init_pack_merkle();
2002
+ }
2003
+ });
2004
+
2005
+ // packages/tokenization/src/verify.ts
2006
+ var init_verify = __esm({
2007
+ "packages/tokenization/src/verify.ts"() {
2008
+ "use strict";
2009
+ init_pack_merkle();
2010
+ }
2011
+ });
2012
+
2013
+ // packages/tokenization/src/index.ts
2014
+ var init_src = __esm({
2015
+ "packages/tokenization/src/index.ts"() {
2016
+ "use strict";
2017
+ init_content_hash();
2018
+ init_pack_merkle();
2019
+ init_mint_client();
2020
+ init_tokenize_memory();
2021
+ init_tokenize_batch();
2022
+ init_tokenize_pack();
2023
+ init_verify();
2024
+ }
2025
+ });
2026
+
2027
+ // packages/shared/src/wiki-packs.ts
2028
+ function getPackManifest(id) {
2029
+ return ALL_PACK_MANIFESTS.find((p) => p.id === id);
2030
+ }
2031
+ function matchesKeyword(text, phrase) {
2032
+ if (!phrase) return false;
2033
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2034
+ const re = new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i");
2035
+ return re.test(text);
2036
+ }
2037
+ function autoCategorizeTags(opts) {
2038
+ const haystack = [
2039
+ opts.content || "",
2040
+ opts.summary || "",
2041
+ ...opts.existingTags || []
2042
+ ].join(" ");
2043
+ const matched = new Set(opts.existingTags || []);
2044
+ const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...opts.installedPackIds]);
2045
+ for (const packId of ids) {
2046
+ const pack = getPackManifest(packId);
2047
+ if (!pack) continue;
2048
+ for (const rule of pack.rules) {
2049
+ if (matched.has(rule.topicId)) continue;
2050
+ const hasMatch = rule.keywords.some((kw) => matchesKeyword(haystack, kw));
2051
+ if (!hasMatch) continue;
2052
+ const isVetoed = (rule.excludeKeywords || []).some((kw) => matchesKeyword(haystack, kw));
2053
+ if (isVetoed) continue;
2054
+ matched.add(rule.topicId);
2055
+ }
2056
+ }
2057
+ return Array.from(matched);
2058
+ }
2059
+ function topicEmbedSources(installedPackIds) {
2060
+ const out = [];
2061
+ const seen = /* @__PURE__ */ new Set();
2062
+ const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...installedPackIds]);
2063
+ for (const packId of ids) {
2064
+ const pack = getPackManifest(packId);
2065
+ if (!pack) continue;
2066
+ for (const t of pack.topics) {
2067
+ if (seen.has(t.id)) continue;
2068
+ seen.add(t.id);
2069
+ const sectionTitles = (t.sectionTemplates || []).map((s) => s.title).join(". ");
2070
+ const text = [t.name, t.summary, sectionTitles].filter(Boolean).join(" \u2014 ");
2071
+ out.push({ topicId: t.id, packId, text });
2072
+ }
2073
+ }
2074
+ return out;
2075
+ }
2076
+ function cosineSimilarity(a, b) {
2077
+ if (a.length !== b.length || a.length === 0) return 0;
2078
+ let dot = 0;
2079
+ let normA = 0;
2080
+ let normB = 0;
2081
+ for (let i = 0; i < a.length; i++) {
2082
+ dot += a[i] * b[i];
2083
+ normA += a[i] * a[i];
2084
+ normB += b[i] * b[i];
2085
+ }
2086
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
2087
+ return denom === 0 ? 0 : dot / denom;
2088
+ }
2089
+ function semanticTagMatches(memoryEmbedding, topicEmbeddings, threshold = EMBEDDING_TAG_THRESHOLD) {
2090
+ const matched = [];
2091
+ for (const { topicId, embedding } of topicEmbeddings) {
2092
+ const sim = cosineSimilarity(memoryEmbedding, embedding);
2093
+ if (sim >= threshold) matched.push(topicId);
2094
+ }
2095
+ return matched;
2096
+ }
2097
+ var WORKSPACE_PACK, COMPLIANCE_PACK, SALES_PACK, ALL_PACK_MANIFESTS, DEFAULT_PACK_ID, EMBEDDING_TAG_THRESHOLD;
2098
+ var init_wiki_packs = __esm({
2099
+ "packages/shared/src/wiki-packs.ts"() {
2100
+ "use strict";
2101
+ WORKSPACE_PACK = {
2102
+ id: "workspace",
2103
+ name: "Workspace Essentials",
2104
+ vendor: "Clude",
2105
+ vertical: "General",
2106
+ version: "1.0.0",
2107
+ installedByDefault: true,
2108
+ 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.",
2109
+ topics: [
2110
+ { id: "q3-roadmap", name: "Q3 Roadmap", cluster: "architecture", color: "#2244FF", summary: "What ships this quarter, what got cut, and the calls behind those decisions." },
2111
+ { id: "auth-migration", name: "Auth Migration", cluster: "architecture", color: "#2244FF", summary: "Moving from session cookies to JWT \u2014 incidents, fixes, and the rollback plan." },
2112
+ { id: "customer-research", name: "Customer Research", cluster: "research", color: "#F59E0B", summary: "Patterns surfaced across customer calls." },
2113
+ { id: "pricing-model", name: "Pricing Model", cluster: "product", color: "#10B981", summary: "Per-token vs per-seat \u2014 the active disagreement." },
2114
+ { id: "demo-day-prep", name: "Demo Day Prep", cluster: "product", color: "#10B981", summary: "What works in the live demo, what breaks." },
2115
+ { id: "hiring", name: "Hiring Pipeline", cluster: "product", color: "#10B981", summary: "Candidates in flight and interview-signal patterns." },
2116
+ { id: "team-process", name: "Team Process", cluster: "self", color: "#8B5CF6", summary: "What's working in how the team operates and what isn't." },
2117
+ { id: "design-decisions", name: "Design Decisions", cluster: "research", color: "#F59E0B", summary: "API shapes, naming choices, and the reasons behind them." }
2118
+ ],
2119
+ rules: [
2120
+ { topicId: "q3-roadmap", keywords: ["roadmap", "q3", "quarter", "sprint plan", "priority"] },
2121
+ { topicId: "auth-migration", keywords: ["auth", "jwt", "session cookie", "oauth", "sso", "token migration"] },
2122
+ { topicId: "customer-research", keywords: ["customer call", "interview", "feedback", "user research", "persona"] },
2123
+ { topicId: "pricing-model", keywords: ["pricing", "per-token", "per-seat", "subscription", "billing tier"] },
2124
+ { topicId: "demo-day-prep", keywords: ["demo", "rehearsal", "investor", "pitch"] },
2125
+ {
2126
+ topicId: "hiring",
2127
+ keywords: ["candidate", "interview", "hire", "phone screen", "onsite", "offer"],
2128
+ // Suppress when "hire" is in the wrong context — got fired, rejected an offer,
2129
+ // hire-purchase, etc. The classic substring-match false positive.
2130
+ excludeKeywords: ["fired me", "i was fired", "hire purchase", "rejected my offer", "rejected the offer"]
2131
+ },
2132
+ { topicId: "team-process", keywords: ["standup", "retro", "sprint", "1:1", "process", "team"] },
2133
+ { topicId: "design-decisions", keywords: ["api design", "schema", "naming", "rfc", "design doc"] }
2134
+ ]
2135
+ };
2136
+ COMPLIANCE_PACK = {
2137
+ id: "compliance",
2138
+ name: "Clude Compliance",
2139
+ vendor: "Clude",
2140
+ vertical: "Compliance",
2141
+ version: "1.0.0",
2142
+ 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.",
2143
+ topics: [
2144
+ {
2145
+ id: "audit-logs",
2146
+ name: "Audit Logs",
2147
+ cluster: "architecture",
2148
+ color: "#0EA5E9",
2149
+ summary: "Every flagged agent action with attribution, timestamp, and a Solana-anchored hash a regulator can verify without trusting us.",
2150
+ sectionTemplates: [
2151
+ { id: "recent-events", title: "Recent flagged events", kind: "overview" },
2152
+ { id: "anomalies", title: "Anomalies & escalations", kind: "concern" },
2153
+ { id: "retention", title: "Retention policy", kind: "decision" }
2154
+ ]
2155
+ },
2156
+ {
2157
+ id: "evidence",
2158
+ name: "Evidence Collection",
2159
+ cluster: "product",
2160
+ color: "#06B6D4",
2161
+ summary: "Documentation gathered for SOC2, HIPAA, and ISO 27001 \u2014 what we have, what we still need, and what auditors have already accepted.",
2162
+ sectionTemplates: [
2163
+ { id: "have", title: "What we have", kind: "highlight" },
2164
+ { id: "gaps", title: "Gaps to fill", kind: "action" },
2165
+ { id: "accepted", title: "What auditors accepted", kind: "decision" }
2166
+ ]
2167
+ },
2168
+ {
2169
+ id: "regulator-asks",
2170
+ name: "Regulator Asks",
2171
+ cluster: "research",
2172
+ color: "#F59E0B",
2173
+ summary: "Specific requests from regulators \u2014 owner, deadline, response status. Nothing falls through the cracks.",
2174
+ sectionTemplates: [
2175
+ { id: "open", title: "Open requests", kind: "action" },
2176
+ { id: "responded", title: "Responded", kind: "decision" },
2177
+ { id: "patterns", title: "Recurring patterns", kind: "highlight" }
2178
+ ]
2179
+ },
2180
+ {
2181
+ id: "policy-decisions",
2182
+ name: "Policy Decisions",
2183
+ cluster: "self",
2184
+ color: "#8B5CF6",
2185
+ summary: "Calls made about what the agents are allowed to do \u2014 data handling, escalation triggers, redaction rules \u2014 each anchored on-chain.",
2186
+ sectionTemplates: [
2187
+ { id: "standing", title: "Standing policy", kind: "decision" },
2188
+ { id: "changes", title: "Recent changes", kind: "overview" },
2189
+ { id: "open", title: "Open questions", kind: "question" }
2190
+ ]
2191
+ },
2192
+ {
2193
+ id: "soc2-status",
2194
+ name: "SOC2 Status",
2195
+ cluster: "product",
2196
+ color: "#10B981",
2197
+ summary: "Where we are in the SOC2 audit cycle, what controls are passing, what still needs evidence.",
2198
+ sectionTemplates: [
2199
+ { id: "controls", title: "Control status", kind: "overview" },
2200
+ { id: "next", title: "Up next", kind: "action" }
2201
+ ]
2202
+ }
2203
+ ],
2204
+ rules: [
2205
+ { topicId: "audit-logs", keywords: ["audit", "audit log", "trail", "attribution", "flagged"] },
2206
+ { topicId: "evidence", keywords: ["evidence", "soc2", "hipaa", "iso 27001", "compliance evidence", "control"] },
2207
+ { topicId: "regulator-asks", keywords: ["regulator", "subpoena", "compliance request", "data request"] },
2208
+ { topicId: "policy-decisions", keywords: ["policy", "redaction", "pii", "escalation rule", "data handling"] },
2209
+ { topicId: "soc2-status", keywords: ["soc2", "audit cycle", "control test"] }
2210
+ ]
2211
+ };
2212
+ SALES_PACK = {
2213
+ id: "sales",
2214
+ name: "Sales Intelligence",
2215
+ vendor: "Clude",
2216
+ vertical: "Sales",
2217
+ version: "1.0.0",
2218
+ description: "Auto-organises pipeline conversations, deal blockers, objection patterns, and post-call follow-ups. Built for AEs who hate CRM data entry.",
2219
+ topics: [
2220
+ { id: "pipeline", name: "Pipeline", cluster: "product", color: "#10B981", summary: "Active deals, stages, blockers." },
2221
+ { id: "objections", name: "Objections", cluster: "research", color: "#F59E0B", summary: "Patterns across discovery calls." },
2222
+ { id: "follow-ups", name: "Follow-ups", cluster: "product", color: "#10B981", summary: "What you committed to send and to whom." },
2223
+ { id: "champions", name: "Champions", cluster: "self", color: "#8B5CF6", summary: "Who is championing your product internally at each account." }
2224
+ ],
2225
+ rules: [
2226
+ { topicId: "pipeline", keywords: ["deal", "opportunity", "stage", "close date", "mql", "sql"] },
2227
+ { topicId: "objections", keywords: ["objection", "concern raised", "pushback", "competitor mentioned"] },
2228
+ { topicId: "follow-ups", keywords: ["follow up", "send", "next step", "committed to"] },
2229
+ { topicId: "champions", keywords: ["champion", "sponsor", "advocate", "introduced me to"] }
2230
+ ]
2231
+ };
2232
+ ALL_PACK_MANIFESTS = [
2233
+ WORKSPACE_PACK,
2234
+ COMPLIANCE_PACK,
2235
+ SALES_PACK
2236
+ ];
2237
+ DEFAULT_PACK_ID = "workspace";
2238
+ EMBEDDING_TAG_THRESHOLD = 0.78;
2239
+ }
2240
+ });
2241
+
1893
2242
  // packages/brain/src/experimental/config.ts
1894
2243
  function envBool(key, fallback) {
1895
2244
  const val = process.env[key];
@@ -2365,6 +2714,7 @@ __export(memory_exports, {
2365
2714
  getSelfModel: () => getSelfModel,
2366
2715
  hydrateMemories: () => hydrateMemories,
2367
2716
  inferConcepts: () => inferConcepts,
2717
+ invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
2368
2718
  isValidHashId: () => isValidHashId,
2369
2719
  listMemories: () => listMemories,
2370
2720
  markJepaQueried: () => markJepaQueried,
@@ -2467,13 +2817,83 @@ function isDuplicateWrite(source, summary) {
2467
2817
  }
2468
2818
  return false;
2469
2819
  }
2820
+ async function getInstalledPackIdsCached(ownerWallet) {
2821
+ const key = ownerWallet ?? "__no_owner__";
2822
+ const now = Date.now();
2823
+ const cached = installedPacksCache.get(key);
2824
+ if (cached && cached.expiresAt > now) return cached.ids;
2825
+ let ids = [DEFAULT_PACK_ID];
2826
+ if (ownerWallet) {
2827
+ try {
2828
+ const db = getDb();
2829
+ const { data, error } = await db.from("wiki_pack_installations").select("pack_id").eq("owner_wallet", ownerWallet);
2830
+ if (!error && data) {
2831
+ const fromDb = data.map((r) => r.pack_id);
2832
+ ids = Array.from(/* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...fromDb]));
2833
+ }
2834
+ } catch (err) {
2835
+ log10.debug({ err }, "Failed to fetch installed packs (using default only)");
2836
+ }
2837
+ }
2838
+ installedPacksCache.set(key, { ids, expiresAt: now + INSTALLED_PACKS_TTL_MS });
2839
+ if (installedPacksCache.size > 200) {
2840
+ for (const [k, v] of installedPacksCache) {
2841
+ if (v.expiresAt <= now) installedPacksCache.delete(k);
2842
+ }
2843
+ }
2844
+ return ids;
2845
+ }
2846
+ function invalidateInstalledPacksCache(ownerWallet) {
2847
+ if (ownerWallet) installedPacksCache.delete(ownerWallet);
2848
+ else installedPacksCache.clear();
2849
+ }
2850
+ async function ensureTopicEmbeddings(installedPackIds) {
2851
+ const sources = topicEmbedSources(installedPackIds);
2852
+ const missing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
2853
+ if (missing.length === 0) {
2854
+ return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
2855
+ }
2856
+ if (topicEmbeddingPopulationLock) {
2857
+ await topicEmbeddingPopulationLock;
2858
+ }
2859
+ const stillMissing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
2860
+ if (stillMissing.length === 0) {
2861
+ return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
2862
+ }
2863
+ topicEmbeddingPopulationLock = (async () => {
2864
+ try {
2865
+ const texts = stillMissing.map((s) => s.text);
2866
+ const embeddings = await generateEmbeddings(texts);
2867
+ stillMissing.forEach((s, i) => {
2868
+ const emb = embeddings[i];
2869
+ if (emb) topicEmbeddingCache.set(s.topicId, emb);
2870
+ });
2871
+ } catch (err) {
2872
+ log10.warn({ err }, "Failed to populate topic embeddings cache");
2873
+ } finally {
2874
+ topicEmbeddingPopulationLock = null;
2875
+ }
2876
+ })();
2877
+ await topicEmbeddingPopulationLock;
2878
+ return new Map(
2879
+ sources.filter((s) => topicEmbeddingCache.has(s.topicId)).map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)])
2880
+ );
2881
+ }
2470
2882
  async function storeMemory(opts) {
2471
2883
  if (opts.source.startsWith("shiro_") && isDuplicateWrite(opts.source, opts.summary)) {
2472
2884
  log10.debug({ source: opts.source, summary: opts.summary.slice(0, 60) }, "Skipping duplicate memory write");
2473
2885
  return null;
2474
2886
  }
2475
2887
  const db = getDb();
2888
+ const ownerWallet = getOwnerWallet();
2476
2889
  const concepts = opts.concepts || inferConcepts(opts.summary, opts.source, opts.tags || []);
2890
+ const installedPackIds = await getInstalledPackIdsCached(ownerWallet);
2891
+ const taggedTags = autoCategorizeTags({
2892
+ content: opts.content,
2893
+ summary: opts.summary,
2894
+ existingTags: opts.tags || [],
2895
+ installedPackIds
2896
+ });
2477
2897
  const hashId = generateHashId();
2478
2898
  try {
2479
2899
  const plaintextContent = opts.content.slice(0, MEMORY_MAX_CONTENT_LENGTH);
@@ -2484,7 +2904,7 @@ async function storeMemory(opts) {
2484
2904
  memory_type: opts.type,
2485
2905
  content: storedContent,
2486
2906
  summary: opts.summary.slice(0, MEMORY_MAX_SUMMARY_LENGTH),
2487
- tags: opts.tags || [],
2907
+ tags: taggedTags,
2488
2908
  concepts,
2489
2909
  emotional_valence: clamp(opts.emotionalValence ?? 0, -1, 1),
2490
2910
  importance: clamp(opts.importance ?? 0.5, 0, 1),
@@ -2497,8 +2917,8 @@ async function storeMemory(opts) {
2497
2917
  compacted: false,
2498
2918
  encrypted: shouldEncrypt,
2499
2919
  encryption_pubkey: shouldEncrypt ? getEncryptionPubkey() : null,
2500
- owner_wallet: getOwnerWallet() || null
2501
- }).select("id, hash_id").single();
2920
+ owner_wallet: ownerWallet || null
2921
+ }).select("id, hash_id, content, memory_type, owner_wallet, created_at, tags, source, related_user, related_wallet").single();
2502
2922
  if (error) {
2503
2923
  log10.error({ error: error.message }, "Failed to store memory");
2504
2924
  return null;
@@ -2516,7 +2936,9 @@ async function storeMemory(opts) {
2516
2936
  memoryType: opts.type,
2517
2937
  source: opts.source
2518
2938
  });
2519
- commitMemoryToChain(data.id, opts).catch((err) => log10.warn({ err }, "On-chain memory commit failed"));
2939
+ commitMemoryToChain(data.id, opts, data).catch(
2940
+ (err) => log10.warn({ err }, "On-chain memory commit failed")
2941
+ );
2520
2942
  embedMemory(data.id, opts).catch((err) => log10.warn({ err }, "Embedding generation failed"));
2521
2943
  autoLinkMemory(data.id, opts).catch((err) => log10.warn({ err }, "Auto-linking failed"));
2522
2944
  extractAndLinkEntitiesForMemory(data.id, opts).catch((err) => log10.debug({ err }, "Entity extraction failed"));
@@ -2526,9 +2948,19 @@ async function storeMemory(opts) {
2526
2948
  return null;
2527
2949
  }
2528
2950
  }
2529
- async function commitMemoryToChain(memoryId, opts) {
2530
- if (opts.source === "demo" || opts.source === "demo-maas" || opts.source === "locomo-benchmark" || opts.source === "longmemeval-benchmark") return;
2531
- const contentHashBuf = (0, import_crypto2.createHash)("sha256").update(opts.content).digest();
2951
+ async function commitMemoryToChain(memoryId, opts, row) {
2952
+ if (TOKENISATION_SKIP_SOURCES.has(opts.source)) return;
2953
+ const canonicalHash = memoryContentHash({
2954
+ content: row.content,
2955
+ memory_type: row.memory_type,
2956
+ owner_wallet: row.owner_wallet,
2957
+ created_at: row.created_at,
2958
+ tags: row.tags ?? [],
2959
+ source: row.source,
2960
+ related_user: row.related_user,
2961
+ related_wallet: row.related_wallet
2962
+ });
2963
+ const contentHashBuf = Buffer.from(canonicalHash, "hex");
2532
2964
  let signature = null;
2533
2965
  if (isRegistryEnabled()) {
2534
2966
  const encrypted = isEncryptionEnabled();
@@ -2541,22 +2973,61 @@ async function commitMemoryToChain(memoryId, opts) {
2541
2973
  );
2542
2974
  }
2543
2975
  if (!signature) {
2544
- const contentHashHex = contentHashBuf.toString("hex");
2545
- const memo = `clude-memory | v2 | ${contentHashHex}`;
2976
+ const memo = `clude:v1:sha256:${canonicalHash}`;
2546
2977
  signature = await writeMemo(memo);
2547
2978
  }
2548
- if (!signature) return;
2549
2979
  const db = getDb();
2550
- await db.from("memories").update({ solana_signature: signature }).eq("id", memoryId);
2551
- log10.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain");
2980
+ if (!signature) {
2981
+ await db.from("memories").update({ content_hash: canonicalHash, tokenization_status: "failed" }).eq("id", memoryId);
2982
+ return;
2983
+ }
2984
+ await db.from("memories").update({
2985
+ solana_signature: signature,
2986
+ content_hash: canonicalHash,
2987
+ cnft_address: signature,
2988
+ // PDA-based v0.1 — assetId = tx sig; LightMintClient (v0.2) will use real cNFT mints
2989
+ cnft_tx_sig: signature,
2990
+ cnft_tree: null,
2991
+ cnft_leaf_index: null,
2992
+ tokenization_status: "minted",
2993
+ tokenized_at: (/* @__PURE__ */ new Date()).toISOString()
2994
+ }).eq("id", memoryId);
2995
+ log10.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain (PMP + legacy)");
2552
2996
  }
2553
2997
  async function embedMemory(memoryId, opts) {
2554
2998
  if (!isEmbeddingEnabled()) return;
2555
2999
  const db = getDb();
2556
3000
  const embeddings = await generateEmbeddings([opts.summary]);
2557
3001
  const summaryEmbedding = embeddings[0];
2558
- if (summaryEmbedding) {
2559
- await db.from("memories").update({ embedding: JSON.stringify(summaryEmbedding) }).eq("id", memoryId);
3002
+ if (!summaryEmbedding) {
3003
+ log10.debug({ memoryId }, "No embedding generated; skipping semantic tagging");
3004
+ return;
3005
+ }
3006
+ await db.from("memories").update({ embedding: JSON.stringify(summaryEmbedding) }).eq("id", memoryId);
3007
+ try {
3008
+ const ownerWallet = getOwnerWallet();
3009
+ const installedPacks = await getInstalledPackIdsCached(ownerWallet);
3010
+ const topicEmbeddings = await ensureTopicEmbeddings(installedPacks);
3011
+ if (topicEmbeddings.size > 0) {
3012
+ const semantic = semanticTagMatches(
3013
+ summaryEmbedding,
3014
+ Array.from(topicEmbeddings, ([topicId, embedding]) => ({ topicId, embedding }))
3015
+ );
3016
+ if (semantic.length > 0) {
3017
+ const { data: row } = await db.from("memories").select("tags").eq("id", memoryId).maybeSingle();
3018
+ const existing = row?.tags ?? [];
3019
+ const merged = Array.from(/* @__PURE__ */ new Set([...existing, ...semantic]));
3020
+ if (merged.length !== existing.length) {
3021
+ await db.from("memories").update({ tags: merged }).eq("id", memoryId);
3022
+ log10.debug({
3023
+ memoryId,
3024
+ added: semantic.filter((t) => !existing.includes(t))
3025
+ }, "Semantic pack tags appended");
3026
+ }
3027
+ }
3028
+ }
3029
+ } catch (err) {
3030
+ log10.debug({ err, memoryId }, "Semantic tagging skipped");
2560
3031
  }
2561
3032
  log10.debug({ memoryId }, "Memory embedded");
2562
3033
  }
@@ -3278,25 +3749,31 @@ async function getMemoryStats() {
3278
3749
  embeddedCount: 0
3279
3750
  };
3280
3751
  try {
3281
- let countQuery = db.from("memories").select("id", { count: "exact", head: true }).gt("decay_factor", MEMORY_MIN_DECAY);
3752
+ let countQuery = db.from("memories").select("id", { count: "exact", head: true });
3282
3753
  countQuery = scopeToOwner(countQuery);
3283
3754
  const { count: totalCount } = await countQuery;
3284
3755
  stats.total = totalCount || 0;
3285
- let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).gt("decay_factor", MEMORY_MIN_DECAY).not("embedding", "is", null);
3756
+ let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).not("embedding", "is", null);
3286
3757
  embeddedQuery = scopeToOwner(embeddedQuery);
3287
3758
  const { count: embCount } = await embeddedQuery;
3288
3759
  stats.embeddedCount = embCount || 0;
3289
- const PAGE_SIZE = 5e3;
3760
+ const TYPES = ["episodic", "semantic", "procedural", "self_model", "introspective"];
3761
+ await Promise.all(TYPES.map(async (type) => {
3762
+ let q = db.from("memories").select("id", { count: "exact", head: true }).eq("memory_type", type);
3763
+ q = scopeToOwner(q);
3764
+ const { count: count2 } = await q;
3765
+ stats.byType[type] = count2 || 0;
3766
+ }));
3767
+ const PAGE_SIZE = 1e3;
3768
+ const MAX_PAGES = 20;
3290
3769
  let allMemories = [];
3291
- let page = 0;
3292
- while (true) {
3293
- 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);
3770
+ for (let page = 0; page < MAX_PAGES; page++) {
3771
+ 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);
3294
3772
  pageQuery = scopeToOwner(pageQuery);
3295
3773
  const { data: pageData } = await pageQuery;
3296
3774
  if (!pageData || pageData.length === 0) break;
3297
3775
  allMemories = allMemories.concat(pageData);
3298
3776
  if (pageData.length < PAGE_SIZE) break;
3299
- page++;
3300
3777
  }
3301
3778
  if (allMemories.length > 0) {
3302
3779
  let impSum = 0;
@@ -3305,8 +3782,6 @@ async function getMemoryStats() {
3305
3782
  const conceptCounts = {};
3306
3783
  const users = /* @__PURE__ */ new Set();
3307
3784
  for (const m of allMemories) {
3308
- const type = m.memory_type;
3309
- if (type in stats.byType) stats.byType[type]++;
3310
3785
  impSum += m.importance;
3311
3786
  decaySum += m.decay_factor;
3312
3787
  if (m.related_user) users.add(m.related_user);
@@ -3494,7 +3969,7 @@ function moodToValence(mood) {
3494
3969
  return 0;
3495
3970
  }
3496
3971
  }
3497
- var import_crypto2, EMBED_CACHE_MAX, embeddingCache, _ownerWallet, SCOPE_BOT_OWN, HASH_ID_PREFIX, log10, DEDUP_TTL_MS, dedupCache, STOPWORDS;
3972
+ 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;
3498
3973
  var init_memory = __esm({
3499
3974
  "packages/brain/src/memory/memory.ts"() {
3500
3975
  "use strict";
@@ -3503,7 +3978,9 @@ var init_memory = __esm({
3503
3978
  init_utils();
3504
3979
  init_claude_client();
3505
3980
  init_solana_client();
3981
+ init_src();
3506
3982
  init_embeddings();
3983
+ init_wiki_packs();
3507
3984
  init_config2();
3508
3985
  init_bm25_search();
3509
3986
  init_openrouter_client();
@@ -3520,6 +3997,16 @@ var init_memory = __esm({
3520
3997
  log10 = createChildLogger("memory");
3521
3998
  DEDUP_TTL_MS = 10 * 60 * 1e3;
3522
3999
  dedupCache = /* @__PURE__ */ new Map();
4000
+ installedPacksCache = /* @__PURE__ */ new Map();
4001
+ INSTALLED_PACKS_TTL_MS = 6e4;
4002
+ topicEmbeddingCache = /* @__PURE__ */ new Map();
4003
+ topicEmbeddingPopulationLock = null;
4004
+ TOKENISATION_SKIP_SOURCES = /* @__PURE__ */ new Set([
4005
+ "demo",
4006
+ "demo-maas",
4007
+ "locomo-benchmark",
4008
+ "longmemeval-benchmark"
4009
+ ]);
3523
4010
  STOPWORDS = /* @__PURE__ */ new Set([
3524
4011
  "the",
3525
4012
  "a",
@@ -3625,6 +4112,7 @@ __export(memory_exports2, {
3625
4112
  getSelfModel: () => getSelfModel,
3626
4113
  hydrateMemories: () => hydrateMemories,
3627
4114
  inferConcepts: () => inferConcepts,
4115
+ invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
3628
4116
  isValidHashId: () => isValidHashId,
3629
4117
  listMemories: () => listMemories,
3630
4118
  moodToValence: () => moodToValence,
@@ -6037,10 +6525,10 @@ var Cortex = class {
6037
6525
  this.requireSelfHosted("verifyOnChain");
6038
6526
  const { hydrateMemories: hydrateMemories2 } = (init_memory2(), __toCommonJS(memory_exports2));
6039
6527
  const { verifyMemoryOnChain: verifyMemoryOnChain2 } = (init_solana_client(), __toCommonJS(solana_client_exports));
6040
- const { createHash: createHash2 } = require("crypto");
6528
+ const { createHash: createHash3 } = require("crypto");
6041
6529
  const memories = await hydrateMemories2([memoryId]);
6042
6530
  if (memories.length === 0) return false;
6043
- const contentHash = createHash2("sha256").update(memories[0].content).digest();
6531
+ const contentHash = createHash3("sha256").update(memories[0].content).digest();
6044
6532
  return verifyMemoryOnChain2(contentHash);
6045
6533
  }
6046
6534
  /** Clean up resources and stop schedules. */