@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/cli/index.js CHANGED
@@ -749,9 +749,9 @@ function createPrompt() {
749
749
  });
750
750
  }
751
751
  function ask(rl, question) {
752
- return new Promise((resolve4) => {
752
+ return new Promise((resolve5) => {
753
753
  rl.question(` ${c.white}?${c.reset} ${question}`, (answer) => {
754
- resolve4(answer.trim());
754
+ resolve5(answer.trim());
755
755
  });
756
756
  });
757
757
  }
@@ -759,14 +759,14 @@ function askChoice(rl, question, choices) {
759
759
  for (const [i, ch] of choices.entries()) {
760
760
  console.log(` ${c.cyan}${i + 1}${c.reset}) ${ch.label}`);
761
761
  }
762
- return new Promise((resolve4) => {
762
+ return new Promise((resolve5) => {
763
763
  rl.question(`
764
764
  ${c.white}?${c.reset} ${question} `, (answer) => {
765
765
  const idx = parseInt(answer.trim(), 10) - 1;
766
766
  if (idx >= 0 && idx < choices.length) {
767
- resolve4(choices[idx].key);
767
+ resolve5(choices[idx].key);
768
768
  } else {
769
- resolve4(choices[choices.length - 1].key);
769
+ resolve5(choices[choices.length - 1].key);
770
770
  }
771
771
  });
772
772
  });
@@ -1497,11 +1497,11 @@ async function getEmail(opts = {}) {
1497
1497
  }
1498
1498
  if (opts.skipPrompt) return null;
1499
1499
  const rl = createPrompt();
1500
- return new Promise((resolve4) => {
1500
+ return new Promise((resolve5) => {
1501
1501
  rl.question(" Email: ", (answer) => {
1502
1502
  rl.close();
1503
1503
  const trimmed = answer.trim();
1504
- resolve4(trimmed && emailRegex.test(trimmed) ? trimmed : null);
1504
+ resolve5(trimmed && emailRegex.test(trimmed) ? trimmed : null);
1505
1505
  });
1506
1506
  });
1507
1507
  }
@@ -1622,23 +1622,23 @@ function createPrompt2() {
1622
1622
  });
1623
1623
  }
1624
1624
  function ask2(rl, question) {
1625
- return new Promise((resolve4) => {
1625
+ return new Promise((resolve5) => {
1626
1626
  rl.question(` ${c.white}?${c.reset} ${question}`, (answer) => {
1627
- resolve4(answer.trim());
1627
+ resolve5(answer.trim());
1628
1628
  });
1629
1629
  });
1630
1630
  }
1631
1631
  function askChoice2(rl, question, choices) {
1632
1632
  const choiceStr = choices.map((ch, i) => `${c.cyan}${i + 1}${c.reset}) ${ch}`).join(" ");
1633
- return new Promise((resolve4) => {
1633
+ return new Promise((resolve5) => {
1634
1634
  rl.question(` ${c.white}?${c.reset} ${question} [${choiceStr}]: `, (answer) => {
1635
1635
  const idx = parseInt(answer.trim(), 10) - 1;
1636
1636
  if (idx >= 0 && idx < choices.length) {
1637
- resolve4(choices[idx]);
1637
+ resolve5(choices[idx]);
1638
1638
  } else if (choices.map((c2) => c2.toLowerCase()).includes(answer.trim().toLowerCase())) {
1639
- resolve4(answer.trim().toLowerCase());
1639
+ resolve5(answer.trim().toLowerCase());
1640
1640
  } else {
1641
- resolve4(choices[0]);
1641
+ resolve5(choices[0]);
1642
1642
  }
1643
1643
  });
1644
1644
  });
@@ -2143,7 +2143,7 @@ async function runInit() {
2143
2143
  }
2144
2144
  }
2145
2145
  console.log(`
2146
- ${c.dim}Docs: https://github.com/sebbsssss/cludebot${c.reset}`);
2146
+ ${c.dim}Docs: https://github.com/sebbsssss/clude${c.reset}`);
2147
2147
  console.log(` ${c.dim}Website: https://clude.io${c.reset}`);
2148
2148
  printDivider();
2149
2149
  console.log("");
@@ -2172,9 +2172,9 @@ function createPrompt3() {
2172
2172
  });
2173
2173
  }
2174
2174
  function ask3(rl, question) {
2175
- return new Promise((resolve4) => {
2175
+ return new Promise((resolve5) => {
2176
2176
  rl.question(` ${c.white}?${c.reset} ${question}`, (answer) => {
2177
- resolve4(answer.trim());
2177
+ resolve5(answer.trim());
2178
2178
  });
2179
2179
  });
2180
2180
  }
@@ -2262,6 +2262,249 @@ var init_register = __esm({
2262
2262
  }
2263
2263
  });
2264
2264
 
2265
+ // packages/brain/src/cli/connect.ts
2266
+ var connect_exports = {};
2267
+ __export(connect_exports, {
2268
+ runConnect: () => runConnect
2269
+ });
2270
+ function createPrompt4() {
2271
+ return readline4.createInterface({ input: process.stdin, output: process.stdout });
2272
+ }
2273
+ function ask4(rl, question) {
2274
+ return new Promise((resolve5) => {
2275
+ rl.question(` ${c.white}?${c.reset} ${question}`, (answer) => resolve5(answer.trim()));
2276
+ });
2277
+ }
2278
+ function askYesNo(rl, question, defaultYes = true) {
2279
+ const hint = defaultYes ? "Y/n" : "y/N";
2280
+ return ask4(rl, `${question} [${hint}]: `).then((a) => {
2281
+ if (!a) return defaultYes;
2282
+ return /^y(es)?$/i.test(a);
2283
+ });
2284
+ }
2285
+ function readStoredConfig() {
2286
+ const p = path4.join(os3.homedir(), ".clude", "config.json");
2287
+ if (!fs3.existsSync(p)) return null;
2288
+ try {
2289
+ return JSON.parse(fs3.readFileSync(p, "utf8"));
2290
+ } catch {
2291
+ return null;
2292
+ }
2293
+ }
2294
+ function detectClaudeDesktopConfigPath() {
2295
+ const home = process.env.HOME || process.env.USERPROFILE || os3.homedir();
2296
+ if (process.platform === "darwin") {
2297
+ return path4.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
2298
+ }
2299
+ if (process.platform === "win32") {
2300
+ return path4.join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
2301
+ }
2302
+ return path4.join(home, ".config", "Claude", "claude_desktop_config.json");
2303
+ }
2304
+ async function verifyMcpKey(host, apiKey) {
2305
+ try {
2306
+ const res = await fetch(`${host}${MCP_PATH}`, {
2307
+ method: "POST",
2308
+ headers: {
2309
+ "Content-Type": "application/json",
2310
+ "Accept": "application/json, text/event-stream",
2311
+ "Authorization": `Bearer ${apiKey}`
2312
+ },
2313
+ body: JSON.stringify({
2314
+ jsonrpc: "2.0",
2315
+ id: 1,
2316
+ method: "initialize",
2317
+ params: {
2318
+ protocolVersion: "2025-06-18",
2319
+ capabilities: {},
2320
+ clientInfo: { name: "clude-connect", version: "1.0.0" }
2321
+ }
2322
+ })
2323
+ });
2324
+ if (res.status === 401) return { ok: false, error: "Bearer key rejected (401)" };
2325
+ if (!res.ok) return { ok: false, error: `HTTP ${res.status}` };
2326
+ const body = await res.text();
2327
+ const match = body.match(/data:\s*(\{.*\})/);
2328
+ if (!match) return { ok: false, error: "No SSE data frame in response" };
2329
+ const doc = JSON.parse(match[1]);
2330
+ if (doc.error) return { ok: false, error: doc.error.message };
2331
+ return { ok: true, serverName: doc?.result?.serverInfo?.name };
2332
+ } catch (err) {
2333
+ return { ok: false, error: err.message ?? "fetch failed" };
2334
+ }
2335
+ }
2336
+ async function registerNewKey(host, name, email) {
2337
+ try {
2338
+ const res = await fetch(`${host}/api/cortex/register`, {
2339
+ method: "POST",
2340
+ headers: { "Content-Type": "application/json" },
2341
+ body: JSON.stringify(email ? { name, email } : { name })
2342
+ });
2343
+ const data = await res.json();
2344
+ if (!res.ok || !data.apiKey) return { error: data?.error ?? `HTTP ${res.status}` };
2345
+ return { apiKey: data.apiKey, agentId: data.agentId };
2346
+ } catch (err) {
2347
+ return { error: err.message ?? "register failed" };
2348
+ }
2349
+ }
2350
+ function buildDesktopEntry(host, apiKey) {
2351
+ return {
2352
+ name: "clude",
2353
+ entry: {
2354
+ url: `${host}${MCP_PATH}`,
2355
+ headers: { Authorization: `Bearer ${apiKey}` }
2356
+ }
2357
+ };
2358
+ }
2359
+ function writeDesktopConfig(configPath, host, apiKey) {
2360
+ const dir = path4.dirname(configPath);
2361
+ fs3.mkdirSync(dir, { recursive: true });
2362
+ let existing = {};
2363
+ let merged = false;
2364
+ if (fs3.existsSync(configPath)) {
2365
+ try {
2366
+ existing = JSON.parse(fs3.readFileSync(configPath, "utf8"));
2367
+ merged = true;
2368
+ } catch {
2369
+ existing = {};
2370
+ }
2371
+ }
2372
+ const { name, entry } = buildDesktopEntry(host, apiKey);
2373
+ existing.mcpServers = existing.mcpServers || {};
2374
+ existing.mcpServers[name] = entry;
2375
+ fs3.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
2376
+ return { wrote: true, merged };
2377
+ }
2378
+ async function runConnect() {
2379
+ const args = process.argv.slice(3);
2380
+ const nonInteractive = args.includes("--non-interactive") || args.includes("-y");
2381
+ const hostFlag = args.find((a) => a.startsWith("--host="));
2382
+ const host = hostFlag ? hostFlag.split("=")[1] : DEFAULT_HOST;
2383
+ const keyFlag = args.find((a) => a.startsWith("--key="));
2384
+ printBanner();
2385
+ console.log(` ${c.bold}Connect Clude to Claude Desktop${c.reset}
2386
+ `);
2387
+ console.log(` ${c.gray}Adds Clude as a remote MCP connector. You'll see memory tools${c.reset}`);
2388
+ console.log(` ${c.gray}(recall_memories, store_memory, get_memory_stats) in Claude.${c.reset}
2389
+ `);
2390
+ printStep(1, 3, "Get your API key");
2391
+ let apiKey = keyFlag?.split("=")[1] || process.env.CORTEX_API_KEY || "";
2392
+ let source = apiKey ? keyFlag ? "--key flag" : "CORTEX_API_KEY env var" : "";
2393
+ if (!apiKey) {
2394
+ const stored = readStoredConfig();
2395
+ if (stored?.apiKey) {
2396
+ apiKey = stored.apiKey;
2397
+ source = "~/.clude/config.json";
2398
+ }
2399
+ }
2400
+ if (apiKey) {
2401
+ printSuccess(`Using existing key from ${source}`);
2402
+ printInfo(` ${apiKey.slice(0, 12)}\u2026`);
2403
+ } else if (nonInteractive) {
2404
+ printError("No API key found. Provide one with --key=<clk_\u2026> or run `npx @clude/sdk setup` first.");
2405
+ process.exit(1);
2406
+ } else {
2407
+ const rl = createPrompt4();
2408
+ try {
2409
+ console.log(` ${c.gray}No API key found locally. Choose one:${c.reset}
2410
+ `);
2411
+ console.log(` ${c.cyan}1${c.reset}) Register a new key now (free)`);
2412
+ console.log(` ${c.cyan}2${c.reset}) Paste an existing ${c.gray}clk_\u2026${c.reset} key
2413
+ `);
2414
+ const choice = await ask4(rl, "Choice [1]: ");
2415
+ if (choice === "" || choice === "1") {
2416
+ const name = await ask4(rl, "Display name: ") || "claude-desktop-user";
2417
+ const email = await ask4(rl, "Email (optional, enables dashboard login): ");
2418
+ printInfo(" Registering\u2026");
2419
+ const reg = await registerNewKey(host, name, email || void 0);
2420
+ if ("error" in reg) {
2421
+ printError(`Registration failed: ${reg.error}`);
2422
+ process.exit(1);
2423
+ }
2424
+ apiKey = reg.apiKey;
2425
+ printSuccess(`API key created ${c.green}${apiKey}${c.reset}`);
2426
+ printWarn("Save this key. It will not be shown again.");
2427
+ } else {
2428
+ apiKey = await ask4(rl, "Paste your clk_\u2026 key: ");
2429
+ if (!apiKey.startsWith("clk_")) {
2430
+ printError("Key should start with clk_. Aborting.");
2431
+ process.exit(1);
2432
+ }
2433
+ }
2434
+ } finally {
2435
+ rl.close();
2436
+ }
2437
+ }
2438
+ printStep(2, 3, "Verify connector");
2439
+ printInfo(` POST ${host}${MCP_PATH} \u2192 initialize`);
2440
+ const verify = await verifyMcpKey(host, apiKey);
2441
+ if (!verify.ok) {
2442
+ printError(`Connector check failed: ${verify.error}`);
2443
+ printInfo(" If the host is right, double-check the bearer key at https://clude.io/dashboard");
2444
+ process.exit(1);
2445
+ }
2446
+ printSuccess(`Connector responded \u2014 server: ${c.green}${verify.serverName ?? "clude"}${c.reset}`);
2447
+ printStep(3, 3, "Install in Claude Desktop");
2448
+ const configPath = detectClaudeDesktopConfigPath();
2449
+ let shouldWrite = nonInteractive;
2450
+ if (!nonInteractive) {
2451
+ const rl = createPrompt4();
2452
+ try {
2453
+ console.log(` ${c.gray}Config path: ${configPath}${c.reset}`);
2454
+ shouldWrite = await askYesNo(rl, "Write this connector to your Claude Desktop config?", true);
2455
+ } finally {
2456
+ rl.close();
2457
+ }
2458
+ }
2459
+ if (shouldWrite) {
2460
+ try {
2461
+ const { merged } = writeDesktopConfig(configPath, host, apiKey);
2462
+ printSuccess(`${merged ? "Merged into" : "Created"} ${configPath}`);
2463
+ printInfo(" Restart Claude Desktop to pick up the new connector.");
2464
+ } catch (err) {
2465
+ printError(`Could not write config: ${err.message}`);
2466
+ printSnippet(host, apiKey);
2467
+ process.exit(1);
2468
+ }
2469
+ } else {
2470
+ printSnippet(host, apiKey);
2471
+ }
2472
+ printDivider();
2473
+ console.log(`
2474
+ ${c.bold}Claude Code users:${c.reset} add this to your project's ${c.cyan}.mcp.json${c.reset}
2475
+ `);
2476
+ printCodeBlock(JSON.stringify({
2477
+ mcpServers: { clude: buildDesktopEntry(host, apiKey).entry }
2478
+ }, null, 2));
2479
+ console.log(` ${c.bold}claude.ai web users:${c.reset}`);
2480
+ console.log(` 1. Open Settings \u2192 Connectors \u2192 Add custom connector`);
2481
+ console.log(` 2. URL: ${c.cyan}${host}${MCP_PATH}${c.reset}`);
2482
+ console.log(` 3. Header: ${c.cyan}Authorization: Bearer ${apiKey.slice(0, 12)}\u2026${c.reset}
2483
+ `);
2484
+ console.log(` ${c.gray}Docs: https://clude.io/docs/connector${c.reset}
2485
+ `);
2486
+ }
2487
+ function printSnippet(host, apiKey) {
2488
+ const configPath = detectClaudeDesktopConfigPath();
2489
+ printWarn(`Skipped writing config. Add manually to ${configPath}:`);
2490
+ printCodeBlock(JSON.stringify({
2491
+ mcpServers: { clude: buildDesktopEntry(host, apiKey).entry }
2492
+ }, null, 2));
2493
+ }
2494
+ var readline4, fs3, path4, os3, DEFAULT_HOST, MCP_PATH;
2495
+ var init_connect = __esm({
2496
+ "packages/brain/src/cli/connect.ts"() {
2497
+ "use strict";
2498
+ readline4 = __toESM(require("readline"));
2499
+ fs3 = __toESM(require("fs"));
2500
+ path4 = __toESM(require("path"));
2501
+ os3 = __toESM(require("os"));
2502
+ init_banner();
2503
+ DEFAULT_HOST = process.env.CORTEX_HOST_URL || "https://clude.io";
2504
+ MCP_PATH = "/api/mcp";
2505
+ }
2506
+ });
2507
+
2265
2508
  // packages/shared/src/config.ts
2266
2509
  var config_exports = {};
2267
2510
  __export(config_exports, {
@@ -2797,6 +3040,819 @@ var init_status = __esm({
2797
3040
  }
2798
3041
  });
2799
3042
 
3043
+ // packages/memorypack/src/types.ts
3044
+ var MEMORYPACK_VERSION;
3045
+ var init_types = __esm({
3046
+ "packages/memorypack/src/types.ts"() {
3047
+ "use strict";
3048
+ MEMORYPACK_VERSION = "0.2";
3049
+ }
3050
+ });
3051
+
3052
+ // packages/memorypack/src/sign.ts
3053
+ function hashRecordLine(line) {
3054
+ const clean = line.replace(/\n$/, "");
3055
+ const hex = (0, import_crypto.createHash)("sha256").update(clean, "utf-8").digest("hex");
3056
+ return `sha256:${hex}`;
3057
+ }
3058
+ function hashBuffer(buf) {
3059
+ const hex = (0, import_crypto.createHash)("sha256").update(buf).digest("hex");
3060
+ return `sha256:${hex}`;
3061
+ }
3062
+ function signHash(hash, secretKey) {
3063
+ const messageBytes = Buffer.from(hash, "utf-8");
3064
+ const sig = import_tweetnacl.default.sign.detached(messageBytes, secretKey);
3065
+ return `base58:${bs58.encode(sig)}`;
3066
+ }
3067
+ function verifyHash(hash, signature, publicKey) {
3068
+ try {
3069
+ const messageBytes = Buffer.from(hash, "utf-8");
3070
+ const sigRaw = signature.startsWith("base58:") ? signature.slice("base58:".length) : signature;
3071
+ const sigBytes = bs58.decode(sigRaw);
3072
+ const pkBytes = bs58.decode(publicKey);
3073
+ return import_tweetnacl.default.sign.detached.verify(messageBytes, sigBytes, pkBytes);
3074
+ } catch {
3075
+ return false;
3076
+ }
3077
+ }
3078
+ function revocationPayload(record_hash, revoked_at) {
3079
+ return `revoke:v1:${record_hash}:${revoked_at}`;
3080
+ }
3081
+ function verifyRevocation(record_hash, revoked_at, signature, publicKey) {
3082
+ try {
3083
+ const messageBytes = Buffer.from(revocationPayload(record_hash, revoked_at), "utf-8");
3084
+ const sigRaw = signature.startsWith("base58:") ? signature.slice("base58:".length) : signature;
3085
+ const sigBytes = bs58.decode(sigRaw);
3086
+ const pkBytes = bs58.decode(publicKey);
3087
+ return import_tweetnacl.default.sign.detached.verify(messageBytes, sigBytes, pkBytes);
3088
+ } catch {
3089
+ return false;
3090
+ }
3091
+ }
3092
+ function randomNonce() {
3093
+ return new Uint8Array((0, import_crypto.randomBytes)(ENCRYPTION_NONCE_BYTES));
3094
+ }
3095
+ function encryptString(plaintext, key) {
3096
+ if (key.length !== ENCRYPTION_KEY_BYTES) {
3097
+ throw new Error(`encryption key must be ${ENCRYPTION_KEY_BYTES} bytes`);
3098
+ }
3099
+ const nonce = randomNonce();
3100
+ const messageBytes = Buffer.from(plaintext, "utf-8");
3101
+ const cipherBytes = import_tweetnacl.default.secretbox(messageBytes, nonce, key);
3102
+ return {
3103
+ ciphertext: Buffer.from(cipherBytes).toString("base64"),
3104
+ nonce: Buffer.from(nonce).toString("base64")
3105
+ };
3106
+ }
3107
+ function decryptString(ciphertext, nonce, key) {
3108
+ if (key.length !== ENCRYPTION_KEY_BYTES) {
3109
+ throw new Error(`encryption key must be ${ENCRYPTION_KEY_BYTES} bytes`);
3110
+ }
3111
+ const cipherBytes = Buffer.from(ciphertext, "base64");
3112
+ const nonceBytes = Buffer.from(nonce, "base64");
3113
+ if (nonceBytes.length !== ENCRYPTION_NONCE_BYTES) {
3114
+ throw new Error(`nonce must be ${ENCRYPTION_NONCE_BYTES} bytes (decoded got ${nonceBytes.length})`);
3115
+ }
3116
+ const plain = import_tweetnacl.default.secretbox.open(cipherBytes, nonceBytes, key);
3117
+ if (!plain) {
3118
+ throw new Error("decryption failed (MAC mismatch \u2014 wrong key or tampered ciphertext)");
3119
+ }
3120
+ return Buffer.from(plain).toString("utf-8");
3121
+ }
3122
+ function encryptBuffer(plaintext, key) {
3123
+ if (key.length !== ENCRYPTION_KEY_BYTES) {
3124
+ throw new Error(`encryption key must be ${ENCRYPTION_KEY_BYTES} bytes`);
3125
+ }
3126
+ const nonce = randomNonce();
3127
+ const cipherBytes = import_tweetnacl.default.secretbox(plaintext, nonce, key);
3128
+ return {
3129
+ ciphertext: Buffer.from(cipherBytes),
3130
+ nonce: Buffer.from(nonce).toString("base64")
3131
+ };
3132
+ }
3133
+ var import_crypto, import_tweetnacl, bs58Module, bs58, ENCRYPTION_KEY_BYTES, ENCRYPTION_NONCE_BYTES;
3134
+ var init_sign = __esm({
3135
+ "packages/memorypack/src/sign.ts"() {
3136
+ "use strict";
3137
+ import_crypto = require("crypto");
3138
+ import_tweetnacl = __toESM(require("tweetnacl"));
3139
+ bs58Module = __toESM(require("bs58"));
3140
+ bs58 = bs58Module.default || bs58Module;
3141
+ ENCRYPTION_KEY_BYTES = import_tweetnacl.default.secretbox.keyLength;
3142
+ ENCRYPTION_NONCE_BYTES = import_tweetnacl.default.secretbox.nonceLength;
3143
+ }
3144
+ });
3145
+
3146
+ // packages/memorypack/src/writer.ts
3147
+ function serializeRecord(record) {
3148
+ const KNOWN_ORDER = [
3149
+ "id",
3150
+ "created_at",
3151
+ "kind",
3152
+ "content",
3153
+ "tags",
3154
+ "importance",
3155
+ "source",
3156
+ "summary",
3157
+ "embedding",
3158
+ "embedding_model",
3159
+ "metadata",
3160
+ "access_count",
3161
+ "last_accessed_at",
3162
+ "parent_ids",
3163
+ "compacted_from",
3164
+ "blob_ref",
3165
+ "encrypted",
3166
+ "nonce"
3167
+ ];
3168
+ const out = {};
3169
+ for (const k of KNOWN_ORDER) {
3170
+ if (record[k] !== void 0) out[k] = record[k];
3171
+ }
3172
+ const recAsUnknown = record;
3173
+ const unknownKeys = Object.keys(recAsUnknown).filter(
3174
+ (k) => !KNOWN_ORDER.includes(k)
3175
+ );
3176
+ for (const k of unknownKeys.sort()) {
3177
+ out[k] = recAsUnknown[k];
3178
+ }
3179
+ return JSON.stringify(out);
3180
+ }
3181
+ function writeMemoryPack(targetPath, records, opts) {
3182
+ const format = opts.format ?? "directory";
3183
+ if (format === "tarball") {
3184
+ writeTarball(targetPath, records, opts);
3185
+ return;
3186
+ }
3187
+ writeDirectory(targetPath, records, opts);
3188
+ }
3189
+ function writeDirectory(dir, records, opts) {
3190
+ if ((0, import_fs2.existsSync)(dir)) {
3191
+ for (const f of [
3192
+ "manifest.json",
3193
+ "records.jsonl",
3194
+ "signatures.jsonl",
3195
+ "anchors.jsonl",
3196
+ "revocations.jsonl",
3197
+ "revocation_anchors.jsonl"
3198
+ ]) {
3199
+ const p = (0, import_path3.join)(dir, f);
3200
+ if ((0, import_fs2.existsSync)(p)) (0, import_fs2.rmSync)(p, { force: true });
3201
+ }
3202
+ const blobsDir = (0, import_path3.join)(dir, "blobs");
3203
+ if ((0, import_fs2.existsSync)(blobsDir)) (0, import_fs2.rmSync)(blobsDir, { recursive: true, force: true });
3204
+ }
3205
+ (0, import_fs2.mkdirSync)(dir, { recursive: true });
3206
+ const clock = opts.clock ?? (() => (/* @__PURE__ */ new Date()).toISOString());
3207
+ const encryption = opts.encryption ? {
3208
+ ...opts.encryption,
3209
+ scope: opts.encryption.scope ?? (opts.blobs && opts.blobs.size > 0 ? "records+blobs" : "records")
3210
+ } : void 0;
3211
+ if (encryption && encryption.key.length !== ENCRYPTION_KEY_BYTES) {
3212
+ throw new Error(`encryption.key must be ${ENCRYPTION_KEY_BYTES} bytes`);
3213
+ }
3214
+ const encryptBlobs = encryption?.scope === "records+blobs";
3215
+ const recordsToWrite = records.map((r) => {
3216
+ if (!encryption) return r;
3217
+ const { ciphertext, nonce } = encryptString(r.content, encryption.key);
3218
+ return { ...r, content: ciphertext, encrypted: true, nonce };
3219
+ });
3220
+ const manifest = {
3221
+ memorypack_version: MEMORYPACK_VERSION,
3222
+ producer: opts.producer,
3223
+ created_at: clock(),
3224
+ record_count: recordsToWrite.length,
3225
+ record_schema: opts.record_schema,
3226
+ signature_algorithm: opts.secretKey ? "ed25519" : void 0,
3227
+ anchor_chain: opts.anchor_chain,
3228
+ pack_format: "directory",
3229
+ blobs_count: opts.blobs && opts.blobs.size > 0 ? opts.blobs.size : void 0,
3230
+ encryption: encryption ? {
3231
+ algorithm: "xsalsa20-poly1305",
3232
+ nonce_strategy: "per-record-random",
3233
+ key_derivation: "none",
3234
+ scope: encryption.scope
3235
+ } : void 0
3236
+ };
3237
+ (0, import_fs2.writeFileSync)((0, import_path3.join)(dir, "manifest.json"), JSON.stringify(manifest, null, 2));
3238
+ const recordLines = [];
3239
+ const sigs = [];
3240
+ for (const record of recordsToWrite) {
3241
+ const line = serializeRecord(record);
3242
+ recordLines.push(line);
3243
+ if (opts.secretKey && opts.producer.public_key) {
3244
+ const hash = hashRecordLine(line);
3245
+ const signature = signHash(hash, opts.secretKey);
3246
+ sigs.push({
3247
+ record_hash: hash,
3248
+ signature,
3249
+ algorithm: "ed25519",
3250
+ public_key: opts.producer.public_key
3251
+ });
3252
+ }
3253
+ }
3254
+ (0, import_fs2.writeFileSync)((0, import_path3.join)(dir, "records.jsonl"), recordLines.join("\n") + "\n");
3255
+ if (sigs.length > 0) {
3256
+ (0, import_fs2.writeFileSync)(
3257
+ (0, import_path3.join)(dir, "signatures.jsonl"),
3258
+ sigs.map((s) => JSON.stringify(s)).join("\n") + "\n"
3259
+ );
3260
+ }
3261
+ if (opts.anchors && opts.anchors.length > 0) {
3262
+ (0, import_fs2.writeFileSync)(
3263
+ (0, import_path3.join)(dir, "anchors.jsonl"),
3264
+ opts.anchors.map((a) => JSON.stringify(a)).join("\n") + "\n"
3265
+ );
3266
+ }
3267
+ if (opts.blobs && opts.blobs.size > 0) {
3268
+ const blobsDir = (0, import_path3.join)(dir, "blobs", "sha256");
3269
+ (0, import_fs2.mkdirSync)(blobsDir, { recursive: true });
3270
+ const indexEntries = [];
3271
+ for (const [declaredHash, blob] of opts.blobs.entries()) {
3272
+ const dataBuf = Buffer.from(blob.data);
3273
+ const computedPlainHash = hashBuffer(dataBuf);
3274
+ if (computedPlainHash !== declaredHash) {
3275
+ throw new Error(
3276
+ `MemoryPack: blob key '${declaredHash}' does not match sha256 of data (computed ${computedPlainHash})`
3277
+ );
3278
+ }
3279
+ let storedBytes;
3280
+ let entry;
3281
+ if (encryptBlobs && encryption) {
3282
+ const { ciphertext, nonce } = encryptBuffer(dataBuf, encryption.key);
3283
+ storedBytes = ciphertext;
3284
+ const storedHash = hashBuffer(ciphertext);
3285
+ entry = {
3286
+ hash: storedHash,
3287
+ byte_size: ciphertext.length,
3288
+ encrypted: true,
3289
+ nonce
3290
+ // filename + content_type intentionally omitted under records+blobs
3291
+ // encryption to avoid leaking attachment metadata in cleartext.
3292
+ };
3293
+ } else {
3294
+ storedBytes = dataBuf;
3295
+ entry = {
3296
+ hash: declaredHash,
3297
+ byte_size: dataBuf.length,
3298
+ content_type: blob.content_type,
3299
+ filename: blob.filename
3300
+ };
3301
+ }
3302
+ const hex = entry.hash.replace(/^sha256:/, "");
3303
+ (0, import_fs2.writeFileSync)((0, import_path3.join)(blobsDir, hex), storedBytes);
3304
+ indexEntries.push(entry);
3305
+ }
3306
+ (0, import_fs2.writeFileSync)(
3307
+ (0, import_path3.join)(dir, "blobs", "index.jsonl"),
3308
+ indexEntries.map((e) => JSON.stringify(e)).join("\n") + "\n"
3309
+ );
3310
+ }
3311
+ }
3312
+ function writeTarball(targetPath, records, opts) {
3313
+ const targetAbs = (0, import_path3.resolve)(targetPath);
3314
+ const parent = (0, import_path3.dirname)(targetAbs);
3315
+ const innerName = (0, import_path3.basename)(targetAbs).replace(/\.tar\.zst$/i, "") || "memorypack";
3316
+ const tmpRoot = `${targetAbs}.tmp-${process.pid}-${Date.now()}`;
3317
+ const stagingDir = (0, import_path3.join)(tmpRoot, innerName);
3318
+ (0, import_fs2.mkdirSync)(stagingDir, { recursive: true });
3319
+ try {
3320
+ writeDirectory(stagingDir, records, opts);
3321
+ const manifestPath = (0, import_path3.join)(stagingDir, "manifest.json");
3322
+ const manifest = JSON.parse(
3323
+ require("fs").readFileSync(manifestPath, "utf-8")
3324
+ );
3325
+ manifest.pack_format = "tarball";
3326
+ (0, import_fs2.writeFileSync)(manifestPath, JSON.stringify(manifest, null, 2));
3327
+ const res = (0, import_child_process2.spawnSync)(
3328
+ "tar",
3329
+ ["--zstd", "-cf", targetAbs, "-C", tmpRoot, innerName],
3330
+ { stdio: ["ignore", "ignore", "pipe"] }
3331
+ );
3332
+ if (res.error) {
3333
+ throw new Error(
3334
+ `MemoryPack: failed to invoke 'tar' (${res.error.message}). Install a recent tar with zstd support.`
3335
+ );
3336
+ }
3337
+ if (res.status !== 0) {
3338
+ const stderr = res.stderr ? res.stderr.toString() : "";
3339
+ throw new Error(
3340
+ `MemoryPack: tar --zstd failed (exit ${res.status}): ${stderr.trim() || "no stderr"}`
3341
+ );
3342
+ }
3343
+ if (!(0, import_fs2.existsSync)(targetAbs) || (0, import_fs2.statSync)(targetAbs).size === 0) {
3344
+ throw new Error("MemoryPack: tar produced an empty archive");
3345
+ }
3346
+ } finally {
3347
+ (0, import_fs2.rmSync)(tmpRoot, { recursive: true, force: true });
3348
+ }
3349
+ }
3350
+ var import_fs2, import_child_process2, import_path3;
3351
+ var init_writer = __esm({
3352
+ "packages/memorypack/src/writer.ts"() {
3353
+ "use strict";
3354
+ import_fs2 = require("fs");
3355
+ import_child_process2 = require("child_process");
3356
+ import_path3 = require("path");
3357
+ init_types();
3358
+ init_sign();
3359
+ }
3360
+ });
3361
+
3362
+ // packages/memorypack/src/reader.ts
3363
+ function readMemoryPack(path9, opts = {}) {
3364
+ const isTarball = /\.tar\.zst$/i.test(path9) || (0, import_fs3.existsSync)(path9) && (0, import_fs3.statSync)(path9).isFile();
3365
+ if (!isTarball) {
3366
+ return readDirectory(path9, opts);
3367
+ }
3368
+ let extractRoot = null;
3369
+ try {
3370
+ extractRoot = extractTarball(path9);
3371
+ return readDirectory(extractRoot, opts);
3372
+ } finally {
3373
+ if (extractRoot) {
3374
+ (0, import_fs3.rmSync)(extractRoot, { recursive: true, force: true });
3375
+ }
3376
+ }
3377
+ }
3378
+ function readDirectory(dir, opts) {
3379
+ const warnings = [];
3380
+ const manifestPath = (0, import_path4.join)(dir, "manifest.json");
3381
+ if (!(0, import_fs3.existsSync)(manifestPath)) {
3382
+ throw new Error(`MemoryPack: manifest.json not found in ${dir}`);
3383
+ }
3384
+ const manifest = JSON.parse((0, import_fs3.readFileSync)(manifestPath, "utf-8"));
3385
+ if (!manifest.memorypack_version) {
3386
+ throw new Error("MemoryPack: manifest missing memorypack_version");
3387
+ }
3388
+ if (!manifest.memorypack_version.startsWith("0.")) {
3389
+ throw new Error(
3390
+ `MemoryPack: unsupported memorypack_version ${manifest.memorypack_version} (reader supports 0.x)`
3391
+ );
3392
+ }
3393
+ const recordsPath = (0, import_path4.join)(dir, "records.jsonl");
3394
+ if (!(0, import_fs3.existsSync)(recordsPath)) {
3395
+ throw new Error(`MemoryPack: records.jsonl not found in ${dir}`);
3396
+ }
3397
+ const recordLines = (0, import_fs3.readFileSync)(recordsPath, "utf-8").split("\n").filter((l) => l.length > 0);
3398
+ const records = [];
3399
+ const lineByHash = /* @__PURE__ */ new Map();
3400
+ for (const line of recordLines) {
3401
+ const rec = JSON.parse(line);
3402
+ records.push(rec);
3403
+ lineByHash.set(hashRecordLine(line), line);
3404
+ }
3405
+ if (records.length !== manifest.record_count) {
3406
+ warnings.push(
3407
+ `record_count mismatch: manifest ${manifest.record_count} vs jsonl ${records.length}`
3408
+ );
3409
+ }
3410
+ const verifiedRecords = /* @__PURE__ */ new Set();
3411
+ const sigsPath = (0, import_path4.join)(dir, "signatures.jsonl");
3412
+ const signaturesPresent = (0, import_fs3.existsSync)(sigsPath);
3413
+ if (signaturesPresent) {
3414
+ const sigLines = (0, import_fs3.readFileSync)(sigsPath, "utf-8").split("\n").filter((l) => l.length > 0);
3415
+ const pubkey = opts.publicKey ?? manifest.producer.public_key;
3416
+ if (!pubkey) {
3417
+ throw new Error("MemoryPack: signatures.jsonl present but no public_key in manifest");
3418
+ }
3419
+ const validSigHashes = /* @__PURE__ */ new Set();
3420
+ for (const line of sigLines) {
3421
+ const sig = JSON.parse(line);
3422
+ const ok = verifyHash(sig.record_hash, sig.signature, pubkey);
3423
+ if (!ok) {
3424
+ throw new Error(`MemoryPack: signature verification failed for ${sig.record_hash}`);
3425
+ }
3426
+ validSigHashes.add(sig.record_hash);
3427
+ }
3428
+ for (const recordHash of lineByHash.keys()) {
3429
+ if (!validSigHashes.has(recordHash)) {
3430
+ throw new Error(
3431
+ `MemoryPack: signature verification failed \u2014 record ${recordHash} has no matching signature`
3432
+ );
3433
+ }
3434
+ verifiedRecords.add(recordHash);
3435
+ }
3436
+ }
3437
+ const unsignedRecords = /* @__PURE__ */ new Set();
3438
+ if (!signaturesPresent) {
3439
+ for (const hash of lineByHash.keys()) unsignedRecords.add(hash);
3440
+ }
3441
+ if (opts.strictSignatures && unsignedRecords.size > 0) {
3442
+ throw new Error(
3443
+ `MemoryPack: strictSignatures=true but ${unsignedRecords.size} records are unsigned`
3444
+ );
3445
+ }
3446
+ const anchors = [];
3447
+ const anchorsPath = (0, import_path4.join)(dir, "anchors.jsonl");
3448
+ if ((0, import_fs3.existsSync)(anchorsPath)) {
3449
+ const anchorLines = (0, import_fs3.readFileSync)(anchorsPath, "utf-8").split("\n").filter((l) => l.length > 0);
3450
+ for (const line of anchorLines) {
3451
+ anchors.push(JSON.parse(line));
3452
+ }
3453
+ }
3454
+ const revocations = [];
3455
+ const revokedRecordHashes = /* @__PURE__ */ new Set();
3456
+ const revocationsPath = (0, import_path4.join)(dir, "revocations.jsonl");
3457
+ if ((0, import_fs3.existsSync)(revocationsPath)) {
3458
+ const expectedSigner = opts.publicKey ?? manifest.producer.public_key;
3459
+ const revLines = (0, import_fs3.readFileSync)(revocationsPath, "utf-8").split("\n").filter((l) => l.length > 0);
3460
+ for (const line of revLines) {
3461
+ let entry;
3462
+ try {
3463
+ entry = JSON.parse(line);
3464
+ } catch {
3465
+ warnings.push(`revocations.jsonl: skipping malformed line`);
3466
+ continue;
3467
+ }
3468
+ if (expectedSigner && entry.public_key !== expectedSigner) {
3469
+ warnings.push(
3470
+ `revocation for ${entry.record_hash} signed by unexpected key ${entry.public_key} \u2014 rejected`
3471
+ );
3472
+ continue;
3473
+ }
3474
+ const ok = verifyRevocation(
3475
+ entry.record_hash,
3476
+ entry.revoked_at,
3477
+ entry.signature,
3478
+ entry.public_key
3479
+ );
3480
+ if (!ok) {
3481
+ warnings.push(
3482
+ `revocation for ${entry.record_hash} signature failed verification \u2014 rejected`
3483
+ );
3484
+ continue;
3485
+ }
3486
+ revocations.push(entry);
3487
+ revokedRecordHashes.add(entry.record_hash);
3488
+ }
3489
+ }
3490
+ const revocationAnchors = [];
3491
+ const revAnchorsPath = (0, import_path4.join)(dir, "revocation_anchors.jsonl");
3492
+ if ((0, import_fs3.existsSync)(revAnchorsPath)) {
3493
+ const validPairs = new Set(
3494
+ revocations.map((r) => `${r.record_hash}@${r.revoked_at}`)
3495
+ );
3496
+ const anchorLines = (0, import_fs3.readFileSync)(revAnchorsPath, "utf-8").split("\n").filter((l) => l.length > 0);
3497
+ for (const line of anchorLines) {
3498
+ let entry;
3499
+ try {
3500
+ entry = JSON.parse(line);
3501
+ } catch {
3502
+ warnings.push("revocation_anchors.jsonl: skipping malformed line");
3503
+ continue;
3504
+ }
3505
+ if (entry.anchor_format !== "memo-revoke-v1") {
3506
+ warnings.push(
3507
+ `revocation anchor ${entry.tx} has unsupported format '${entry.anchor_format}' \u2014 skipped`
3508
+ );
3509
+ continue;
3510
+ }
3511
+ const pair = `${entry.record_hash}@${entry.revoked_at}`;
3512
+ if (!validPairs.has(pair)) {
3513
+ warnings.push(
3514
+ `revocation anchor ${entry.tx} pair (${entry.record_hash}, ${entry.revoked_at}) does not match any verified revocation \u2014 skipped`
3515
+ );
3516
+ continue;
3517
+ }
3518
+ revocationAnchors.push(entry);
3519
+ }
3520
+ }
3521
+ const verifiedBlobs = /* @__PURE__ */ new Set();
3522
+ const blobsDir = (0, import_path4.join)(dir, "blobs", "sha256");
3523
+ const blobIndexPath = (0, import_path4.join)(dir, "blobs", "index.jsonl");
3524
+ const blobIndexEntries = /* @__PURE__ */ new Map();
3525
+ if ((0, import_fs3.existsSync)(blobIndexPath)) {
3526
+ const indexLines = (0, import_fs3.readFileSync)(blobIndexPath, "utf-8").split("\n").filter((l) => l.length > 0);
3527
+ for (const line of indexLines) {
3528
+ const entry = JSON.parse(line);
3529
+ blobIndexEntries.set(entry.hash, entry);
3530
+ const hex = entry.hash.replace(/^sha256:/, "");
3531
+ const blobPath = (0, import_path4.join)(blobsDir, hex);
3532
+ if (!(0, import_fs3.existsSync)(blobPath)) {
3533
+ warnings.push(`declared blob ${entry.hash} missing from blobs/`);
3534
+ continue;
3535
+ }
3536
+ const data = (0, import_fs3.readFileSync)(blobPath);
3537
+ const computed = hashBuffer(data);
3538
+ if (computed !== entry.hash) {
3539
+ throw new Error(
3540
+ `MemoryPack: blob hash mismatch \u2014 declared ${entry.hash}, computed ${computed}`
3541
+ );
3542
+ }
3543
+ const actualSize = (0, import_fs3.statSync)(blobPath).size;
3544
+ if (entry.byte_size !== actualSize) {
3545
+ warnings.push(
3546
+ `blob ${entry.hash} byte_size mismatch: declared ${entry.byte_size}, actual ${actualSize}`
3547
+ );
3548
+ }
3549
+ verifiedBlobs.add(entry.hash);
3550
+ }
3551
+ }
3552
+ for (const r of records) {
3553
+ if (r.blob_ref && !verifiedBlobs.has(r.blob_ref)) {
3554
+ const reason = blobIndexEntries.size === 0 ? "no blobs/index.jsonl in pack" : "not present or not verified";
3555
+ warnings.push(
3556
+ `record ${r.id} references blob ${r.blob_ref} (${reason})`
3557
+ );
3558
+ }
3559
+ }
3560
+ if (manifest.encryption) {
3561
+ if (!opts.decryptionKey) {
3562
+ warnings.push(
3563
+ `manifest declares encryption (${manifest.encryption.algorithm}, scope=${manifest.encryption.scope}) but no decryptionKey supplied \u2014 content remains ciphertext`
3564
+ );
3565
+ } else if (opts.decryptionKey.length !== ENCRYPTION_KEY_BYTES) {
3566
+ throw new Error(
3567
+ `MemoryPack: decryptionKey must be ${ENCRYPTION_KEY_BYTES} bytes (got ${opts.decryptionKey.length})`
3568
+ );
3569
+ } else {
3570
+ for (const r of records) {
3571
+ if (!r.encrypted) continue;
3572
+ if (!r.nonce) {
3573
+ warnings.push(`record ${r.id} marked encrypted but has no nonce \u2014 leaving ciphertext`);
3574
+ continue;
3575
+ }
3576
+ try {
3577
+ r.content = decryptString(r.content, r.nonce, opts.decryptionKey);
3578
+ r.encrypted = false;
3579
+ delete r.nonce;
3580
+ } catch (e) {
3581
+ warnings.push(
3582
+ `record ${r.id} decryption failed: ${e.message}`
3583
+ );
3584
+ }
3585
+ }
3586
+ }
3587
+ }
3588
+ const minimalRecords = records.filter((r) => !r.encrypted).map((r) => ({
3589
+ id: r.id,
3590
+ created_at: r.created_at,
3591
+ kind: r.kind,
3592
+ content: r.content,
3593
+ tags: r.tags,
3594
+ importance: r.importance,
3595
+ source: r.source,
3596
+ encrypted: r.encrypted
3597
+ }));
3598
+ return {
3599
+ manifest,
3600
+ records,
3601
+ verifiedRecords,
3602
+ unsignedRecords,
3603
+ anchors,
3604
+ verifiedBlobs,
3605
+ verifiedAnchors: /* @__PURE__ */ new Set(),
3606
+ revocations,
3607
+ revokedRecordHashes,
3608
+ revocationAnchors,
3609
+ verifiedRevocationAnchors: /* @__PURE__ */ new Set(),
3610
+ minimalRecords,
3611
+ warnings
3612
+ };
3613
+ }
3614
+ function extractTarball(path9) {
3615
+ if (!(0, import_fs3.existsSync)(path9)) {
3616
+ throw new Error(`MemoryPack: tarball not found: ${path9}`);
3617
+ }
3618
+ const list = (0, import_child_process3.spawnSync)("tar", ["--zstd", "-tvf", path9], {
3619
+ stdio: ["ignore", "pipe", "pipe"]
3620
+ });
3621
+ if (list.error) {
3622
+ throw new Error(
3623
+ `MemoryPack: failed to invoke 'tar' (${list.error.message}). Install a recent tar with zstd support.`
3624
+ );
3625
+ }
3626
+ if (list.status !== 0) {
3627
+ const stderr = list.stderr ? list.stderr.toString() : "";
3628
+ throw new Error(
3629
+ `MemoryPack: tar --zstd -tvf failed (exit ${list.status}): ${stderr.trim() || "no stderr"}`
3630
+ );
3631
+ }
3632
+ const lines = list.stdout.toString().split("\n").filter((l) => l.length > 0);
3633
+ for (const line of lines) {
3634
+ const typeChar = line.charAt(0);
3635
+ if (typeChar === "l" || typeChar === "h") {
3636
+ throw new Error(
3637
+ `MemoryPack: tarball contains symlink/hardlink \u2014 refusing to extract (${line.slice(0, 80)})`
3638
+ );
3639
+ }
3640
+ const name = line.split(/\s+/).pop() ?? "";
3641
+ if (!name) continue;
3642
+ if (name.startsWith("/")) {
3643
+ throw new Error(`MemoryPack: tarball contains absolute path \u2014 refusing (${name})`);
3644
+ }
3645
+ if (name.split("/").some((seg) => seg === "..")) {
3646
+ throw new Error(`MemoryPack: tarball contains '..' segment \u2014 refusing (${name})`);
3647
+ }
3648
+ if (!SAFE_MEMBER_RE.test(name.replace(/\/$/, ""))) {
3649
+ throw new Error(`MemoryPack: tarball member has unsafe characters \u2014 refusing (${name})`);
3650
+ }
3651
+ }
3652
+ const tmp = (0, import_fs3.mkdtempSync)((0, import_path4.join)((0, import_os.tmpdir)(), "mp-extract-"));
3653
+ const extract = (0, import_child_process3.spawnSync)(
3654
+ "tar",
3655
+ [
3656
+ "--zstd",
3657
+ "--no-same-owner",
3658
+ "--no-same-permissions",
3659
+ "-xf",
3660
+ path9,
3661
+ "-C",
3662
+ tmp
3663
+ ],
3664
+ { stdio: ["ignore", "ignore", "pipe"] }
3665
+ );
3666
+ if (extract.status !== 0) {
3667
+ (0, import_fs3.rmSync)(tmp, { recursive: true, force: true });
3668
+ const stderr = extract.stderr ? extract.stderr.toString() : "";
3669
+ throw new Error(
3670
+ `MemoryPack: tar --zstd -xf failed (exit ${extract.status}): ${stderr.trim() || "no stderr"}`
3671
+ );
3672
+ }
3673
+ if ((0, import_fs3.existsSync)((0, import_path4.join)(tmp, "manifest.json"))) {
3674
+ return tmp;
3675
+ }
3676
+ const fs7 = require("fs");
3677
+ const entries = fs7.readdirSync(tmp);
3678
+ if (entries.length === 1) {
3679
+ const inner = (0, import_path4.join)(tmp, entries[0]);
3680
+ if ((0, import_fs3.statSync)(inner).isDirectory() && (0, import_fs3.existsSync)((0, import_path4.join)(inner, "manifest.json"))) {
3681
+ return inner;
3682
+ }
3683
+ }
3684
+ (0, import_fs3.rmSync)(tmp, { recursive: true, force: true });
3685
+ throw new Error(
3686
+ `MemoryPack: tarball must contain manifest.json at the root or inside a single wrapping directory`
3687
+ );
3688
+ }
3689
+ var import_fs3, import_child_process3, import_os, import_path4, SAFE_MEMBER_RE;
3690
+ var init_reader = __esm({
3691
+ "packages/memorypack/src/reader.ts"() {
3692
+ "use strict";
3693
+ import_fs3 = require("fs");
3694
+ import_child_process3 = require("child_process");
3695
+ import_os = require("os");
3696
+ import_path4 = require("path");
3697
+ init_sign();
3698
+ SAFE_MEMBER_RE = /^[A-Za-z0-9._/-]+$/;
3699
+ }
3700
+ });
3701
+
3702
+ // packages/memorypack/src/stream.ts
3703
+ var init_stream = __esm({
3704
+ "packages/memorypack/src/stream.ts"() {
3705
+ "use strict";
3706
+ init_sign();
3707
+ }
3708
+ });
3709
+
3710
+ // packages/memorypack/src/chain-verify.ts
3711
+ async function verifyChainAnchors(anchors, opts) {
3712
+ const verified = /* @__PURE__ */ new Set();
3713
+ const warnings = [];
3714
+ if (anchors.length === 0) return { verified, warnings };
3715
+ const conn = new import_web3.Connection(opts.rpcUrl, { commitment: "confirmed" });
3716
+ if (opts.cluster) {
3717
+ try {
3718
+ const genesis = await conn.getGenesisHash();
3719
+ const expected = GENESIS_HASHES[opts.cluster];
3720
+ if (!expected) {
3721
+ const msg = `unknown cluster '${opts.cluster}' \u2014 cannot cross-check genesis`;
3722
+ if (opts.strict) throw new Error(msg);
3723
+ warnings.push(msg);
3724
+ } else if (genesis !== expected) {
3725
+ const msg = `cluster mismatch: rpc reports genesis ${genesis}, expected ${expected} for ${opts.cluster}`;
3726
+ if (opts.strict) throw new Error(msg);
3727
+ warnings.push(msg);
3728
+ return { verified, warnings };
3729
+ }
3730
+ } catch (e) {
3731
+ const msg = `getGenesisHash failed: ${e.message}`;
3732
+ if (opts.strict) throw new Error(msg);
3733
+ warnings.push(msg);
3734
+ }
3735
+ }
3736
+ for (const anchor of anchors) {
3737
+ try {
3738
+ await verifyOne(anchor, conn, opts, verified);
3739
+ } catch (e) {
3740
+ const msg = `${anchor.tx} \u2192 ${e.message}`;
3741
+ if (opts.strict) throw new Error(msg);
3742
+ warnings.push(msg);
3743
+ }
3744
+ }
3745
+ return { verified, warnings };
3746
+ }
3747
+ async function verifyOne(anchor, conn, opts, verified) {
3748
+ if (anchor.anchor_format !== "memo-v1") {
3749
+ throw new Error(`unsupported anchor_format '${anchor.anchor_format}'`);
3750
+ }
3751
+ const cfg = {
3752
+ commitment: "confirmed",
3753
+ maxSupportedTransactionVersion: 0
3754
+ };
3755
+ const tx = await conn.getTransaction(anchor.tx, cfg);
3756
+ if (!tx) {
3757
+ throw new Error("transaction not found");
3758
+ }
3759
+ const message = tx.transaction.message;
3760
+ let accountKeys;
3761
+ if (Array.isArray(message.accountKeys)) {
3762
+ accountKeys = message.accountKeys;
3763
+ } else if (typeof message.getAccountKeys === "function") {
3764
+ accountKeys = message.getAccountKeys({
3765
+ accountKeysFromLookups: tx.meta?.loadedAddresses
3766
+ }).staticAccountKeys;
3767
+ } else {
3768
+ throw new Error("cannot resolve account keys from transaction message");
3769
+ }
3770
+ const numSigners = message.header.numRequiredSignatures;
3771
+ const signerKeys = accountKeys.slice(0, numSigners).map((k) => k.toBase58());
3772
+ const expectedMemo = expectedMemoForRecordHash(anchor.record_hash);
3773
+ const instructions = message.compiledInstructions ?? message.instructions ?? [];
3774
+ let memoInstructionFound = false;
3775
+ let memoMatched = false;
3776
+ for (const ix of instructions) {
3777
+ const programId = accountKeys[ix.programIdIndex]?.toBase58();
3778
+ if (!programId || !MEMO_PROGRAM_IDS.has(programId)) continue;
3779
+ memoInstructionFound = true;
3780
+ const dataStr = decodeMemoInstructionData(ix.data);
3781
+ if (dataStr === expectedMemo) {
3782
+ memoMatched = true;
3783
+ break;
3784
+ }
3785
+ }
3786
+ if (!memoInstructionFound) {
3787
+ throw new Error("no SPL Memo program instruction in transaction");
3788
+ }
3789
+ if (!memoMatched) {
3790
+ throw new Error(`no Memo instruction with data exactly equal to "${expectedMemo}"`);
3791
+ }
3792
+ if (opts.expectedSigner) {
3793
+ if (!signerKeys.includes(opts.expectedSigner)) {
3794
+ throw new Error(
3795
+ `expectedSigner ${opts.expectedSigner} is not among tx signers [${signerKeys.join(", ")}]`
3796
+ );
3797
+ }
3798
+ }
3799
+ verified.add(anchor.record_hash);
3800
+ }
3801
+ function expectedMemoForRecordHash(recordHash) {
3802
+ return `clude:v1:${recordHash}`;
3803
+ }
3804
+ function decodeMemoInstructionData(data) {
3805
+ if (typeof data === "string") {
3806
+ const bs583 = loadBs58();
3807
+ const bytes = bs583.decode(data);
3808
+ return Buffer.from(bytes).toString("utf-8");
3809
+ }
3810
+ if (data instanceof Uint8Array || Buffer.isBuffer(data)) {
3811
+ return Buffer.from(data).toString("utf-8");
3812
+ }
3813
+ if (data && typeof data === "object" && "type" in data && "data" in data) {
3814
+ return Buffer.from(data.data).toString("utf-8");
3815
+ }
3816
+ throw new Error(`unrecognized instruction data shape: ${typeof data}`);
3817
+ }
3818
+ function loadBs58() {
3819
+ if (_bs58) return _bs58;
3820
+ const mod = require("bs58");
3821
+ _bs58 = mod.default || mod;
3822
+ return _bs58;
3823
+ }
3824
+ var import_web3, MEMO_PROGRAM_IDS, GENESIS_HASHES, _bs58;
3825
+ var init_chain_verify = __esm({
3826
+ "packages/memorypack/src/chain-verify.ts"() {
3827
+ "use strict";
3828
+ import_web3 = require("@solana/web3.js");
3829
+ MEMO_PROGRAM_IDS = /* @__PURE__ */ new Set([
3830
+ "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr",
3831
+ "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"
3832
+ ]);
3833
+ GENESIS_HASHES = {
3834
+ mainnet: "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
3835
+ "mainnet-beta": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
3836
+ devnet: "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG",
3837
+ testnet: "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY"
3838
+ };
3839
+ _bs58 = null;
3840
+ }
3841
+ });
3842
+
3843
+ // packages/memorypack/src/index.ts
3844
+ var init_src = __esm({
3845
+ "packages/memorypack/src/index.ts"() {
3846
+ "use strict";
3847
+ init_types();
3848
+ init_writer();
3849
+ init_reader();
3850
+ init_stream();
3851
+ init_chain_verify();
3852
+ init_sign();
3853
+ }
3854
+ });
3855
+
2800
3856
  // packages/brain/src/mcp/local-store.ts
2801
3857
  var local_store_exports = {};
2802
3858
  __export(local_store_exports, {
@@ -2809,22 +3865,22 @@ __export(local_store_exports, {
2809
3865
  localUpdate: () => localUpdate
2810
3866
  });
2811
3867
  function ensureDir() {
2812
- if (!(0, import_fs2.existsSync)(CLUDE_DIR2)) {
2813
- (0, import_fs2.mkdirSync)(CLUDE_DIR2, { recursive: true });
3868
+ if (!(0, import_fs4.existsSync)(CLUDE_DIR2)) {
3869
+ (0, import_fs4.mkdirSync)(CLUDE_DIR2, { recursive: true });
2814
3870
  }
2815
3871
  }
2816
3872
  function loadStore() {
2817
3873
  ensureDir();
2818
- if (!(0, import_fs2.existsSync)(MEMORIES_FILE2)) {
3874
+ if (!(0, import_fs4.existsSync)(MEMORIES_FILE2)) {
2819
3875
  return { version: 1, next_id: 1, memories: [] };
2820
3876
  }
2821
3877
  try {
2822
- const raw = (0, import_fs2.readFileSync)(MEMORIES_FILE2, "utf-8");
3878
+ const raw = (0, import_fs4.readFileSync)(MEMORIES_FILE2, "utf-8");
2823
3879
  const data = JSON.parse(raw);
2824
3880
  if (!data || !Array.isArray(data.memories)) {
2825
3881
  console.error("[clude-local] Warning: memories.json has invalid structure, starting fresh. Backup saved.");
2826
3882
  try {
2827
- (0, import_fs2.writeFileSync)(MEMORIES_FILE2 + ".bak", raw);
3883
+ (0, import_fs4.writeFileSync)(MEMORIES_FILE2 + ".bak", raw);
2828
3884
  } catch {
2829
3885
  }
2830
3886
  return { version: 1, next_id: 1, memories: [] };
@@ -2833,8 +3889,8 @@ function loadStore() {
2833
3889
  } catch (err) {
2834
3890
  console.error("[clude-local] Warning: failed to parse memories.json:", err.message);
2835
3891
  try {
2836
- const raw = (0, import_fs2.readFileSync)(MEMORIES_FILE2, "utf-8");
2837
- (0, import_fs2.writeFileSync)(MEMORIES_FILE2 + ".bak", raw);
3892
+ const raw = (0, import_fs4.readFileSync)(MEMORIES_FILE2, "utf-8");
3893
+ (0, import_fs4.writeFileSync)(MEMORIES_FILE2 + ".bak", raw);
2838
3894
  console.error("[clude-local] Backup saved to memories.json.bak");
2839
3895
  } catch {
2840
3896
  }
@@ -2844,8 +3900,8 @@ function loadStore() {
2844
3900
  function saveStore(store) {
2845
3901
  ensureDir();
2846
3902
  const tmpFile = MEMORIES_FILE2 + ".tmp";
2847
- (0, import_fs2.writeFileSync)(tmpFile, JSON.stringify(store, null, 2));
2848
- (0, import_fs2.renameSync)(tmpFile, MEMORIES_FILE2);
3903
+ (0, import_fs4.writeFileSync)(tmpFile, JSON.stringify(store, null, 2));
3904
+ (0, import_fs4.renameSync)(tmpFile, MEMORIES_FILE2);
2849
3905
  }
2850
3906
  function scoreRelevance(query, memory) {
2851
3907
  const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
@@ -3031,14 +4087,14 @@ function localList(opts) {
3031
4087
  const start = (page - 1) * pageSize;
3032
4088
  return { memories: memories.slice(start, start + pageSize), total };
3033
4089
  }
3034
- var import_fs2, import_path3, CLUDE_DIR2, MEMORIES_FILE2;
4090
+ var import_fs4, import_path5, CLUDE_DIR2, MEMORIES_FILE2;
3035
4091
  var init_local_store = __esm({
3036
4092
  "packages/brain/src/mcp/local-store.ts"() {
3037
4093
  "use strict";
3038
- import_fs2 = require("fs");
3039
- import_path3 = require("path");
3040
- CLUDE_DIR2 = (0, import_path3.join)(process.env.HOME || process.env.USERPROFILE || ".", ".clude");
3041
- MEMORIES_FILE2 = (0, import_path3.join)(CLUDE_DIR2, "memories.json");
4094
+ import_fs4 = require("fs");
4095
+ import_path5 = require("path");
4096
+ CLUDE_DIR2 = (0, import_path5.join)(process.env.HOME || process.env.USERPROFILE || ".", ".clude");
4097
+ MEMORIES_FILE2 = (0, import_path5.join)(CLUDE_DIR2, "memories.json");
3042
4098
  }
3043
4099
  });
3044
4100
 
@@ -3160,13 +4216,18 @@ async function initDatabase2() {
3160
4216
  is_active BOOLEAN DEFAULT TRUE,
3161
4217
  metadata JSONB DEFAULT '{}',
3162
4218
  owner_wallet TEXT,
3163
- privy_did TEXT
4219
+ privy_did TEXT,
4220
+ email TEXT
3164
4221
  );
3165
4222
 
4223
+ -- Backfill: older deployments may have agent_keys without the email column.
4224
+ ALTER TABLE agent_keys ADD COLUMN IF NOT EXISTS email TEXT;
4225
+
3166
4226
  CREATE INDEX IF NOT EXISTS idx_agent_keys_api_key ON agent_keys(api_key);
3167
4227
  CREATE INDEX IF NOT EXISTS idx_agent_keys_owner ON agent_keys(owner_wallet);
3168
4228
  CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_owner_unique ON agent_keys(owner_wallet) WHERE owner_wallet IS NOT NULL;
3169
4229
  CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_privy_did ON agent_keys(privy_did) WHERE privy_did IS NOT NULL;
4230
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_email ON agent_keys(email) WHERE email IS NOT NULL AND is_active = true;
3170
4231
 
3171
4232
  -- Cortex recall performance: owner_wallet scoped queries
3172
4233
  CREATE INDEX IF NOT EXISTS idx_cortex_owner_recall ON memories(owner_wallet, decay_factor DESC, created_at DESC);
@@ -3600,6 +4661,19 @@ async function initDatabase2() {
3600
4661
  created_at TIMESTAMPTZ DEFAULT NOW()
3601
4662
  );
3602
4663
  CREATE INDEX IF NOT EXISTS idx_chat_usage_wallet ON chat_usage(wallet_address, created_at DESC);
4664
+
4665
+ -- Wiki pack installations: which packs (Workspace, Compliance, Sales)
4666
+ -- each wallet has installed. Drives the topic rail in /wiki and
4667
+ -- the auto-categorisation rules applied to incoming memories.
4668
+ CREATE TABLE IF NOT EXISTS wiki_pack_installations (
4669
+ id BIGSERIAL PRIMARY KEY,
4670
+ owner_wallet TEXT NOT NULL,
4671
+ pack_id TEXT NOT NULL,
4672
+ installed_at TIMESTAMPTZ DEFAULT NOW(),
4673
+ UNIQUE (owner_wallet, pack_id)
4674
+ );
4675
+ CREATE INDEX IF NOT EXISTS idx_wiki_pack_installations_owner
4676
+ ON wiki_pack_installations(owner_wallet);
3603
4677
  `
3604
4678
  });
3605
4679
  if (error) {
@@ -3679,11 +4753,12 @@ var init_text = __esm({
3679
4753
  });
3680
4754
 
3681
4755
  // packages/shared/src/utils/constants.ts
3682
- var MEMO_PROGRAM_ID, BOT_X_HANDLE, MEMORY_MIN_DECAY, MEMORY_MAX_CONTENT_LENGTH, MEMORY_MAX_SUMMARY_LENGTH, DECAY_RATES, RECENCY_DECAY_BASE, RETRIEVAL_WEIGHT_RECENCY, RETRIEVAL_WEIGHT_RELEVANCE, RETRIEVAL_WEIGHT_IMPORTANCE, RETRIEVAL_WEIGHT_VECTOR, KNOWLEDGE_TYPE_BOOST, VECTOR_MATCH_THRESHOLD, BOND_TYPE_WEIGHTS, RETRIEVAL_WEIGHT_GRAPH, RETRIEVAL_WEIGHT_COOCCURRENCE, LINK_SIMILARITY_THRESHOLD, MAX_AUTO_LINKS, LINK_CO_RETRIEVAL_BOOST, INTERNAL_MEMORY_SOURCES, INTERNAL_IMPORTANCE_BOOST, REFLECTION_MIN_INTERVAL_MS, WHALE_SELL_COOLDOWN_MS, MEMO_MAX_LENGTH;
4756
+ var MEMO_PROGRAM_ID, SOLSCAN_TX_BASE_URL, BOT_X_HANDLE, MEMORY_MIN_DECAY, MEMORY_MAX_CONTENT_LENGTH, MEMORY_MAX_SUMMARY_LENGTH, DECAY_RATES, RECENCY_DECAY_BASE, RETRIEVAL_WEIGHT_RECENCY, RETRIEVAL_WEIGHT_RELEVANCE, RETRIEVAL_WEIGHT_IMPORTANCE, RETRIEVAL_WEIGHT_VECTOR, KNOWLEDGE_TYPE_BOOST, VECTOR_MATCH_THRESHOLD, BOND_TYPE_WEIGHTS, RETRIEVAL_WEIGHT_GRAPH, RETRIEVAL_WEIGHT_COOCCURRENCE, LINK_SIMILARITY_THRESHOLD, MAX_AUTO_LINKS, LINK_CO_RETRIEVAL_BOOST, INTERNAL_MEMORY_SOURCES, INTERNAL_IMPORTANCE_BOOST, REFLECTION_MIN_INTERVAL_MS, WHALE_SELL_COOLDOWN_MS, MEMO_MAX_LENGTH;
3683
4757
  var init_constants = __esm({
3684
4758
  "packages/shared/src/utils/constants.ts"() {
3685
4759
  "use strict";
3686
4760
  MEMO_PROGRAM_ID = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr";
4761
+ SOLSCAN_TX_BASE_URL = "https://solscan.io/tx";
3687
4762
  BOT_X_HANDLE = process.env.BOT_X_HANDLE || "cludebot";
3688
4763
  MEMORY_MIN_DECAY = 0.05;
3689
4764
  MEMORY_MAX_CONTENT_LENGTH = 5e3;
@@ -3883,11 +4958,13 @@ var init_openrouter_client = __esm({
3883
4958
  OPENROUTER_API_URL = "https://openrouter.ai/api/v1";
3884
4959
  OPENROUTER_MODELS = {
3885
4960
  // Frontier (Anthropic)
4961
+ "claude-opus-4.7": "anthropic/claude-opus-4.7",
3886
4962
  "claude-opus-4.6": "anthropic/claude-opus-4.6",
3887
4963
  "claude-sonnet-4.6": "anthropic/claude-sonnet-4.6",
3888
4964
  "claude-opus-4.5": "anthropic/claude-opus-4.5",
3889
4965
  "claude-sonnet-4.5": "anthropic/claude-sonnet-4.5",
3890
4966
  // Frontier (Other providers)
4967
+ "gpt-5.5": "openai/gpt-5.5",
3891
4968
  "gpt-5.4": "openai/gpt-5.4",
3892
4969
  "grok-4.1": "x-ai/grok-4.1-fast",
3893
4970
  "gemini-3-pro": "google/gemini-3-pro-preview",
@@ -3990,12 +5067,46 @@ var init_claude_client = __esm({
3990
5067
  });
3991
5068
 
3992
5069
  // packages/shared/src/core/solana-client.ts
5070
+ var solana_client_exports = {};
5071
+ __export(solana_client_exports, {
5072
+ _configureMemoryRegistry: () => _configureMemoryRegistry,
5073
+ _configureSolana: () => _configureSolana,
5074
+ getBotWallet: () => getBotWallet,
5075
+ getConnection: () => getConnection,
5076
+ initializeRegistry: () => initializeRegistry,
5077
+ isRegistryEnabled: () => isRegistryEnabled,
5078
+ parseMemoContentHash: () => parseMemoContentHash,
5079
+ registerMemoryOnChain: () => registerMemoryOnChain,
5080
+ solscanTxUrl: () => solscanTxUrl,
5081
+ verifyMemoTransaction: () => verifyMemoTransaction,
5082
+ verifyMemoryOnChain: () => verifyMemoryOnChain,
5083
+ verifySignature: () => verifySignature,
5084
+ writeMemo: () => writeMemo,
5085
+ writeMemoDevnet: () => writeMemoDevnet
5086
+ });
3993
5087
  function getConnection() {
3994
5088
  if (!connection) {
3995
- connection = new import_web3.Connection(config.solana.rpcUrl, "confirmed");
5089
+ connection = new import_web32.Connection(config.solana.rpcUrl, "confirmed");
3996
5090
  }
3997
5091
  return connection;
3998
5092
  }
5093
+ function _configureSolana(rpcUrl, privateKey) {
5094
+ connection = new import_web32.Connection(rpcUrl, "confirmed");
5095
+ if (privateKey) {
5096
+ try {
5097
+ const raw = privateKey.trim();
5098
+ let secretKey;
5099
+ if (raw.startsWith("[")) {
5100
+ secretKey = Uint8Array.from(JSON.parse(raw));
5101
+ } else {
5102
+ secretKey = bs582.decode(raw);
5103
+ }
5104
+ botWallet = import_web32.Keypair.fromSecretKey(secretKey);
5105
+ } catch (err) {
5106
+ log6.error({ err }, "SDK: Failed to load bot wallet");
5107
+ }
5108
+ }
5109
+ }
3999
5110
  function getBotWallet() {
4000
5111
  if (!botWallet && config.solana.botWalletPrivateKey) {
4001
5112
  try {
@@ -4004,9 +5115,9 @@ function getBotWallet() {
4004
5115
  if (raw.startsWith("[")) {
4005
5116
  secretKey = Uint8Array.from(JSON.parse(raw));
4006
5117
  } else {
4007
- secretKey = bs58.decode(raw);
5118
+ secretKey = bs582.decode(raw);
4008
5119
  }
4009
- botWallet = import_web3.Keypair.fromSecretKey(secretKey);
5120
+ botWallet = import_web32.Keypair.fromSecretKey(secretKey);
4010
5121
  log6.info({ publicKey: botWallet.publicKey.toBase58() }, "Bot wallet loaded");
4011
5122
  } catch (err) {
4012
5123
  log6.error({ err }, "Failed to load bot wallet");
@@ -4022,14 +5133,14 @@ async function writeMemo(memo) {
4022
5133
  }
4023
5134
  const conn = getConnection();
4024
5135
  const truncatedMemo = memo.slice(0, MEMO_MAX_LENGTH);
4025
- const instruction = new import_web3.TransactionInstruction({
5136
+ const instruction = new import_web32.TransactionInstruction({
4026
5137
  keys: [{ pubkey: wallet.publicKey, isSigner: true, isWritable: true }],
4027
5138
  programId: MEMO_PROGRAM_ID2,
4028
5139
  data: Buffer.from(truncatedMemo, "utf-8")
4029
5140
  });
4030
- const transaction = new import_web3.Transaction().add(instruction);
5141
+ const transaction = new import_web32.Transaction().add(instruction);
4031
5142
  try {
4032
- const signature = await (0, import_web3.sendAndConfirmTransaction)(conn, transaction, [wallet]);
5143
+ const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
4033
5144
  log6.info({ signature, memoLength: truncatedMemo.length }, "Memo written on-chain");
4034
5145
  return signature;
4035
5146
  } catch (err) {
@@ -4037,21 +5148,97 @@ async function writeMemo(memo) {
4037
5148
  return null;
4038
5149
  }
4039
5150
  }
5151
+ function getDevnetConnection() {
5152
+ if (!devnetConnection) {
5153
+ devnetConnection = new import_web32.Connection("https://api.devnet.solana.com", "confirmed");
5154
+ }
5155
+ return devnetConnection;
5156
+ }
5157
+ async function writeMemoDevnet(memo) {
5158
+ const wallet = getBotWallet();
5159
+ if (!wallet) {
5160
+ log6.error("No bot wallet configured, cannot write devnet memo");
5161
+ return null;
5162
+ }
5163
+ const conn = getDevnetConnection();
5164
+ const truncatedMemo = memo.slice(0, MEMO_MAX_LENGTH);
5165
+ const instruction = new import_web32.TransactionInstruction({
5166
+ keys: [{ pubkey: wallet.publicKey, isSigner: true, isWritable: true }],
5167
+ programId: MEMO_PROGRAM_ID2,
5168
+ data: Buffer.from(truncatedMemo, "utf-8")
5169
+ });
5170
+ const transaction = new import_web32.Transaction().add(instruction);
5171
+ try {
5172
+ const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
5173
+ log6.info({ signature, memoLength: truncatedMemo.length }, "Memo written on devnet");
5174
+ return signature;
5175
+ } catch (err) {
5176
+ log6.error({ err }, "Failed to write devnet memo");
5177
+ return null;
5178
+ }
5179
+ }
5180
+ function verifySignature(message, signature, publicKey) {
5181
+ const messageBytes = new TextEncoder().encode(message);
5182
+ return import_tweetnacl2.default.sign.detached.verify(messageBytes, signature, publicKey);
5183
+ }
5184
+ function solscanTxUrl(signature) {
5185
+ return `${SOLSCAN_TX_BASE_URL}/${signature}`;
5186
+ }
5187
+ function _configureMemoryRegistry(programId) {
5188
+ try {
5189
+ registryProgramId = new import_web32.PublicKey(programId);
5190
+ log6.info({ programId: programId.slice(0, 12) + "..." }, "Memory registry program configured");
5191
+ } catch (err) {
5192
+ log6.error({ err }, "Invalid memory registry program ID");
5193
+ }
5194
+ }
4040
5195
  function isRegistryEnabled() {
4041
5196
  return registryProgramId !== null;
4042
5197
  }
4043
5198
  function deriveRegistryPDA(authority) {
4044
5199
  if (!registryProgramId) throw new Error("Registry program not configured");
4045
- return import_web3.PublicKey.findProgramAddressSync(
5200
+ return import_web32.PublicKey.findProgramAddressSync(
4046
5201
  [Buffer.from("memory-registry"), authority.toBuffer()],
4047
5202
  registryProgramId
4048
5203
  );
4049
5204
  }
4050
5205
  function anchorDiscriminator(name) {
4051
- const { createHash: createHash3 } = require("crypto");
4052
- const hash = createHash3("sha256").update(`global:${name}`).digest();
5206
+ const { createHash: createHash5 } = require("crypto");
5207
+ const hash = createHash5("sha256").update(`global:${name}`).digest();
4053
5208
  return hash.subarray(0, 8);
4054
5209
  }
5210
+ async function initializeRegistry() {
5211
+ if (registryInitialized) return;
5212
+ const wallet = getBotWallet();
5213
+ if (!wallet || !registryProgramId) return;
5214
+ const conn = getConnection();
5215
+ const [registryPDA] = deriveRegistryPDA(wallet.publicKey);
5216
+ const accountInfo = await conn.getAccountInfo(registryPDA);
5217
+ if (accountInfo) {
5218
+ registryInitialized = true;
5219
+ log6.info({ registry: registryPDA.toBase58() }, "Registry PDA already exists");
5220
+ return;
5221
+ }
5222
+ const discriminator = anchorDiscriminator("initialize");
5223
+ const instruction = new import_web32.TransactionInstruction({
5224
+ keys: [
5225
+ { pubkey: registryPDA, isSigner: false, isWritable: true },
5226
+ { pubkey: wallet.publicKey, isSigner: true, isWritable: true },
5227
+ { pubkey: import_web32.SystemProgram.programId, isSigner: false, isWritable: false }
5228
+ ],
5229
+ programId: registryProgramId,
5230
+ data: discriminator
5231
+ });
5232
+ const transaction = new import_web32.Transaction().add(instruction);
5233
+ try {
5234
+ const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
5235
+ registryInitialized = true;
5236
+ log6.info({ signature, registry: registryPDA.toBase58() }, "Registry PDA initialized on-chain");
5237
+ } catch (err) {
5238
+ log6.error({ err }, "Failed to initialize registry PDA");
5239
+ throw err;
5240
+ }
5241
+ }
4055
5242
  function memoryTypeToU8(type) {
4056
5243
  switch (type) {
4057
5244
  case "episodic":
@@ -4084,18 +5271,18 @@ async function registerMemoryOnChain(contentHash, memoryType, importance, memory
4084
5271
  data.writeUInt8(importanceToTier(importance), 41);
4085
5272
  data.writeBigUInt64LE(BigInt(memoryId), 42);
4086
5273
  data.writeUInt8(encrypted ? 1 : 0, 50);
4087
- const instruction = new import_web3.TransactionInstruction({
5274
+ const instruction = new import_web32.TransactionInstruction({
4088
5275
  keys: [
4089
5276
  { pubkey: registryPDA, isSigner: false, isWritable: true },
4090
5277
  { pubkey: wallet.publicKey, isSigner: true, isWritable: true },
4091
- { pubkey: import_web3.SystemProgram.programId, isSigner: false, isWritable: false }
5278
+ { pubkey: import_web32.SystemProgram.programId, isSigner: false, isWritable: false }
4092
5279
  ],
4093
5280
  programId: registryProgramId,
4094
5281
  data
4095
5282
  });
4096
- const transaction = new import_web3.Transaction().add(instruction);
5283
+ const transaction = new import_web32.Transaction().add(instruction);
4097
5284
  try {
4098
- const signature = await (0, import_web3.sendAndConfirmTransaction)(conn, transaction, [wallet]);
5285
+ const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
4099
5286
  log6.info({ signature: signature.slice(0, 16), memoryId }, "Memory registered on-chain");
4100
5287
  return signature;
4101
5288
  } catch (err) {
@@ -4103,21 +5290,196 @@ async function registerMemoryOnChain(contentHash, memoryType, importance, memory
4103
5290
  return null;
4104
5291
  }
4105
5292
  }
4106
- var import_web3, bs58Module, import_tweetnacl, bs58, log6, connection, botWallet, MEMO_PROGRAM_ID2, registryProgramId;
5293
+ async function verifyMemoryOnChain(contentHash, authority) {
5294
+ const wallet = getBotWallet();
5295
+ const authPubkey = authority || wallet?.publicKey;
5296
+ if (!authPubkey || !registryProgramId) return false;
5297
+ const conn = getConnection();
5298
+ const [registryPDA] = deriveRegistryPDA(authPubkey);
5299
+ try {
5300
+ const accountInfo = await conn.getAccountInfo(registryPDA);
5301
+ if (!accountInfo || !accountInfo.data) return false;
5302
+ const data = accountInfo.data;
5303
+ if (data.length < 53) return false;
5304
+ const vecLen = data.readUInt32LE(49);
5305
+ const ENTRY_SIZE = 56;
5306
+ const entriesStart = 53;
5307
+ for (let i = 0; i < vecLen; i++) {
5308
+ const offset = entriesStart + i * ENTRY_SIZE;
5309
+ if (offset + 32 > data.length) break;
5310
+ const hash = data.subarray(offset, offset + 32);
5311
+ if (contentHash.equals(hash)) return true;
5312
+ }
5313
+ return false;
5314
+ } catch (err) {
5315
+ log6.warn({ err }, "Failed to verify memory on-chain");
5316
+ return false;
5317
+ }
5318
+ }
5319
+ function parseMemoContentHash(memo) {
5320
+ if (!memo.startsWith("clude-memory |")) return null;
5321
+ const v2Match = memo.match(/^clude-memory \| v2 \| ([a-f0-9]{64})$/);
5322
+ if (v2Match) return v2Match[1];
5323
+ const v1Match = memo.match(/^clude-memory \| id: \d+ \| type: \w+ \| hash: ([a-f0-9]{16}) \|/);
5324
+ if (v1Match) return v1Match[1];
5325
+ return null;
5326
+ }
5327
+ async function verifyMemoTransaction(signature, contentHash) {
5328
+ const conn = getConnection();
5329
+ try {
5330
+ const tx = await conn.getTransaction(signature, { maxSupportedTransactionVersion: 0 });
5331
+ if (!tx?.meta || !tx.transaction.message) return false;
5332
+ const memoProgram = MEMO_PROGRAM_ID2.toBase58();
5333
+ const message = tx.transaction.message;
5334
+ const accountKeys = message.staticAccountKeys?.map((k) => k.toBase58()) ?? [];
5335
+ const compiledIxs = message.compiledInstructions;
5336
+ for (const ix of compiledIxs) {
5337
+ if (accountKeys[ix.programIdIndex] !== memoProgram) continue;
5338
+ const memoStr = Buffer.from(ix.data).toString("utf-8");
5339
+ const parsedHash = parseMemoContentHash(memoStr);
5340
+ if (!parsedHash) continue;
5341
+ const contentHashHex = contentHash.toString("hex");
5342
+ if (parsedHash.length === 64) {
5343
+ return parsedHash === contentHashHex;
5344
+ }
5345
+ return contentHashHex.startsWith(parsedHash);
5346
+ }
5347
+ return false;
5348
+ } catch (err) {
5349
+ log6.warn({ err, signature: signature.slice(0, 16) }, "Failed to verify memo transaction");
5350
+ return false;
5351
+ }
5352
+ }
5353
+ var import_web32, bs58Module2, import_tweetnacl2, bs582, log6, connection, botWallet, MEMO_PROGRAM_ID2, devnetConnection, registryProgramId, registryInitialized;
4107
5354
  var init_solana_client = __esm({
4108
5355
  "packages/shared/src/core/solana-client.ts"() {
4109
5356
  "use strict";
4110
- import_web3 = require("@solana/web3.js");
4111
- bs58Module = __toESM(require("bs58"));
5357
+ import_web32 = require("@solana/web3.js");
5358
+ bs58Module2 = __toESM(require("bs58"));
4112
5359
  init_config();
4113
5360
  init_logger();
4114
- import_tweetnacl = __toESM(require("tweetnacl"));
5361
+ import_tweetnacl2 = __toESM(require("tweetnacl"));
4115
5362
  init_constants();
4116
- bs58 = bs58Module.default || bs58Module;
5363
+ bs582 = bs58Module2.default || bs58Module2;
4117
5364
  log6 = createChildLogger("solana-client");
4118
5365
  botWallet = null;
4119
- MEMO_PROGRAM_ID2 = new import_web3.PublicKey(MEMO_PROGRAM_ID);
5366
+ MEMO_PROGRAM_ID2 = new import_web32.PublicKey(MEMO_PROGRAM_ID);
4120
5367
  registryProgramId = null;
5368
+ registryInitialized = false;
5369
+ }
5370
+ });
5371
+
5372
+ // packages/tokenization/src/content-hash.ts
5373
+ function stableStringify(value) {
5374
+ if (value === null) return "null";
5375
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
5376
+ return JSON.stringify(value);
5377
+ }
5378
+ if (Array.isArray(value)) {
5379
+ return "[" + value.map(stableStringify).join(",") + "]";
5380
+ }
5381
+ if (typeof value === "object") {
5382
+ const obj = value;
5383
+ const keys = Object.keys(obj).sort();
5384
+ const pairs = keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k]));
5385
+ return "{" + pairs.join(",") + "}";
5386
+ }
5387
+ throw new Error(`stableStringify: unsupported value of type ${typeof value}`);
5388
+ }
5389
+ function normaliseString(s) {
5390
+ return s.normalize("NFC").trim();
5391
+ }
5392
+ function normaliseNullable(s) {
5393
+ return s === null ? null : normaliseString(s);
5394
+ }
5395
+ function normaliseTags(tags) {
5396
+ const trimmed = tags.map(normaliseString).filter((t) => t.length > 0);
5397
+ return Array.from(new Set(trimmed)).sort();
5398
+ }
5399
+ function canonicaliseMemory(input) {
5400
+ const normalised = {
5401
+ algorithm: HASH_ALGORITHM,
5402
+ content: normaliseString(input.content),
5403
+ created_at: input.created_at,
5404
+ memory_type: input.memory_type,
5405
+ owner_wallet: normaliseNullable(input.owner_wallet),
5406
+ related_user: normaliseNullable(input.related_user),
5407
+ related_wallet: normaliseNullable(input.related_wallet),
5408
+ source: normaliseNullable(input.source),
5409
+ tags: normaliseTags(input.tags)
5410
+ };
5411
+ return stableStringify(normalised);
5412
+ }
5413
+ function memoryContentHash(input) {
5414
+ return (0, import_node_crypto.createHash)("sha256").update(canonicaliseMemory(input), "utf8").digest("hex");
5415
+ }
5416
+ var import_node_crypto, HASH_ALGORITHM;
5417
+ var init_content_hash = __esm({
5418
+ "packages/tokenization/src/content-hash.ts"() {
5419
+ "use strict";
5420
+ import_node_crypto = require("node:crypto");
5421
+ HASH_ALGORITHM = "memory-hash-v1";
5422
+ }
5423
+ });
5424
+
5425
+ // packages/tokenization/src/pack-merkle.ts
5426
+ var init_pack_merkle = __esm({
5427
+ "packages/tokenization/src/pack-merkle.ts"() {
5428
+ "use strict";
5429
+ }
5430
+ });
5431
+
5432
+ // packages/tokenization/src/mint-client.ts
5433
+ var init_mint_client = __esm({
5434
+ "packages/tokenization/src/mint-client.ts"() {
5435
+ "use strict";
5436
+ }
5437
+ });
5438
+
5439
+ // packages/tokenization/src/tokenize-memory.ts
5440
+ var init_tokenize_memory = __esm({
5441
+ "packages/tokenization/src/tokenize-memory.ts"() {
5442
+ "use strict";
5443
+ init_content_hash();
5444
+ }
5445
+ });
5446
+
5447
+ // packages/tokenization/src/tokenize-batch.ts
5448
+ var init_tokenize_batch = __esm({
5449
+ "packages/tokenization/src/tokenize-batch.ts"() {
5450
+ "use strict";
5451
+ init_content_hash();
5452
+ init_pack_merkle();
5453
+ }
5454
+ });
5455
+
5456
+ // packages/tokenization/src/tokenize-pack.ts
5457
+ var init_tokenize_pack = __esm({
5458
+ "packages/tokenization/src/tokenize-pack.ts"() {
5459
+ "use strict";
5460
+ init_pack_merkle();
5461
+ }
5462
+ });
5463
+
5464
+ // packages/tokenization/src/verify.ts
5465
+ var init_verify = __esm({
5466
+ "packages/tokenization/src/verify.ts"() {
5467
+ "use strict";
5468
+ init_pack_merkle();
5469
+ }
5470
+ });
5471
+
5472
+ // packages/tokenization/src/index.ts
5473
+ var init_src2 = __esm({
5474
+ "packages/tokenization/src/index.ts"() {
5475
+ "use strict";
5476
+ init_content_hash();
5477
+ init_pack_merkle();
5478
+ init_mint_client();
5479
+ init_tokenize_memory();
5480
+ init_tokenize_batch();
5481
+ init_tokenize_pack();
5482
+ init_verify();
4121
5483
  }
4122
5484
  });
4123
5485
 
@@ -4295,6 +5657,221 @@ var init_embeddings = __esm({
4295
5657
  }
4296
5658
  });
4297
5659
 
5660
+ // packages/shared/src/wiki-packs.ts
5661
+ function getPackManifest(id) {
5662
+ return ALL_PACK_MANIFESTS.find((p) => p.id === id);
5663
+ }
5664
+ function matchesKeyword(text, phrase) {
5665
+ if (!phrase) return false;
5666
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5667
+ const re = new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i");
5668
+ return re.test(text);
5669
+ }
5670
+ function autoCategorizeTags(opts) {
5671
+ const haystack = [
5672
+ opts.content || "",
5673
+ opts.summary || "",
5674
+ ...opts.existingTags || []
5675
+ ].join(" ");
5676
+ const matched = new Set(opts.existingTags || []);
5677
+ const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...opts.installedPackIds]);
5678
+ for (const packId of ids) {
5679
+ const pack = getPackManifest(packId);
5680
+ if (!pack) continue;
5681
+ for (const rule of pack.rules) {
5682
+ if (matched.has(rule.topicId)) continue;
5683
+ const hasMatch = rule.keywords.some((kw) => matchesKeyword(haystack, kw));
5684
+ if (!hasMatch) continue;
5685
+ const isVetoed = (rule.excludeKeywords || []).some((kw) => matchesKeyword(haystack, kw));
5686
+ if (isVetoed) continue;
5687
+ matched.add(rule.topicId);
5688
+ }
5689
+ }
5690
+ return Array.from(matched);
5691
+ }
5692
+ function topicEmbedSources(installedPackIds) {
5693
+ const out = [];
5694
+ const seen = /* @__PURE__ */ new Set();
5695
+ const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...installedPackIds]);
5696
+ for (const packId of ids) {
5697
+ const pack = getPackManifest(packId);
5698
+ if (!pack) continue;
5699
+ for (const t of pack.topics) {
5700
+ if (seen.has(t.id)) continue;
5701
+ seen.add(t.id);
5702
+ const sectionTitles = (t.sectionTemplates || []).map((s) => s.title).join(". ");
5703
+ const text = [t.name, t.summary, sectionTitles].filter(Boolean).join(" \u2014 ");
5704
+ out.push({ topicId: t.id, packId, text });
5705
+ }
5706
+ }
5707
+ return out;
5708
+ }
5709
+ function cosineSimilarity2(a, b) {
5710
+ if (a.length !== b.length || a.length === 0) return 0;
5711
+ let dot = 0;
5712
+ let normA = 0;
5713
+ let normB = 0;
5714
+ for (let i = 0; i < a.length; i++) {
5715
+ dot += a[i] * b[i];
5716
+ normA += a[i] * a[i];
5717
+ normB += b[i] * b[i];
5718
+ }
5719
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
5720
+ return denom === 0 ? 0 : dot / denom;
5721
+ }
5722
+ function semanticTagMatches(memoryEmbedding, topicEmbeddings, threshold = EMBEDDING_TAG_THRESHOLD) {
5723
+ const matched = [];
5724
+ for (const { topicId, embedding } of topicEmbeddings) {
5725
+ const sim = cosineSimilarity2(memoryEmbedding, embedding);
5726
+ if (sim >= threshold) matched.push(topicId);
5727
+ }
5728
+ return matched;
5729
+ }
5730
+ var WORKSPACE_PACK, COMPLIANCE_PACK, SALES_PACK, ALL_PACK_MANIFESTS, DEFAULT_PACK_ID, EMBEDDING_TAG_THRESHOLD;
5731
+ var init_wiki_packs = __esm({
5732
+ "packages/shared/src/wiki-packs.ts"() {
5733
+ "use strict";
5734
+ WORKSPACE_PACK = {
5735
+ id: "workspace",
5736
+ name: "Workspace Essentials",
5737
+ vendor: "Clude",
5738
+ vertical: "General",
5739
+ version: "1.0.0",
5740
+ installedByDefault: true,
5741
+ 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.",
5742
+ topics: [
5743
+ { id: "q3-roadmap", name: "Q3 Roadmap", cluster: "architecture", color: "#2244FF", summary: "What ships this quarter, what got cut, and the calls behind those decisions." },
5744
+ { id: "auth-migration", name: "Auth Migration", cluster: "architecture", color: "#2244FF", summary: "Moving from session cookies to JWT \u2014 incidents, fixes, and the rollback plan." },
5745
+ { id: "customer-research", name: "Customer Research", cluster: "research", color: "#F59E0B", summary: "Patterns surfaced across customer calls." },
5746
+ { id: "pricing-model", name: "Pricing Model", cluster: "product", color: "#10B981", summary: "Per-token vs per-seat \u2014 the active disagreement." },
5747
+ { id: "demo-day-prep", name: "Demo Day Prep", cluster: "product", color: "#10B981", summary: "What works in the live demo, what breaks." },
5748
+ { id: "hiring", name: "Hiring Pipeline", cluster: "product", color: "#10B981", summary: "Candidates in flight and interview-signal patterns." },
5749
+ { id: "team-process", name: "Team Process", cluster: "self", color: "#8B5CF6", summary: "What's working in how the team operates and what isn't." },
5750
+ { id: "design-decisions", name: "Design Decisions", cluster: "research", color: "#F59E0B", summary: "API shapes, naming choices, and the reasons behind them." }
5751
+ ],
5752
+ rules: [
5753
+ { topicId: "q3-roadmap", keywords: ["roadmap", "q3", "quarter", "sprint plan", "priority"] },
5754
+ { topicId: "auth-migration", keywords: ["auth", "jwt", "session cookie", "oauth", "sso", "token migration"] },
5755
+ { topicId: "customer-research", keywords: ["customer call", "interview", "feedback", "user research", "persona"] },
5756
+ { topicId: "pricing-model", keywords: ["pricing", "per-token", "per-seat", "subscription", "billing tier"] },
5757
+ { topicId: "demo-day-prep", keywords: ["demo", "rehearsal", "investor", "pitch"] },
5758
+ {
5759
+ topicId: "hiring",
5760
+ keywords: ["candidate", "interview", "hire", "phone screen", "onsite", "offer"],
5761
+ // Suppress when "hire" is in the wrong context — got fired, rejected an offer,
5762
+ // hire-purchase, etc. The classic substring-match false positive.
5763
+ excludeKeywords: ["fired me", "i was fired", "hire purchase", "rejected my offer", "rejected the offer"]
5764
+ },
5765
+ { topicId: "team-process", keywords: ["standup", "retro", "sprint", "1:1", "process", "team"] },
5766
+ { topicId: "design-decisions", keywords: ["api design", "schema", "naming", "rfc", "design doc"] }
5767
+ ]
5768
+ };
5769
+ COMPLIANCE_PACK = {
5770
+ id: "compliance",
5771
+ name: "Clude Compliance",
5772
+ vendor: "Clude",
5773
+ vertical: "Compliance",
5774
+ version: "1.0.0",
5775
+ 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.",
5776
+ topics: [
5777
+ {
5778
+ id: "audit-logs",
5779
+ name: "Audit Logs",
5780
+ cluster: "architecture",
5781
+ color: "#0EA5E9",
5782
+ summary: "Every flagged agent action with attribution, timestamp, and a Solana-anchored hash a regulator can verify without trusting us.",
5783
+ sectionTemplates: [
5784
+ { id: "recent-events", title: "Recent flagged events", kind: "overview" },
5785
+ { id: "anomalies", title: "Anomalies & escalations", kind: "concern" },
5786
+ { id: "retention", title: "Retention policy", kind: "decision" }
5787
+ ]
5788
+ },
5789
+ {
5790
+ id: "evidence",
5791
+ name: "Evidence Collection",
5792
+ cluster: "product",
5793
+ color: "#06B6D4",
5794
+ summary: "Documentation gathered for SOC2, HIPAA, and ISO 27001 \u2014 what we have, what we still need, and what auditors have already accepted.",
5795
+ sectionTemplates: [
5796
+ { id: "have", title: "What we have", kind: "highlight" },
5797
+ { id: "gaps", title: "Gaps to fill", kind: "action" },
5798
+ { id: "accepted", title: "What auditors accepted", kind: "decision" }
5799
+ ]
5800
+ },
5801
+ {
5802
+ id: "regulator-asks",
5803
+ name: "Regulator Asks",
5804
+ cluster: "research",
5805
+ color: "#F59E0B",
5806
+ summary: "Specific requests from regulators \u2014 owner, deadline, response status. Nothing falls through the cracks.",
5807
+ sectionTemplates: [
5808
+ { id: "open", title: "Open requests", kind: "action" },
5809
+ { id: "responded", title: "Responded", kind: "decision" },
5810
+ { id: "patterns", title: "Recurring patterns", kind: "highlight" }
5811
+ ]
5812
+ },
5813
+ {
5814
+ id: "policy-decisions",
5815
+ name: "Policy Decisions",
5816
+ cluster: "self",
5817
+ color: "#8B5CF6",
5818
+ summary: "Calls made about what the agents are allowed to do \u2014 data handling, escalation triggers, redaction rules \u2014 each anchored on-chain.",
5819
+ sectionTemplates: [
5820
+ { id: "standing", title: "Standing policy", kind: "decision" },
5821
+ { id: "changes", title: "Recent changes", kind: "overview" },
5822
+ { id: "open", title: "Open questions", kind: "question" }
5823
+ ]
5824
+ },
5825
+ {
5826
+ id: "soc2-status",
5827
+ name: "SOC2 Status",
5828
+ cluster: "product",
5829
+ color: "#10B981",
5830
+ summary: "Where we are in the SOC2 audit cycle, what controls are passing, what still needs evidence.",
5831
+ sectionTemplates: [
5832
+ { id: "controls", title: "Control status", kind: "overview" },
5833
+ { id: "next", title: "Up next", kind: "action" }
5834
+ ]
5835
+ }
5836
+ ],
5837
+ rules: [
5838
+ { topicId: "audit-logs", keywords: ["audit", "audit log", "trail", "attribution", "flagged"] },
5839
+ { topicId: "evidence", keywords: ["evidence", "soc2", "hipaa", "iso 27001", "compliance evidence", "control"] },
5840
+ { topicId: "regulator-asks", keywords: ["regulator", "subpoena", "compliance request", "data request"] },
5841
+ { topicId: "policy-decisions", keywords: ["policy", "redaction", "pii", "escalation rule", "data handling"] },
5842
+ { topicId: "soc2-status", keywords: ["soc2", "audit cycle", "control test"] }
5843
+ ]
5844
+ };
5845
+ SALES_PACK = {
5846
+ id: "sales",
5847
+ name: "Sales Intelligence",
5848
+ vendor: "Clude",
5849
+ vertical: "Sales",
5850
+ version: "1.0.0",
5851
+ description: "Auto-organises pipeline conversations, deal blockers, objection patterns, and post-call follow-ups. Built for AEs who hate CRM data entry.",
5852
+ topics: [
5853
+ { id: "pipeline", name: "Pipeline", cluster: "product", color: "#10B981", summary: "Active deals, stages, blockers." },
5854
+ { id: "objections", name: "Objections", cluster: "research", color: "#F59E0B", summary: "Patterns across discovery calls." },
5855
+ { id: "follow-ups", name: "Follow-ups", cluster: "product", color: "#10B981", summary: "What you committed to send and to whom." },
5856
+ { id: "champions", name: "Champions", cluster: "self", color: "#8B5CF6", summary: "Who is championing your product internally at each account." }
5857
+ ],
5858
+ rules: [
5859
+ { topicId: "pipeline", keywords: ["deal", "opportunity", "stage", "close date", "mql", "sql"] },
5860
+ { topicId: "objections", keywords: ["objection", "concern raised", "pushback", "competitor mentioned"] },
5861
+ { topicId: "follow-ups", keywords: ["follow up", "send", "next step", "committed to"] },
5862
+ { topicId: "champions", keywords: ["champion", "sponsor", "advocate", "introduced me to"] }
5863
+ ]
5864
+ };
5865
+ ALL_PACK_MANIFESTS = [
5866
+ WORKSPACE_PACK,
5867
+ COMPLIANCE_PACK,
5868
+ SALES_PACK
5869
+ ];
5870
+ DEFAULT_PACK_ID = "workspace";
5871
+ EMBEDDING_TAG_THRESHOLD = 0.78;
5872
+ }
5873
+ });
5874
+
4298
5875
  // packages/brain/src/experimental/config.ts
4299
5876
  function envBool(key, fallback) {
4300
5877
  const val = process.env[key];
@@ -4391,9 +5968,9 @@ function encryptContent(plaintext) {
4391
5968
  if (!encryptionKey) {
4392
5969
  throw new Error("Encryption not configured. Call configureEncryption() first.");
4393
5970
  }
4394
- const nonce = import_tweetnacl2.default.randomBytes(NONCE_LENGTH);
5971
+ const nonce = import_tweetnacl3.default.randomBytes(NONCE_LENGTH);
4395
5972
  const messageBytes = new TextEncoder().encode(plaintext);
4396
- const ciphertext = import_tweetnacl2.default.secretbox(messageBytes, nonce, encryptionKey);
5973
+ const ciphertext = import_tweetnacl3.default.secretbox(messageBytes, nonce, encryptionKey);
4397
5974
  const combined = new Uint8Array(NONCE_LENGTH + ciphertext.length);
4398
5975
  combined.set(nonce, 0);
4399
5976
  combined.set(ciphertext, NONCE_LENGTH);
@@ -4410,7 +5987,7 @@ function decryptContent(encrypted) {
4410
5987
  }
4411
5988
  const nonce = combined.subarray(0, NONCE_LENGTH);
4412
5989
  const ciphertext = combined.subarray(NONCE_LENGTH);
4413
- const plaintext = import_tweetnacl2.default.secretbox.open(ciphertext, nonce, encryptionKey);
5990
+ const plaintext = import_tweetnacl3.default.secretbox.open(ciphertext, nonce, encryptionKey);
4414
5991
  if (!plaintext) {
4415
5992
  return null;
4416
5993
  }
@@ -4436,17 +6013,17 @@ function decryptMemoryBatch(memories) {
4436
6013
  }
4437
6014
  return memories;
4438
6015
  }
4439
- var import_tweetnacl2, import_crypto, import_util, log9, hkdfAsync, NONCE_LENGTH, encryptionKey, encryptionPubkey;
6016
+ var import_tweetnacl3, import_crypto2, import_util, log9, hkdfAsync, NONCE_LENGTH, encryptionKey, encryptionPubkey;
4440
6017
  var init_encryption = __esm({
4441
6018
  "packages/shared/src/core/encryption.ts"() {
4442
6019
  "use strict";
4443
- import_tweetnacl2 = __toESM(require("tweetnacl"));
4444
- import_crypto = require("crypto");
6020
+ import_tweetnacl3 = __toESM(require("tweetnacl"));
6021
+ import_crypto2 = require("crypto");
4445
6022
  import_util = require("util");
4446
6023
  init_logger();
4447
6024
  log9 = createChildLogger("encryption");
4448
- hkdfAsync = (0, import_util.promisify)(import_crypto.hkdf);
4449
- NONCE_LENGTH = import_tweetnacl2.default.secretbox.nonceLength;
6025
+ hkdfAsync = (0, import_util.promisify)(import_crypto2.hkdf);
6026
+ NONCE_LENGTH = import_tweetnacl3.default.secretbox.nonceLength;
4450
6027
  encryptionKey = null;
4451
6028
  encryptionPubkey = null;
4452
6029
  }
@@ -4736,6 +6313,7 @@ __export(memory_exports, {
4736
6313
  getSelfModel: () => getSelfModel,
4737
6314
  hydrateMemories: () => hydrateMemories,
4738
6315
  inferConcepts: () => inferConcepts,
6316
+ invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
4739
6317
  isValidHashId: () => isValidHashId,
4740
6318
  listMemories: () => listMemories,
4741
6319
  markJepaQueried: () => markJepaQueried,
@@ -4787,7 +6365,7 @@ function scopeToOwner(query) {
4787
6365
  return query;
4788
6366
  }
4789
6367
  function generateHashId() {
4790
- return `${HASH_ID_PREFIX}-${(0, import_crypto2.randomBytes)(4).toString("hex")}`;
6368
+ return `${HASH_ID_PREFIX}-${(0, import_crypto3.randomBytes)(4).toString("hex")}`;
4791
6369
  }
4792
6370
  function isValidHashId(id) {
4793
6371
  return /^clude-[a-f0-9]{8}$/.test(id);
@@ -4838,13 +6416,83 @@ function isDuplicateWrite(source, summary) {
4838
6416
  }
4839
6417
  return false;
4840
6418
  }
6419
+ async function getInstalledPackIdsCached(ownerWallet) {
6420
+ const key = ownerWallet ?? "__no_owner__";
6421
+ const now = Date.now();
6422
+ const cached = installedPacksCache.get(key);
6423
+ if (cached && cached.expiresAt > now) return cached.ids;
6424
+ let ids = [DEFAULT_PACK_ID];
6425
+ if (ownerWallet) {
6426
+ try {
6427
+ const db = getDb();
6428
+ const { data, error } = await db.from("wiki_pack_installations").select("pack_id").eq("owner_wallet", ownerWallet);
6429
+ if (!error && data) {
6430
+ const fromDb = data.map((r) => r.pack_id);
6431
+ ids = Array.from(/* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...fromDb]));
6432
+ }
6433
+ } catch (err) {
6434
+ log11.debug({ err }, "Failed to fetch installed packs (using default only)");
6435
+ }
6436
+ }
6437
+ installedPacksCache.set(key, { ids, expiresAt: now + INSTALLED_PACKS_TTL_MS });
6438
+ if (installedPacksCache.size > 200) {
6439
+ for (const [k, v] of installedPacksCache) {
6440
+ if (v.expiresAt <= now) installedPacksCache.delete(k);
6441
+ }
6442
+ }
6443
+ return ids;
6444
+ }
6445
+ function invalidateInstalledPacksCache(ownerWallet) {
6446
+ if (ownerWallet) installedPacksCache.delete(ownerWallet);
6447
+ else installedPacksCache.clear();
6448
+ }
6449
+ async function ensureTopicEmbeddings(installedPackIds) {
6450
+ const sources = topicEmbedSources(installedPackIds);
6451
+ const missing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
6452
+ if (missing.length === 0) {
6453
+ return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
6454
+ }
6455
+ if (topicEmbeddingPopulationLock) {
6456
+ await topicEmbeddingPopulationLock;
6457
+ }
6458
+ const stillMissing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
6459
+ if (stillMissing.length === 0) {
6460
+ return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
6461
+ }
6462
+ topicEmbeddingPopulationLock = (async () => {
6463
+ try {
6464
+ const texts = stillMissing.map((s) => s.text);
6465
+ const embeddings = await generateEmbeddings(texts);
6466
+ stillMissing.forEach((s, i) => {
6467
+ const emb = embeddings[i];
6468
+ if (emb) topicEmbeddingCache.set(s.topicId, emb);
6469
+ });
6470
+ } catch (err) {
6471
+ log11.warn({ err }, "Failed to populate topic embeddings cache");
6472
+ } finally {
6473
+ topicEmbeddingPopulationLock = null;
6474
+ }
6475
+ })();
6476
+ await topicEmbeddingPopulationLock;
6477
+ return new Map(
6478
+ sources.filter((s) => topicEmbeddingCache.has(s.topicId)).map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)])
6479
+ );
6480
+ }
4841
6481
  async function storeMemory(opts) {
4842
6482
  if (opts.source.startsWith("shiro_") && isDuplicateWrite(opts.source, opts.summary)) {
4843
6483
  log11.debug({ source: opts.source, summary: opts.summary.slice(0, 60) }, "Skipping duplicate memory write");
4844
6484
  return null;
4845
6485
  }
4846
6486
  const db = getDb();
6487
+ const ownerWallet = getOwnerWallet();
4847
6488
  const concepts = opts.concepts || inferConcepts(opts.summary, opts.source, opts.tags || []);
6489
+ const installedPackIds = await getInstalledPackIdsCached(ownerWallet);
6490
+ const taggedTags = autoCategorizeTags({
6491
+ content: opts.content,
6492
+ summary: opts.summary,
6493
+ existingTags: opts.tags || [],
6494
+ installedPackIds
6495
+ });
4848
6496
  const hashId = generateHashId();
4849
6497
  try {
4850
6498
  const plaintextContent = opts.content.slice(0, MEMORY_MAX_CONTENT_LENGTH);
@@ -4855,7 +6503,7 @@ async function storeMemory(opts) {
4855
6503
  memory_type: opts.type,
4856
6504
  content: storedContent,
4857
6505
  summary: opts.summary.slice(0, MEMORY_MAX_SUMMARY_LENGTH),
4858
- tags: opts.tags || [],
6506
+ tags: taggedTags,
4859
6507
  concepts,
4860
6508
  emotional_valence: clamp(opts.emotionalValence ?? 0, -1, 1),
4861
6509
  importance: clamp(opts.importance ?? 0.5, 0, 1),
@@ -4868,8 +6516,8 @@ async function storeMemory(opts) {
4868
6516
  compacted: false,
4869
6517
  encrypted: shouldEncrypt,
4870
6518
  encryption_pubkey: shouldEncrypt ? getEncryptionPubkey() : null,
4871
- owner_wallet: getOwnerWallet() || null
4872
- }).select("id, hash_id").single();
6519
+ owner_wallet: ownerWallet || null
6520
+ }).select("id, hash_id, content, memory_type, owner_wallet, created_at, tags, source, related_user, related_wallet").single();
4873
6521
  if (error) {
4874
6522
  log11.error({ error: error.message }, "Failed to store memory");
4875
6523
  return null;
@@ -4887,7 +6535,9 @@ async function storeMemory(opts) {
4887
6535
  memoryType: opts.type,
4888
6536
  source: opts.source
4889
6537
  });
4890
- commitMemoryToChain(data.id, opts).catch((err) => log11.warn({ err }, "On-chain memory commit failed"));
6538
+ commitMemoryToChain(data.id, opts, data).catch(
6539
+ (err) => log11.warn({ err }, "On-chain memory commit failed")
6540
+ );
4891
6541
  embedMemory(data.id, opts).catch((err) => log11.warn({ err }, "Embedding generation failed"));
4892
6542
  autoLinkMemory(data.id, opts).catch((err) => log11.warn({ err }, "Auto-linking failed"));
4893
6543
  extractAndLinkEntitiesForMemory(data.id, opts).catch((err) => log11.debug({ err }, "Entity extraction failed"));
@@ -4897,9 +6547,19 @@ async function storeMemory(opts) {
4897
6547
  return null;
4898
6548
  }
4899
6549
  }
4900
- async function commitMemoryToChain(memoryId, opts) {
4901
- if (opts.source === "demo" || opts.source === "demo-maas" || opts.source === "locomo-benchmark" || opts.source === "longmemeval-benchmark") return;
4902
- const contentHashBuf = (0, import_crypto2.createHash)("sha256").update(opts.content).digest();
6550
+ async function commitMemoryToChain(memoryId, opts, row) {
6551
+ if (TOKENISATION_SKIP_SOURCES.has(opts.source)) return;
6552
+ const canonicalHash = memoryContentHash({
6553
+ content: row.content,
6554
+ memory_type: row.memory_type,
6555
+ owner_wallet: row.owner_wallet,
6556
+ created_at: row.created_at,
6557
+ tags: row.tags ?? [],
6558
+ source: row.source,
6559
+ related_user: row.related_user,
6560
+ related_wallet: row.related_wallet
6561
+ });
6562
+ const contentHashBuf = Buffer.from(canonicalHash, "hex");
4903
6563
  let signature = null;
4904
6564
  if (isRegistryEnabled()) {
4905
6565
  const encrypted = isEncryptionEnabled();
@@ -4912,22 +6572,61 @@ async function commitMemoryToChain(memoryId, opts) {
4912
6572
  );
4913
6573
  }
4914
6574
  if (!signature) {
4915
- const contentHashHex = contentHashBuf.toString("hex");
4916
- const memo = `clude-memory | v2 | ${contentHashHex}`;
6575
+ const memo = `clude:v1:sha256:${canonicalHash}`;
4917
6576
  signature = await writeMemo(memo);
4918
6577
  }
4919
- if (!signature) return;
4920
6578
  const db = getDb();
4921
- await db.from("memories").update({ solana_signature: signature }).eq("id", memoryId);
4922
- log11.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain");
6579
+ if (!signature) {
6580
+ await db.from("memories").update({ content_hash: canonicalHash, tokenization_status: "failed" }).eq("id", memoryId);
6581
+ return;
6582
+ }
6583
+ await db.from("memories").update({
6584
+ solana_signature: signature,
6585
+ content_hash: canonicalHash,
6586
+ cnft_address: signature,
6587
+ // PDA-based v0.1 — assetId = tx sig; LightMintClient (v0.2) will use real cNFT mints
6588
+ cnft_tx_sig: signature,
6589
+ cnft_tree: null,
6590
+ cnft_leaf_index: null,
6591
+ tokenization_status: "minted",
6592
+ tokenized_at: (/* @__PURE__ */ new Date()).toISOString()
6593
+ }).eq("id", memoryId);
6594
+ log11.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain (PMP + legacy)");
4923
6595
  }
4924
6596
  async function embedMemory(memoryId, opts) {
4925
6597
  if (!isEmbeddingEnabled()) return;
4926
6598
  const db = getDb();
4927
6599
  const embeddings = await generateEmbeddings([opts.summary]);
4928
6600
  const summaryEmbedding = embeddings[0];
4929
- if (summaryEmbedding) {
4930
- await db.from("memories").update({ embedding: JSON.stringify(summaryEmbedding) }).eq("id", memoryId);
6601
+ if (!summaryEmbedding) {
6602
+ log11.debug({ memoryId }, "No embedding generated; skipping semantic tagging");
6603
+ return;
6604
+ }
6605
+ await db.from("memories").update({ embedding: JSON.stringify(summaryEmbedding) }).eq("id", memoryId);
6606
+ try {
6607
+ const ownerWallet = getOwnerWallet();
6608
+ const installedPacks = await getInstalledPackIdsCached(ownerWallet);
6609
+ const topicEmbeddings = await ensureTopicEmbeddings(installedPacks);
6610
+ if (topicEmbeddings.size > 0) {
6611
+ const semantic = semanticTagMatches(
6612
+ summaryEmbedding,
6613
+ Array.from(topicEmbeddings, ([topicId, embedding]) => ({ topicId, embedding }))
6614
+ );
6615
+ if (semantic.length > 0) {
6616
+ const { data: row } = await db.from("memories").select("tags").eq("id", memoryId).maybeSingle();
6617
+ const existing = row?.tags ?? [];
6618
+ const merged = Array.from(/* @__PURE__ */ new Set([...existing, ...semantic]));
6619
+ if (merged.length !== existing.length) {
6620
+ await db.from("memories").update({ tags: merged }).eq("id", memoryId);
6621
+ log11.debug({
6622
+ memoryId,
6623
+ added: semantic.filter((t) => !existing.includes(t))
6624
+ }, "Semantic pack tags appended");
6625
+ }
6626
+ }
6627
+ }
6628
+ } catch (err) {
6629
+ log11.debug({ err, memoryId }, "Semantic tagging skipped");
4931
6630
  }
4932
6631
  log11.debug({ memoryId }, "Memory embedded");
4933
6632
  }
@@ -5130,9 +6829,9 @@ async function recallMemories(opts) {
5130
6829
  }
5131
6830
  if (candidates.length === 0) return [];
5132
6831
  const linkPathMap = /* @__PURE__ */ new Map();
5133
- const addLinkPath = (id, path8) => {
6832
+ const addLinkPath = (id, path9) => {
5134
6833
  if (!linkPathMap.has(id)) linkPathMap.set(id, /* @__PURE__ */ new Set());
5135
- linkPathMap.get(id).add(path8);
6834
+ linkPathMap.get(id).add(path9);
5136
6835
  };
5137
6836
  for (const id of vectorScores.keys()) addLinkPath(id, "vector");
5138
6837
  for (const id of bm25Scores.keys()) addLinkPath(id, "bm25");
@@ -5649,25 +7348,31 @@ async function getMemoryStats() {
5649
7348
  embeddedCount: 0
5650
7349
  };
5651
7350
  try {
5652
- let countQuery = db.from("memories").select("id", { count: "exact", head: true }).gt("decay_factor", MEMORY_MIN_DECAY);
7351
+ let countQuery = db.from("memories").select("id", { count: "exact", head: true });
5653
7352
  countQuery = scopeToOwner(countQuery);
5654
7353
  const { count: totalCount } = await countQuery;
5655
7354
  stats.total = totalCount || 0;
5656
- let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).gt("decay_factor", MEMORY_MIN_DECAY).not("embedding", "is", null);
7355
+ let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).not("embedding", "is", null);
5657
7356
  embeddedQuery = scopeToOwner(embeddedQuery);
5658
7357
  const { count: embCount } = await embeddedQuery;
5659
7358
  stats.embeddedCount = embCount || 0;
5660
- const PAGE_SIZE = 5e3;
7359
+ const TYPES = ["episodic", "semantic", "procedural", "self_model", "introspective"];
7360
+ await Promise.all(TYPES.map(async (type) => {
7361
+ let q = db.from("memories").select("id", { count: "exact", head: true }).eq("memory_type", type);
7362
+ q = scopeToOwner(q);
7363
+ const { count: count2 } = await q;
7364
+ stats.byType[type] = count2 || 0;
7365
+ }));
7366
+ const PAGE_SIZE = 1e3;
7367
+ const MAX_PAGES = 20;
5661
7368
  let allMemories = [];
5662
- let page = 0;
5663
- while (true) {
5664
- 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);
7369
+ for (let page = 0; page < MAX_PAGES; page++) {
7370
+ 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);
5665
7371
  pageQuery = scopeToOwner(pageQuery);
5666
7372
  const { data: pageData } = await pageQuery;
5667
7373
  if (!pageData || pageData.length === 0) break;
5668
7374
  allMemories = allMemories.concat(pageData);
5669
7375
  if (pageData.length < PAGE_SIZE) break;
5670
- page++;
5671
7376
  }
5672
7377
  if (allMemories.length > 0) {
5673
7378
  let impSum = 0;
@@ -5676,8 +7381,6 @@ async function getMemoryStats() {
5676
7381
  const conceptCounts = {};
5677
7382
  const users = /* @__PURE__ */ new Set();
5678
7383
  for (const m of allMemories) {
5679
- const type = m.memory_type;
5680
- if (type in stats.byType) stats.byType[type]++;
5681
7384
  impSum += m.importance;
5682
7385
  decaySum += m.decay_factor;
5683
7386
  if (m.related_user) users.add(m.related_user);
@@ -5865,7 +7568,7 @@ function moodToValence(mood) {
5865
7568
  return 0;
5866
7569
  }
5867
7570
  }
5868
- var import_crypto2, EMBED_CACHE_MAX, embeddingCache, _ownerWallet, SCOPE_BOT_OWN, HASH_ID_PREFIX, log11, DEDUP_TTL_MS, dedupCache, STOPWORDS;
7571
+ var import_crypto3, EMBED_CACHE_MAX, embeddingCache, _ownerWallet, SCOPE_BOT_OWN, HASH_ID_PREFIX, log11, DEDUP_TTL_MS, dedupCache, installedPacksCache, INSTALLED_PACKS_TTL_MS, topicEmbeddingCache, topicEmbeddingPopulationLock, TOKENISATION_SKIP_SOURCES, STOPWORDS;
5869
7572
  var init_memory = __esm({
5870
7573
  "packages/brain/src/memory/memory.ts"() {
5871
7574
  "use strict";
@@ -5874,13 +7577,15 @@ var init_memory = __esm({
5874
7577
  init_utils();
5875
7578
  init_claude_client();
5876
7579
  init_solana_client();
7580
+ init_src2();
5877
7581
  init_embeddings();
7582
+ init_wiki_packs();
5878
7583
  init_config2();
5879
7584
  init_bm25_search();
5880
7585
  init_openrouter_client();
5881
7586
  init_encryption();
5882
7587
  init_event_bus();
5883
- import_crypto2 = require("crypto");
7588
+ import_crypto3 = require("crypto");
5884
7589
  init_graph();
5885
7590
  init_owner_context();
5886
7591
  EMBED_CACHE_MAX = 200;
@@ -5891,6 +7596,16 @@ var init_memory = __esm({
5891
7596
  log11 = createChildLogger("memory");
5892
7597
  DEDUP_TTL_MS = 10 * 60 * 1e3;
5893
7598
  dedupCache = /* @__PURE__ */ new Map();
7599
+ installedPacksCache = /* @__PURE__ */ new Map();
7600
+ INSTALLED_PACKS_TTL_MS = 6e4;
7601
+ topicEmbeddingCache = /* @__PURE__ */ new Map();
7602
+ topicEmbeddingPopulationLock = null;
7603
+ TOKENISATION_SKIP_SOURCES = /* @__PURE__ */ new Set([
7604
+ "demo",
7605
+ "demo-maas",
7606
+ "locomo-benchmark",
7607
+ "longmemeval-benchmark"
7608
+ ]);
5894
7609
  STOPWORDS = /* @__PURE__ */ new Set([
5895
7610
  "the",
5896
7611
  "a",
@@ -5996,6 +7711,7 @@ __export(memory_exports2, {
5996
7711
  getSelfModel: () => getSelfModel,
5997
7712
  hydrateMemories: () => hydrateMemories,
5998
7713
  inferConcepts: () => inferConcepts,
7714
+ invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
5999
7715
  isValidHashId: () => isValidHashId,
6000
7716
  listMemories: () => listMemories,
6001
7717
  moodToValence: () => moodToValence,
@@ -6179,7 +7895,8 @@ async function runExport() {
6179
7895
  -h, --help Show this help
6180
7896
 
6181
7897
  ${c.bold}Formats:${c.reset}
6182
- ${c.cyan}json${c.reset} MemoryPack JSON (default)
7898
+ ${c.cyan}json${c.reset} Legacy single-file JSON (default)
7899
+ ${c.cyan}memorypack${c.reset} MemoryPack v0.1 spec directory (manifest + records.jsonl + sigs)
6183
7900
  ${c.cyan}md${c.reset} Clean Markdown
6184
7901
  ${c.cyan}chatgpt${c.reset} System prompt for ChatGPT Custom Instructions
6185
7902
  ${c.cyan}gemini${c.reset} System prompt for Gemini Gems
@@ -6199,7 +7916,7 @@ async function runExport() {
6199
7916
  const output = getFlag(args, "--output") || getFlag(args, "-o");
6200
7917
  const typesRaw = getFlag(args, "--types");
6201
7918
  const types = typesRaw ? typesRaw.split(",").map((t) => t.trim()) : void 0;
6202
- const validFormats = ["json", "md", "chatgpt", "gemini", "clipboard"];
7919
+ const validFormats = ["json", "md", "chatgpt", "gemini", "clipboard", "memorypack"];
6203
7920
  if (!validFormats.includes(format)) {
6204
7921
  printError(`Format must be one of: ${validFormats.join(", ")}`);
6205
7922
  process.exit(1);
@@ -6346,6 +8063,63 @@ async function runExport() {
6346
8063
  } else if (format === "md") {
6347
8064
  content = formatMarkdown(memories);
6348
8065
  defaultName = "clude-memories.md";
8066
+ } else if (format === "memorypack") {
8067
+ const outputDir = output || "clude-memorypack";
8068
+ const records = memories.map((m, i) => {
8069
+ const id = m.id ? String(m.id) : String(i + 1);
8070
+ const kind = m.type || m.memory_type || "episodic";
8071
+ const rec = {
8072
+ id,
8073
+ created_at: m.created_at || (/* @__PURE__ */ new Date()).toISOString(),
8074
+ kind,
8075
+ content: m.content || "",
8076
+ tags: m.tags || [],
8077
+ importance: typeof m.importance === "number" ? m.importance : 0.5,
8078
+ source: m.source || ""
8079
+ };
8080
+ if (m.summary) rec.summary = m.summary;
8081
+ if (m.access_count != null) rec.access_count = m.access_count;
8082
+ if (m.last_accessed_at) rec.last_accessed_at = m.last_accessed_at;
8083
+ return rec;
8084
+ });
8085
+ let secretKey;
8086
+ let publicKey;
8087
+ try {
8088
+ const { getBotWallet: getBotWallet2 } = (init_solana_client(), __toCommonJS(solana_client_exports));
8089
+ const wallet = getBotWallet2();
8090
+ if (wallet) {
8091
+ secretKey = wallet.secretKey;
8092
+ publicKey = wallet.publicKey.toBase58();
8093
+ }
8094
+ } catch {
8095
+ }
8096
+ writeMemoryPack(outputDir, records, {
8097
+ producer: {
8098
+ name: "clude",
8099
+ version: "3.1.0",
8100
+ agent_id: config3?.agent?.id,
8101
+ public_key: publicKey
8102
+ },
8103
+ record_schema: "clude-memory-v3",
8104
+ secretKey,
8105
+ anchor_chain: "solana-mainnet"
8106
+ });
8107
+ printSuccess(
8108
+ `MemoryPack written to ${outputDir}/ (${records.length} records${secretKey ? ", signed" : ", UNSIGNED"})`
8109
+ );
8110
+ if (!secretKey) {
8111
+ console.log(
8112
+ `
8113
+ ${c.dim}No wallet configured \u2014 pack is unsigned. Set CLUDE_WALLET_SECRET to sign.${c.reset}`
8114
+ );
8115
+ }
8116
+ console.log(
8117
+ `
8118
+ ${c.dim}Contents: manifest.json, records.jsonl${secretKey ? ", signatures.jsonl" : ""}${c.reset}`
8119
+ );
8120
+ printDivider();
8121
+ console.log("");
8122
+ return;
6349
8123
  } else {
6350
8124
  defaultName = "clude-memories.json";
6351
8125
  const pack = {
@@ -6374,7 +8148,7 @@ async function runExport() {
6374
8148
  content = JSON.stringify(pack, null, 2);
6375
8149
  }
6376
8150
  const filename = output || defaultName;
6377
- (0, import_fs3.writeFileSync)(filename, content, "utf-8");
8151
+ (0, import_fs5.writeFileSync)(filename, content, "utf-8");
6378
8152
  printSuccess(`Written to ${filename} (${formatBytes(Buffer.byteLength(content))})`);
6379
8153
  if (format === "chatgpt") {
6380
8154
  console.log(`
@@ -6389,12 +8163,13 @@ async function runExport() {
6389
8163
  printDivider();
6390
8164
  console.log("");
6391
8165
  }
6392
- var import_fs3;
8166
+ var import_fs5;
6393
8167
  var init_export = __esm({
6394
8168
  "packages/brain/src/cli/export.ts"() {
6395
8169
  "use strict";
6396
- import_fs3 = require("fs");
8170
+ import_fs5 = require("fs");
6397
8171
  init_banner();
8172
+ init_src();
6398
8173
  }
6399
8174
  });
6400
8175
 
@@ -6406,19 +8181,45 @@ __export(import_exports, {
6406
8181
  function hasFlag2(args, flag) {
6407
8182
  return args.includes(flag);
6408
8183
  }
6409
- function isZipFile(path8) {
8184
+ function isMemoryPackDir(path9) {
8185
+ try {
8186
+ return (0, import_fs6.statSync)(path9).isDirectory() && (0, import_fs6.existsSync)((0, import_path6.join)(path9, "manifest.json"));
8187
+ } catch {
8188
+ return false;
8189
+ }
8190
+ }
8191
+ function parseMemoryPackDir(dir) {
8192
+ const result = readMemoryPack(dir);
8193
+ const signedCount = result.verifiedRecords.size;
8194
+ if (signedCount > 0) {
8195
+ const pubFp = result.manifest.producer.public_key?.slice(0, 8) ?? "?";
8196
+ printInfo(`Verified ${signedCount} record signature(s) against ${pubFp}...`);
8197
+ } else {
8198
+ printInfo("Pack is unsigned (no signatures.jsonl) \u2014 proceeding without verification");
8199
+ }
8200
+ for (const w of result.warnings) printInfo(`warning: ${w}`);
8201
+ return result.records.map((r) => ({
8202
+ content: r.content,
8203
+ summary: r.summary || r.content.slice(0, 200),
8204
+ type: ["episodic", "semantic", "procedural"].includes(r.kind) ? r.kind : "episodic",
8205
+ importance: r.importance,
8206
+ tags: [...r.tags || [], "imported", "memorypack"],
8207
+ source: r.source || "memorypack"
8208
+ }));
8209
+ }
8210
+ function isZipFile(path9) {
6410
8211
  try {
6411
- const buf = (0, import_fs4.readFileSync)(path8);
8212
+ const buf = (0, import_fs6.readFileSync)(path9);
6412
8213
  return buf[0] === 80 && buf[1] === 75;
6413
8214
  } catch {
6414
8215
  return false;
6415
8216
  }
6416
8217
  }
6417
- function isJsonFile(path8) {
6418
- return path8.endsWith(".json");
8218
+ function isJsonFile(path9) {
8219
+ return path9.endsWith(".json");
6419
8220
  }
6420
- function isMarkdownOrText(path8) {
6421
- return path8.endsWith(".md") || path8.endsWith(".txt") || path8.endsWith(".markdown");
8221
+ function isMarkdownOrText(path9) {
8222
+ return path9.endsWith(".md") || path9.endsWith(".txt") || path9.endsWith(".markdown");
6422
8223
  }
6423
8224
  function isSubstantive(text) {
6424
8225
  if (!text || text.length < 20) return false;
@@ -6535,18 +8336,18 @@ function extractFromChatGPTConversations(conversations) {
6535
8336
  }
6536
8337
  async function parseChatGPTZip(filePath) {
6537
8338
  const { execSync: execSync2 } = require("child_process");
6538
- const { mkdtempSync, readdirSync: readdirSync2 } = require("fs");
6539
- const { join: join8 } = require("path");
6540
- const os5 = require("os");
6541
- const tmpDir = mkdtempSync(join8(os5.tmpdir(), "clude-import-"));
8339
+ const { mkdtempSync: mkdtempSync3, readdirSync: readdirSync3 } = require("fs");
8340
+ const { join: join13 } = require("path");
8341
+ const os6 = require("os");
8342
+ const tmpDir = mkdtempSync3(join13(os6.tmpdir(), "clude-import-"));
6542
8343
  try {
6543
8344
  execSync2(`unzip -o -q "${filePath}" -d "${tmpDir}"`);
6544
8345
  } catch {
6545
8346
  printError('Failed to unzip file. Make sure "unzip" is installed.');
6546
8347
  process.exit(1);
6547
8348
  }
6548
- const files = readdirSync2(tmpDir);
6549
- const convFile = files.find((f) => f === "conversations.json") ? join8(tmpDir, "conversations.json") : null;
8349
+ const files = readdirSync3(tmpDir);
8350
+ const convFile = files.find((f) => f === "conversations.json") ? join13(tmpDir, "conversations.json") : null;
6550
8351
  if (!convFile) {
6551
8352
  const { execSync: exec2 } = require("child_process");
6552
8353
  try {
@@ -6555,18 +8356,18 @@ async function parseChatGPTZip(filePath) {
6555
8356
  printError("No conversations.json found in ZIP.");
6556
8357
  process.exit(1);
6557
8358
  }
6558
- const conversations2 = JSON.parse((0, import_fs4.readFileSync)(found.split("\n")[0], "utf-8"));
8359
+ const conversations2 = JSON.parse((0, import_fs6.readFileSync)(found.split("\n")[0], "utf-8"));
6559
8360
  return extractFromChatGPTConversations(conversations2);
6560
8361
  } catch {
6561
8362
  printError("No conversations.json found in ZIP.");
6562
8363
  process.exit(1);
6563
8364
  }
6564
8365
  }
6565
- const conversations = JSON.parse((0, import_fs4.readFileSync)(convFile, "utf-8"));
8366
+ const conversations = JSON.parse((0, import_fs6.readFileSync)(convFile, "utf-8"));
6566
8367
  return extractFromChatGPTConversations(conversations);
6567
8368
  }
6568
8369
  function parseMarkdown(filePath) {
6569
- const content = (0, import_fs4.readFileSync)(filePath, "utf-8");
8370
+ const content = (0, import_fs6.readFileSync)(filePath, "utf-8");
6570
8371
  const memories = [];
6571
8372
  const sections = content.split(/\n#{1,3}\s+|\n\n+/).filter((s) => s.trim().length > 20);
6572
8373
  for (const section of sections) {
@@ -6600,7 +8401,7 @@ function parseMarkdown(filePath) {
6600
8401
  return memories;
6601
8402
  }
6602
8403
  function parseMemoryPack(filePath) {
6603
- const data = JSON.parse((0, import_fs4.readFileSync)(filePath, "utf-8"));
8404
+ const data = JSON.parse((0, import_fs6.readFileSync)(filePath, "utf-8"));
6604
8405
  if (data.memories && Array.isArray(data.memories)) {
6605
8406
  return data.memories.map((m) => ({
6606
8407
  content: m.content || m.summary || "",
@@ -6684,9 +8485,10 @@ async function runImport() {
6684
8485
  ${c.bold}Usage:${c.reset} npx @clude/sdk import <file> [options]
6685
8486
 
6686
8487
  ${c.bold}Supported formats:${c.reset}
8488
+ ${c.cyan}pack/${c.reset} MemoryPack v0.1 directory (manifest.json + records.jsonl)
6687
8489
  ${c.cyan}chatgpt-export.zip${c.reset} ChatGPT data export (ZIP with conversations.json)
6688
8490
  ${c.cyan}memories.md${c.reset} Markdown or text file (paragraphs \u2192 memories)
6689
- ${c.cyan}pack.json${c.reset} Clude MemoryPack JSON
8491
+ ${c.cyan}pack.json${c.reset} Legacy Clude MemoryPack JSON
6690
8492
 
6691
8493
  ${c.bold}Options:${c.reset}
6692
8494
  --dry-run Show what would be imported without storing
@@ -6702,7 +8504,7 @@ async function runImport() {
6702
8504
  }
6703
8505
  const filePath = args.find((a) => !a.startsWith("--"));
6704
8506
  const dryRun = hasFlag2(args, "--dry-run");
6705
- if (!(0, import_fs4.existsSync)(filePath)) {
8507
+ if (!(0, import_fs6.existsSync)(filePath)) {
6706
8508
  printError(`File not found: ${filePath}`);
6707
8509
  process.exit(1);
6708
8510
  }
@@ -6730,11 +8532,14 @@ async function runImport() {
6730
8532
  ${c.bold}Import${c.reset}
6731
8533
  `);
6732
8534
  let memories;
6733
- if (isZipFile(filePath)) {
8535
+ if (isMemoryPackDir(filePath)) {
8536
+ printInfo("Detected: MemoryPack v0.1 directory");
8537
+ memories = parseMemoryPackDir(filePath);
8538
+ } else if (isZipFile(filePath)) {
6734
8539
  printInfo("Detected: ChatGPT data export (ZIP)");
6735
8540
  memories = await parseChatGPTZip(filePath);
6736
8541
  } else if (isJsonFile(filePath)) {
6737
- printInfo("Detected: MemoryPack JSON");
8542
+ printInfo("Detected: Legacy MemoryPack JSON");
6738
8543
  memories = parseMemoryPack(filePath);
6739
8544
  } else if (isMarkdownOrText(filePath)) {
6740
8545
  printInfo("Detected: Markdown/text file");
@@ -6778,12 +8583,14 @@ async function runImport() {
6778
8583
  printDivider();
6779
8584
  console.log("");
6780
8585
  }
6781
- var import_fs4;
8586
+ var import_fs6, import_path6;
6782
8587
  var init_import = __esm({
6783
8588
  "packages/brain/src/cli/import.ts"() {
6784
8589
  "use strict";
6785
- import_fs4 = require("fs");
8590
+ import_fs6 = require("fs");
8591
+ import_path6 = require("path");
6786
8592
  init_banner();
8593
+ init_src();
6787
8594
  }
6788
8595
  });
6789
8596
 
@@ -6895,7 +8702,7 @@ async function runSync() {
6895
8702
  const refresh = async () => {
6896
8703
  try {
6897
8704
  const content = await generatePrompt(format);
6898
- (0, import_fs5.writeFileSync)(output, content, "utf-8");
8705
+ (0, import_fs7.writeFileSync)(output, content, "utf-8");
6899
8706
  printSuccess(`Updated ${output} (${content.split(/\s+/).length} words) \u2014 ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`);
6900
8707
  } catch (err) {
6901
8708
  printError(`Refresh failed: ${err.message}`);
@@ -6911,11 +8718,11 @@ async function runSync() {
6911
8718
  `);
6912
8719
  setInterval(refresh, interval * 1e3);
6913
8720
  }
6914
- var import_fs5;
8721
+ var import_fs7;
6915
8722
  var init_sync = __esm({
6916
8723
  "packages/brain/src/cli/sync.ts"() {
6917
8724
  "use strict";
6918
- import_fs5 = require("fs");
8725
+ import_fs7 = require("fs");
6919
8726
  init_banner();
6920
8727
  }
6921
8728
  });
@@ -6934,13 +8741,13 @@ async function runInjectInstructions() {
6934
8741
  const forceAgents = args.includes("--agents");
6935
8742
  const forceClaude = args.includes("--claude");
6936
8743
  const dirFlag = args.find((a) => a.startsWith("--dir="));
6937
- const dir = dirFlag ? path5.resolve(dirFlag.slice(6)) : process.cwd();
8744
+ const dir = dirFlag ? path6.resolve(dirFlag.slice(6)) : process.cwd();
6938
8745
  let useAgentsmd = forceAgents;
6939
8746
  if (!forceAgents && !forceClaude) {
6940
- useAgentsmd = fs3.existsSync(path5.join(dir, "AGENTS.md")) || fs3.existsSync(path5.join(dir, "agents"));
8747
+ useAgentsmd = fs4.existsSync(path6.join(dir, "AGENTS.md")) || fs4.existsSync(path6.join(dir, "agents"));
6941
8748
  }
6942
8749
  const targetFile = useAgentsmd ? "AGENTS.md" : "CLAUDE.md";
6943
- printInfo(`Target: ${path5.join(dir, targetFile)}`);
8750
+ printInfo(`Target: ${path6.join(dir, targetFile)}`);
6944
8751
  console.log("");
6945
8752
  const result = injectInstructions(dir, { agentsmd: useAgentsmd });
6946
8753
  if (result) {
@@ -6959,12 +8766,12 @@ async function runInjectInstructions() {
6959
8766
  printDivider();
6960
8767
  console.log("");
6961
8768
  }
6962
- var path5, fs3;
8769
+ var path6, fs4;
6963
8770
  var init_inject_instructions = __esm({
6964
8771
  "packages/brain/src/cli/inject-instructions.ts"() {
6965
8772
  "use strict";
6966
- path5 = __toESM(require("path"));
6967
- fs3 = __toESM(require("fs"));
8773
+ path6 = __toESM(require("path"));
8774
+ fs4 = __toESM(require("fs"));
6968
8775
  init_banner();
6969
8776
  init_setup();
6970
8777
  }
@@ -6977,13 +8784,13 @@ __export(doctor_exports, {
6977
8784
  });
6978
8785
  async function runDoctor() {
6979
8786
  console.log("\n CLUDE DIAGNOSTICS\n");
6980
- const dbPath = path6.join(os3.homedir(), ".clude", "brain.db");
6981
- if (fs4.existsSync(dbPath)) {
8787
+ const dbPath = path7.join(os4.homedir(), ".clude", "brain.db");
8788
+ if (fs5.existsSync(dbPath)) {
6982
8789
  try {
6983
8790
  const Database2 = require("better-sqlite3");
6984
8791
  const db = new Database2(dbPath, { readonly: true });
6985
8792
  const count = db.prepare("SELECT COUNT(*) as c FROM memories").get().c;
6986
- const stats = fs4.statSync(dbPath);
8793
+ const stats = fs5.statSync(dbPath);
6987
8794
  const sizeMB = (stats.size / 1024 / 1024).toFixed(1);
6988
8795
  console.log(` \u2713 Database ${dbPath} (${count} memories, ${sizeMB} MB)`);
6989
8796
  db.close();
@@ -6993,15 +8800,15 @@ async function runDoctor() {
6993
8800
  } else {
6994
8801
  console.log(` \u2717 Database Not found (run 'npx @clude/sdk setup')`);
6995
8802
  }
6996
- const modelDir = path6.join(os3.homedir(), ".clude", "models");
6997
- const modelCached = fs4.existsSync(modelDir) && fs4.readdirSync(modelDir).length > 0;
8803
+ const modelDir = path7.join(os4.homedir(), ".clude", "models");
8804
+ const modelCached = fs5.existsSync(modelDir) && fs5.readdirSync(modelDir).length > 0;
6998
8805
  console.log(
6999
8806
  modelCached ? " \u2713 Embeddings all-MiniLM-L6-v2 (cached)" : " \u2717 Embeddings Model not cached (will download on first use)"
7000
8807
  );
7001
- const configPath = path6.join(os3.homedir(), ".clude", "config.json");
7002
- if (fs4.existsSync(configPath)) {
8808
+ const configPath = path7.join(os4.homedir(), ".clude", "config.json");
8809
+ if (fs5.existsSync(configPath)) {
7003
8810
  try {
7004
- const config3 = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
8811
+ const config3 = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
7005
8812
  console.log(
7006
8813
  config3.apiKey ? ` \u2713 Cloud ${config3.apiKey.slice(0, 8)}... (configured)` : " \u2717 Cloud No API key configured"
7007
8814
  );
@@ -7024,13 +8831,13 @@ async function runDoctor() {
7024
8831
  console.log(" \u2717 Ollama Not running");
7025
8832
  }
7026
8833
  const mcpPaths = [
7027
- { name: "Claude Code", path: path6.join(os3.homedir(), ".claude", ".mcp.json") },
7028
- { name: "Cursor", path: path6.join(os3.homedir(), ".cursor", "mcp.json") }
8834
+ { name: "Claude Code", path: path7.join(os4.homedir(), ".claude", ".mcp.json") },
8835
+ { name: "Cursor", path: path7.join(os4.homedir(), ".cursor", "mcp.json") }
7029
8836
  ];
7030
8837
  for (const mcp of mcpPaths) {
7031
- if (fs4.existsSync(mcp.path)) {
8838
+ if (fs5.existsSync(mcp.path)) {
7032
8839
  try {
7033
- const config3 = JSON.parse(fs4.readFileSync(mcp.path, "utf-8"));
8840
+ const config3 = JSON.parse(fs5.readFileSync(mcp.path, "utf-8"));
7034
8841
  const hasClude = config3.mcpServers?.["clude-memory"] !== void 0;
7035
8842
  console.log(
7036
8843
  hasClude ? ` \u2713 MCP ${mcp.name}` : ` - MCP ${mcp.name} (no clude-memory entry)`
@@ -7042,7 +8849,7 @@ async function runDoctor() {
7042
8849
  console.log(` \u2717 MCP ${mcp.name} (not detected)`);
7043
8850
  }
7044
8851
  }
7045
- if (fs4.existsSync(dbPath)) {
8852
+ if (fs5.existsSync(dbPath)) {
7046
8853
  try {
7047
8854
  const Database2 = require("better-sqlite3");
7048
8855
  const db = new Database2(dbPath, { readonly: true });
@@ -7054,13 +8861,13 @@ async function runDoctor() {
7054
8861
  }
7055
8862
  console.log("");
7056
8863
  }
7057
- var path6, os3, fs4;
8864
+ var path7, os4, fs5;
7058
8865
  var init_doctor = __esm({
7059
8866
  "packages/brain/src/cli/doctor.ts"() {
7060
8867
  "use strict";
7061
- path6 = __toESM(require("path"));
7062
- os3 = __toESM(require("os"));
7063
- fs4 = __toESM(require("fs"));
8868
+ path7 = __toESM(require("path"));
8869
+ os4 = __toESM(require("os"));
8870
+ fs5 = __toESM(require("fs"));
7064
8871
  }
7065
8872
  });
7066
8873
 
@@ -7330,8 +9137,8 @@ __export(dream_exports, {
7330
9137
  });
7331
9138
  async function runDream() {
7332
9139
  console.log("\n DREAM CYCLE\n");
7333
- const dbPath = path7.join(os4.homedir(), ".clude", "brain.db");
7334
- if (!fs5.existsSync(dbPath)) {
9140
+ const dbPath = path8.join(os5.homedir(), ".clude", "brain.db");
9141
+ if (!fs6.existsSync(dbPath)) {
7335
9142
  console.log(' \u2717 No database found. Run "npx @clude/sdk setup" first.\n');
7336
9143
  return;
7337
9144
  }
@@ -7361,13 +9168,300 @@ async function runDream() {
7361
9168
  store.close();
7362
9169
  console.log("\n Dream cycle complete.\n");
7363
9170
  }
7364
- var path7, os4, fs5;
9171
+ var path8, os5, fs6;
7365
9172
  var init_dream = __esm({
7366
9173
  "packages/brain/src/cli/dream.ts"() {
7367
9174
  "use strict";
7368
- path7 = __toESM(require("path"));
7369
- os4 = __toESM(require("os"));
7370
- fs5 = __toESM(require("fs"));
9175
+ path8 = __toESM(require("path"));
9176
+ os5 = __toESM(require("os"));
9177
+ fs6 = __toESM(require("fs"));
9178
+ }
9179
+ });
9180
+
9181
+ // packages/brain/src/cli/verify.ts
9182
+ var verify_exports = {};
9183
+ __export(verify_exports, {
9184
+ runVerify: () => runVerify
9185
+ });
9186
+ function parseArgs() {
9187
+ const argv = process.argv.slice(3);
9188
+ const args = {
9189
+ path: "",
9190
+ strictSignatures: false,
9191
+ verifyChain: false,
9192
+ strictChain: false,
9193
+ rpcUrl: process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com",
9194
+ showHelp: false
9195
+ };
9196
+ for (let i = 0; i < argv.length; i++) {
9197
+ const a = argv[i];
9198
+ if (a === "--help" || a === "-h") args.showHelp = true;
9199
+ else if (a === "--public-key") args.publicKey = argv[++i];
9200
+ else if (a === "--strict-signatures") args.strictSignatures = true;
9201
+ else if (a === "--strict-chain") args.strictChain = true;
9202
+ else if (a === "--verify-chain") args.verifyChain = true;
9203
+ else if (a === "--rpc-url") args.rpcUrl = argv[++i];
9204
+ else if (a === "--cluster") args.cluster = argv[++i];
9205
+ else if (a === "--decrypt-key") args.decryptKey = argv[++i];
9206
+ else if (!a.startsWith("--") && !args.path) args.path = a;
9207
+ }
9208
+ return args;
9209
+ }
9210
+ function printUsage() {
9211
+ console.log(`Usage: clude verify <pack> [options]
9212
+
9213
+ Validates a MemoryPack directory or .tar.zst tarball.
9214
+
9215
+ Options:
9216
+ --public-key <base58> Override the public key (default: from manifest)
9217
+ --strict-signatures Fail if any record is unsigned
9218
+ --verify-chain Also verify Solana on-chain anchors
9219
+ --strict-chain Fail on any chain verification mismatch
9220
+ --rpc-url <url> Solana RPC URL for chain verification
9221
+ (default: SOLANA_RPC_URL env or mainnet-beta)
9222
+ --cluster <name> Cross-check RPC genesis hash against this
9223
+ cluster (mainnet | devnet | testnet)
9224
+ --decrypt-key <base64> Pack-level decryption key (32 bytes, base64)
9225
+
9226
+ Exit code: 0 if valid, 1 if any check failed.`);
9227
+ }
9228
+ async function runVerify() {
9229
+ const args = parseArgs();
9230
+ if (args.showHelp || !args.path) {
9231
+ printUsage();
9232
+ process.exit(args.showHelp ? 0 : 1);
9233
+ }
9234
+ console.log(`${c.bold}MemoryPack verify${c.reset} ${c.dim}${args.path}${c.reset}
9235
+ `);
9236
+ let decryptionKey;
9237
+ if (args.decryptKey) {
9238
+ const buf = Buffer.from(args.decryptKey, "base64");
9239
+ decryptionKey = new Uint8Array(buf);
9240
+ }
9241
+ let result;
9242
+ try {
9243
+ result = readMemoryPack(args.path, {
9244
+ publicKey: args.publicKey,
9245
+ strictSignatures: args.strictSignatures,
9246
+ decryptionKey
9247
+ });
9248
+ } catch (e) {
9249
+ console.error(`${c.red}REJECTED${c.reset}: ${e.message}`);
9250
+ process.exit(1);
9251
+ }
9252
+ const {
9253
+ manifest,
9254
+ records,
9255
+ verifiedRecords,
9256
+ unsignedRecords,
9257
+ anchors,
9258
+ verifiedBlobs,
9259
+ minimalRecords,
9260
+ warnings
9261
+ } = result;
9262
+ console.log(` ${c.dim}producer:${c.reset} ${manifest.producer.name} ${manifest.producer.version}`);
9263
+ if (manifest.producer.did) console.log(` ${c.dim}did:${c.reset} ${manifest.producer.did}`);
9264
+ if (manifest.producer.public_key) console.log(` ${c.dim}public_key:${c.reset} ${manifest.producer.public_key}`);
9265
+ console.log(` ${c.dim}created_at:${c.reset} ${manifest.created_at}`);
9266
+ console.log(` ${c.dim}records:${c.reset} ${records.length}`);
9267
+ console.log(` ${c.dim}schema:${c.reset} ${manifest.record_schema}`);
9268
+ console.log(` ${c.dim}encryption:${c.reset} ${manifest.encryption ? `${manifest.encryption.algorithm} (scope=${manifest.encryption.scope})` : "none"}`);
9269
+ console.log(` ${c.dim}pack_format:${c.reset} ${manifest.pack_format ?? "directory"}`);
9270
+ console.log();
9271
+ console.log(` ${c.bold}Signatures${c.reset}`);
9272
+ console.log(` verified: ${c.green}${verifiedRecords.size}${c.reset}`);
9273
+ console.log(` unsigned: ${unsignedRecords.size}`);
9274
+ console.log();
9275
+ if (verifiedBlobs.size > 0 || manifest.blobs_count) {
9276
+ console.log(` ${c.bold}Blobs${c.reset}`);
9277
+ const declared = manifest.blobs_count ?? "?";
9278
+ console.log(` verified: ${c.green}${verifiedBlobs.size}${c.reset} of ${declared}`);
9279
+ console.log();
9280
+ }
9281
+ console.log(` ${c.bold}Anchors${c.reset}`);
9282
+ console.log(` declared: ${anchors.length}`);
9283
+ let chainFailed = false;
9284
+ if (args.verifyChain && anchors.length > 0) {
9285
+ process.stdout.write(` fetching: `);
9286
+ try {
9287
+ const expectedSigner = args.publicKey ?? manifest.producer.public_key;
9288
+ const { verified, warnings: chainWarnings } = await verifyChainAnchors(anchors, {
9289
+ rpcUrl: args.rpcUrl,
9290
+ cluster: args.cluster,
9291
+ expectedSigner,
9292
+ strict: args.strictChain
9293
+ });
9294
+ const ok = verified.size;
9295
+ console.log(`${c.green}${ok}${c.reset}/${anchors.length} verified on-chain`);
9296
+ if (chainWarnings.length > 0) {
9297
+ for (const w of chainWarnings) console.log(` ${c.yellow}warn:${c.reset} ${w}`);
9298
+ }
9299
+ if (ok < anchors.length) chainFailed = true;
9300
+ } catch (e) {
9301
+ console.log(`${c.red}FAILED${c.reset}`);
9302
+ console.error(` ${c.red}${e.message}${c.reset}`);
9303
+ chainFailed = true;
9304
+ }
9305
+ }
9306
+ console.log();
9307
+ if (manifest.encryption) {
9308
+ console.log(` ${c.bold}Encryption${c.reset}`);
9309
+ if (decryptionKey) {
9310
+ const decrypted = records.filter((r) => !r.encrypted).length;
9311
+ console.log(` decrypted: ${c.green}${decrypted}${c.reset} of ${records.length}`);
9312
+ } else {
9313
+ console.log(` ${c.yellow}no --decrypt-key supplied; ${records.length - minimalRecords.length} record(s) excluded from minimalRecords${c.reset}`);
9314
+ }
9315
+ console.log();
9316
+ }
9317
+ if (warnings.length > 0) {
9318
+ console.log(` ${c.bold}Warnings${c.reset}`);
9319
+ for (const w of warnings) console.log(` ${c.yellow}!${c.reset} ${w}`);
9320
+ console.log();
9321
+ }
9322
+ if (chainFailed) {
9323
+ console.log(`${c.red}REJECTED${c.reset} Chain verification incomplete.`);
9324
+ process.exit(1);
9325
+ }
9326
+ console.log(`${c.green}OK${c.reset} Pack is valid.`);
9327
+ }
9328
+ var init_verify2 = __esm({
9329
+ "packages/brain/src/cli/verify.ts"() {
9330
+ "use strict";
9331
+ init_src();
9332
+ init_banner();
9333
+ }
9334
+ });
9335
+
9336
+ // packages/brain/src/cli/snapshot.ts
9337
+ var snapshot_exports = {};
9338
+ __export(snapshot_exports, {
9339
+ runSnapshot: () => runSnapshot
9340
+ });
9341
+ function parseArgs2() {
9342
+ const argv = process.argv.slice(3);
9343
+ const args = { showHelp: false };
9344
+ for (let i = 0; i < argv.length; i++) {
9345
+ const a = argv[i];
9346
+ if (a === "--help" || a === "-h") args.showHelp = true;
9347
+ else if (a === "--out") args.out = argv[++i];
9348
+ else if (a === "--encrypt-key") args.encryptKey = argv[++i];
9349
+ }
9350
+ return args;
9351
+ }
9352
+ function printUsage2() {
9353
+ console.log(`Usage: clude snapshot [options]
9354
+
9355
+ Writes a dated MemoryPack tarball of your local memories.
9356
+
9357
+ Options:
9358
+ --out <path> Output path (default:
9359
+ ~/.clude/snapshots/clude-YYYYMMDD-HHMMSS.tar.zst)
9360
+ --encrypt-key <base64> Encrypt the snapshot (32 bytes, base64).
9361
+ Without this, the snapshot is plaintext on
9362
+ disk \u2014 fine for personal local backups,
9363
+ but encrypt for anything you'll move off-host.
9364
+
9365
+ Cron example (daily 03:00):
9366
+ 0 3 * * * /usr/local/bin/node /usr/local/lib/node_modules/@clude/sdk/dist/cli/index.js snapshot
9367
+
9368
+ Exit code: 0 on success, 1 on failure. Single-line stdout on success
9369
+ (the snapshot path); errors go to stderr.`);
9370
+ }
9371
+ function timestamp() {
9372
+ const d = /* @__PURE__ */ new Date();
9373
+ const pad = (n) => String(n).padStart(2, "0");
9374
+ return `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}-${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}`;
9375
+ }
9376
+ function defaultOutPath() {
9377
+ const dir = (0, import_path7.join)((0, import_os2.homedir)(), ".clude", "snapshots");
9378
+ if (!(0, import_fs8.existsSync)(dir)) (0, import_fs8.mkdirSync)(dir, { recursive: true });
9379
+ return (0, import_path7.join)(dir, `clude-${timestamp()}.tar.zst`);
9380
+ }
9381
+ async function runSnapshot() {
9382
+ const args = parseArgs2();
9383
+ if (args.showHelp) {
9384
+ printUsage2();
9385
+ process.exit(0);
9386
+ }
9387
+ let raw;
9388
+ try {
9389
+ const { localRecall: localRecall2, localStats: localStats2 } = (init_local_store(), __toCommonJS(local_store_exports));
9390
+ const stats = localStats2();
9391
+ if (stats.total_memories === 0) {
9392
+ console.error("clude snapshot: no local memories found in ~/.clude/memories.json");
9393
+ process.exit(1);
9394
+ }
9395
+ raw = localRecall2({ query: "", limit: 1e4 });
9396
+ } catch (err) {
9397
+ console.error(`clude snapshot: failed to read local store: ${err.message}`);
9398
+ process.exit(1);
9399
+ }
9400
+ const records = raw.map((m, i) => {
9401
+ const rec = {
9402
+ id: m.id ? String(m.id) : String(i + 1),
9403
+ created_at: m.created_at || (/* @__PURE__ */ new Date()).toISOString(),
9404
+ kind: m.type || m.memory_type || "episodic",
9405
+ content: m.content || "",
9406
+ tags: m.tags || [],
9407
+ importance: typeof m.importance === "number" ? m.importance : 0.5,
9408
+ source: m.source || ""
9409
+ };
9410
+ if (m.summary) rec.summary = m.summary;
9411
+ if (m.access_count != null) rec.access_count = m.access_count;
9412
+ if (m.last_accessed_at) rec.last_accessed_at = m.last_accessed_at;
9413
+ return rec;
9414
+ });
9415
+ let secretKey;
9416
+ let publicKey;
9417
+ try {
9418
+ const { getBotWallet: getBotWallet2 } = (init_solana_client(), __toCommonJS(solana_client_exports));
9419
+ const wallet = getBotWallet2();
9420
+ if (wallet) {
9421
+ secretKey = wallet.secretKey;
9422
+ publicKey = wallet.publicKey.toBase58();
9423
+ }
9424
+ } catch {
9425
+ }
9426
+ let encryption;
9427
+ if (args.encryptKey) {
9428
+ const buf = Buffer.from(args.encryptKey, "base64");
9429
+ if (buf.length !== ENCRYPTION_KEY_BYTES) {
9430
+ console.error(
9431
+ `clude snapshot: --encrypt-key must decode to ${ENCRYPTION_KEY_BYTES} bytes (got ${buf.length})`
9432
+ );
9433
+ process.exit(1);
9434
+ }
9435
+ encryption = { key: new Uint8Array(buf) };
9436
+ }
9437
+ const outPath = args.out || defaultOutPath();
9438
+ try {
9439
+ writeMemoryPack(outPath, records, {
9440
+ producer: {
9441
+ name: "clude",
9442
+ version: "3.1.0",
9443
+ public_key: publicKey
9444
+ },
9445
+ record_schema: "clude-memory-v3",
9446
+ secretKey,
9447
+ anchor_chain: "solana-mainnet",
9448
+ format: "tarball",
9449
+ encryption
9450
+ });
9451
+ } catch (err) {
9452
+ console.error(`clude snapshot: write failed: ${err.message}`);
9453
+ process.exit(1);
9454
+ }
9455
+ console.log(outPath);
9456
+ }
9457
+ var import_fs8, import_os2, import_path7;
9458
+ var init_snapshot = __esm({
9459
+ "packages/brain/src/cli/snapshot.ts"() {
9460
+ "use strict";
9461
+ import_fs8 = require("fs");
9462
+ import_os2 = require("os");
9463
+ import_path7 = require("path");
9464
+ init_src();
7371
9465
  }
7372
9466
  });
7373
9467
 
@@ -7561,23 +9655,23 @@ var init_clinamen = __esm({
7561
9655
  var server_exports = {};
7562
9656
  async function getSqliteStore() {
7563
9657
  if (_sqliteStore) return _sqliteStore;
7564
- const path8 = require("path");
7565
- const os5 = require("os");
7566
- const fs6 = require("fs");
9658
+ const path9 = require("path");
9659
+ const os6 = require("os");
9660
+ const fs7 = require("fs");
7567
9661
  const { SqliteStore: SqliteStore2 } = (init_sqlite_store(), __toCommonJS(sqlite_store_exports));
7568
9662
  const { LocalEmbedder: LocalEmbedder2 } = (init_embedder(), __toCommonJS(embedder_exports));
7569
- const dbDir = path8.join(os5.homedir(), ".clude");
7570
- fs6.mkdirSync(dbDir, { recursive: true });
9663
+ const dbDir = path9.join(os6.homedir(), ".clude");
9664
+ fs7.mkdirSync(dbDir, { recursive: true });
7571
9665
  const embedder = new LocalEmbedder2();
7572
9666
  const store = new SqliteStore2({
7573
- dbPath: path8.join(dbDir, "brain.db"),
9667
+ dbPath: path9.join(dbDir, "brain.db"),
7574
9668
  embedder
7575
9669
  });
7576
9670
  _sqliteStore = store;
7577
9671
  return store;
7578
9672
  }
7579
- async function cortexFetch(method, path8, body) {
7580
- const url = `${CORTEX_HOST_URL}${path8}`;
9673
+ async function cortexFetch(method, path9, body) {
9674
+ const url = `${CORTEX_HOST_URL}${path9}`;
7581
9675
  const controller = new AbortController();
7582
9676
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
7583
9677
  try {
@@ -7597,7 +9691,7 @@ async function cortexFetch(method, path8, body) {
7597
9691
  return await res.json();
7598
9692
  } catch (err) {
7599
9693
  if (err.name === "AbortError") {
7600
- throw new Error(`Cortex API timeout after ${FETCH_TIMEOUT_MS / 1e3}s: ${method} ${path8}`);
9694
+ throw new Error(`Cortex API timeout after ${FETCH_TIMEOUT_MS / 1e3}s: ${method} ${path9}`);
7601
9695
  }
7602
9696
  throw err;
7603
9697
  } finally {
@@ -8473,7 +10567,9 @@ var require_package = __commonJS({
8473
10567
  "@ai-sdk/openai": "^3.0.49",
8474
10568
  "@ai-sdk/xai": "^3.0.75",
8475
10569
  "@anthropic-ai/sdk": "^0.39.0",
10570
+ "@clude/memorypack": "workspace:*",
8476
10571
  "@clude/shared": "workspace:*",
10572
+ "@clude/tokenization": "workspace:*",
8477
10573
  "@huggingface/transformers": "^4.1.0",
8478
10574
  "@modelcontextprotocol/sdk": "^1.26.0",
8479
10575
  "@openrouter/ai-sdk-provider": "^2.3.3",
@@ -8530,6 +10626,12 @@ if (command === "setup") {
8530
10626
  console.error("MCP install failed:", err.message);
8531
10627
  process.exit(1);
8532
10628
  });
10629
+ } else if (command === "connect") {
10630
+ const { runConnect: runConnect2 } = (init_connect(), __toCommonJS(connect_exports));
10631
+ runConnect2().catch((err) => {
10632
+ console.error("Connect failed:", err.message);
10633
+ process.exit(1);
10634
+ });
8533
10635
  } else if (command === "ship") {
8534
10636
  const message = process.argv.slice(3).join(" ");
8535
10637
  const { runShip: runShip2 } = (init_ship(), __toCommonJS(ship_exports));
@@ -8579,6 +10681,28 @@ if (command === "setup") {
8579
10681
  console.error("Dream failed:", err.message);
8580
10682
  process.exit(1);
8581
10683
  });
10684
+ } else if (command === "verify") {
10685
+ try {
10686
+ const { runVerify: runVerify2 } = (init_verify2(), __toCommonJS(verify_exports));
10687
+ runVerify2().catch((err) => {
10688
+ console.error("Verify failed:", err.message);
10689
+ process.exit(1);
10690
+ });
10691
+ } catch (err) {
10692
+ console.error("Verify failed:", err.message);
10693
+ process.exit(1);
10694
+ }
10695
+ } else if (command === "snapshot") {
10696
+ try {
10697
+ const { runSnapshot: runSnapshot2 } = (init_snapshot(), __toCommonJS(snapshot_exports));
10698
+ runSnapshot2().catch((err) => {
10699
+ console.error("Snapshot failed:", err.message);
10700
+ process.exit(1);
10701
+ });
10702
+ } catch (err) {
10703
+ console.error("Snapshot failed:", err.message);
10704
+ process.exit(1);
10705
+ }
8582
10706
  } else if (command === "mcp-serve") {
8583
10707
  init_server();
8584
10708
  } else if (command === "start" || command === "bot") {
@@ -8600,10 +10724,13 @@ if (command === "setup") {
8600
10724
  console.log(` ${c2.cyan}npx @clude/sdk status${c2.reset} Check if Clude is active + memory stats`);
8601
10725
  console.log(` ${c2.cyan}npx @clude/sdk init${c2.reset} Advanced setup (self-hosted options)`);
8602
10726
  console.log(` ${c2.cyan}npx @clude/sdk register${c2.reset} Get an API key only`);
8603
- console.log(` ${c2.cyan}npx @clude/sdk mcp-install${c2.reset} Install MCP server for your IDE`);
10727
+ console.log(` ${c2.cyan}npx @clude/sdk connect${c2.reset} Connect to Claude Desktop as a remote connector`);
10728
+ console.log(` ${c2.cyan}npx @clude/sdk mcp-install${c2.reset} Install MCP server for your IDE (local stdio)`);
8604
10729
  console.log(` ${c2.cyan}npx @clude/sdk inject-instructions${c2.reset} Write usage instructions to CLAUDE.md`);
8605
- console.log(` ${c2.cyan}npx @clude/sdk export${c2.reset} Export memories (json/md/chatgpt/gemini)`);
10730
+ console.log(` ${c2.cyan}npx @clude/sdk export${c2.reset} Export memories (json/md/chatgpt/gemini/memorypack)`);
8606
10731
  console.log(` ${c2.cyan}npx @clude/sdk import${c2.reset} Import from ChatGPT export, markdown, JSON`);
10732
+ console.log(` ${c2.cyan}npx @clude/sdk verify${c2.reset} Verify a MemoryPack (.tar.zst or directory)`);
10733
+ console.log(` ${c2.cyan}npx @clude/sdk snapshot${c2.reset} Cron-friendly tarball of local memories`);
8607
10734
  console.log(` ${c2.cyan}npx @clude/sdk sync${c2.reset} Auto-update system prompt file`);
8608
10735
  console.log(` ${c2.cyan}npx @clude/sdk ship "msg"${c2.reset} Broadcast to Telegram channel`);
8609
10736
  console.log(` ${c2.cyan}npx @clude/sdk doctor${c2.reset} Run diagnostics`);