@clude/sdk 3.0.4 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1792 -154
- package/dist/mcp/server.js +515 -27
- package/dist/sdk/index.js +517 -29
- package/package.json +8 -10
- package/supabase-schema.sql +107 -0
package/dist/cli/index.js
CHANGED
|
@@ -749,9 +749,9 @@ function createPrompt() {
|
|
|
749
749
|
});
|
|
750
750
|
}
|
|
751
751
|
function ask(rl, question) {
|
|
752
|
-
return new Promise((
|
|
752
|
+
return new Promise((resolve5) => {
|
|
753
753
|
rl.question(` ${c.white}?${c.reset} ${question}`, (answer) => {
|
|
754
|
-
|
|
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((
|
|
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
|
-
|
|
767
|
+
resolve5(choices[idx].key);
|
|
768
768
|
} else {
|
|
769
|
-
|
|
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((
|
|
1500
|
+
return new Promise((resolve5) => {
|
|
1501
1501
|
rl.question(" Email: ", (answer) => {
|
|
1502
1502
|
rl.close();
|
|
1503
1503
|
const trimmed = answer.trim();
|
|
1504
|
-
|
|
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((
|
|
1625
|
+
return new Promise((resolve5) => {
|
|
1626
1626
|
rl.question(` ${c.white}?${c.reset} ${question}`, (answer) => {
|
|
1627
|
-
|
|
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((
|
|
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
|
-
|
|
1637
|
+
resolve5(choices[idx]);
|
|
1638
1638
|
} else if (choices.map((c2) => c2.toLowerCase()).includes(answer.trim().toLowerCase())) {
|
|
1639
|
-
|
|
1639
|
+
resolve5(answer.trim().toLowerCase());
|
|
1640
1640
|
} else {
|
|
1641
|
-
|
|
1641
|
+
resolve5(choices[0]);
|
|
1642
1642
|
}
|
|
1643
1643
|
});
|
|
1644
1644
|
});
|
|
@@ -2172,9 +2172,9 @@ function createPrompt3() {
|
|
|
2172
2172
|
});
|
|
2173
2173
|
}
|
|
2174
2174
|
function ask3(rl, question) {
|
|
2175
|
-
return new Promise((
|
|
2175
|
+
return new Promise((resolve5) => {
|
|
2176
2176
|
rl.question(` ${c.white}?${c.reset} ${question}`, (answer) => {
|
|
2177
|
-
|
|
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,21 +3040,25 @@ var init_status = __esm({
|
|
|
2797
3040
|
}
|
|
2798
3041
|
});
|
|
2799
3042
|
|
|
2800
|
-
// packages/
|
|
3043
|
+
// packages/memorypack/src/types.ts
|
|
2801
3044
|
var MEMORYPACK_VERSION;
|
|
2802
3045
|
var init_types = __esm({
|
|
2803
|
-
"packages/
|
|
3046
|
+
"packages/memorypack/src/types.ts"() {
|
|
2804
3047
|
"use strict";
|
|
2805
|
-
MEMORYPACK_VERSION = "0.
|
|
3048
|
+
MEMORYPACK_VERSION = "0.2";
|
|
2806
3049
|
}
|
|
2807
3050
|
});
|
|
2808
3051
|
|
|
2809
|
-
// packages/
|
|
3052
|
+
// packages/memorypack/src/sign.ts
|
|
2810
3053
|
function hashRecordLine(line) {
|
|
2811
3054
|
const clean = line.replace(/\n$/, "");
|
|
2812
3055
|
const hex = (0, import_crypto.createHash)("sha256").update(clean, "utf-8").digest("hex");
|
|
2813
3056
|
return `sha256:${hex}`;
|
|
2814
3057
|
}
|
|
3058
|
+
function hashBuffer(buf) {
|
|
3059
|
+
const hex = (0, import_crypto.createHash)("sha256").update(buf).digest("hex");
|
|
3060
|
+
return `sha256:${hex}`;
|
|
3061
|
+
}
|
|
2815
3062
|
function signHash(hash, secretKey) {
|
|
2816
3063
|
const messageBytes = Buffer.from(hash, "utf-8");
|
|
2817
3064
|
const sig = import_tweetnacl.default.sign.detached(messageBytes, secretKey);
|
|
@@ -2828,18 +3075,75 @@ function verifyHash(hash, signature, publicKey) {
|
|
|
2828
3075
|
return false;
|
|
2829
3076
|
}
|
|
2830
3077
|
}
|
|
2831
|
-
|
|
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;
|
|
2832
3134
|
var init_sign = __esm({
|
|
2833
|
-
"packages/
|
|
3135
|
+
"packages/memorypack/src/sign.ts"() {
|
|
2834
3136
|
"use strict";
|
|
2835
3137
|
import_crypto = require("crypto");
|
|
2836
3138
|
import_tweetnacl = __toESM(require("tweetnacl"));
|
|
2837
3139
|
bs58Module = __toESM(require("bs58"));
|
|
2838
3140
|
bs58 = bs58Module.default || bs58Module;
|
|
3141
|
+
ENCRYPTION_KEY_BYTES = import_tweetnacl.default.secretbox.keyLength;
|
|
3142
|
+
ENCRYPTION_NONCE_BYTES = import_tweetnacl.default.secretbox.nonceLength;
|
|
2839
3143
|
}
|
|
2840
3144
|
});
|
|
2841
3145
|
|
|
2842
|
-
// packages/
|
|
3146
|
+
// packages/memorypack/src/writer.ts
|
|
2843
3147
|
function serializeRecord(record) {
|
|
2844
3148
|
const KNOWN_ORDER = [
|
|
2845
3149
|
"id",
|
|
@@ -2857,7 +3161,9 @@ function serializeRecord(record) {
|
|
|
2857
3161
|
"last_accessed_at",
|
|
2858
3162
|
"parent_ids",
|
|
2859
3163
|
"compacted_from",
|
|
2860
|
-
"blob_ref"
|
|
3164
|
+
"blob_ref",
|
|
3165
|
+
"encrypted",
|
|
3166
|
+
"nonce"
|
|
2861
3167
|
];
|
|
2862
3168
|
const out = {};
|
|
2863
3169
|
for (const k of KNOWN_ORDER) {
|
|
@@ -2872,21 +3178,66 @@ function serializeRecord(record) {
|
|
|
2872
3178
|
}
|
|
2873
3179
|
return JSON.stringify(out);
|
|
2874
3180
|
}
|
|
2875
|
-
function writeMemoryPack(
|
|
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
|
+
}
|
|
2876
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
|
+
});
|
|
2877
3220
|
const manifest = {
|
|
2878
3221
|
memorypack_version: MEMORYPACK_VERSION,
|
|
2879
3222
|
producer: opts.producer,
|
|
2880
|
-
created_at: (
|
|
2881
|
-
record_count:
|
|
3223
|
+
created_at: clock(),
|
|
3224
|
+
record_count: recordsToWrite.length,
|
|
2882
3225
|
record_schema: opts.record_schema,
|
|
2883
3226
|
signature_algorithm: opts.secretKey ? "ed25519" : void 0,
|
|
2884
|
-
anchor_chain: opts.anchor_chain
|
|
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
|
|
2885
3236
|
};
|
|
2886
3237
|
(0, import_fs2.writeFileSync)((0, import_path3.join)(dir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
2887
3238
|
const recordLines = [];
|
|
2888
3239
|
const sigs = [];
|
|
2889
|
-
for (const record of
|
|
3240
|
+
for (const record of recordsToWrite) {
|
|
2890
3241
|
const line = serializeRecord(record);
|
|
2891
3242
|
recordLines.push(line);
|
|
2892
3243
|
if (opts.secretKey && opts.producer.public_key) {
|
|
@@ -2907,26 +3258,124 @@ function writeMemoryPack(dir, records, opts, anchors) {
|
|
|
2907
3258
|
sigs.map((s) => JSON.stringify(s)).join("\n") + "\n"
|
|
2908
3259
|
);
|
|
2909
3260
|
}
|
|
2910
|
-
if (anchors && anchors.length > 0) {
|
|
3261
|
+
if (opts.anchors && opts.anchors.length > 0) {
|
|
2911
3262
|
(0, import_fs2.writeFileSync)(
|
|
2912
3263
|
(0, import_path3.join)(dir, "anchors.jsonl"),
|
|
2913
|
-
anchors.map((a) => JSON.stringify(a)).join("\n") + "\n"
|
|
3264
|
+
opts.anchors.map((a) => JSON.stringify(a)).join("\n") + "\n"
|
|
2914
3265
|
);
|
|
2915
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
|
+
}
|
|
2916
3349
|
}
|
|
2917
|
-
var import_fs2, import_path3;
|
|
3350
|
+
var import_fs2, import_child_process2, import_path3;
|
|
2918
3351
|
var init_writer = __esm({
|
|
2919
|
-
"packages/
|
|
3352
|
+
"packages/memorypack/src/writer.ts"() {
|
|
2920
3353
|
"use strict";
|
|
2921
3354
|
import_fs2 = require("fs");
|
|
3355
|
+
import_child_process2 = require("child_process");
|
|
2922
3356
|
import_path3 = require("path");
|
|
2923
3357
|
init_types();
|
|
2924
3358
|
init_sign();
|
|
2925
3359
|
}
|
|
2926
3360
|
});
|
|
2927
3361
|
|
|
2928
|
-
// packages/
|
|
2929
|
-
function readMemoryPack(
|
|
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) {
|
|
2930
3379
|
const warnings = [];
|
|
2931
3380
|
const manifestPath = (0, import_path4.join)(dir, "manifest.json");
|
|
2932
3381
|
if (!(0, import_fs3.existsSync)(manifestPath)) {
|
|
@@ -2954,12 +3403,14 @@ function readMemoryPack(dir, opts = {}) {
|
|
|
2954
3403
|
lineByHash.set(hashRecordLine(line), line);
|
|
2955
3404
|
}
|
|
2956
3405
|
if (records.length !== manifest.record_count) {
|
|
2957
|
-
warnings.push(
|
|
3406
|
+
warnings.push(
|
|
3407
|
+
`record_count mismatch: manifest ${manifest.record_count} vs jsonl ${records.length}`
|
|
3408
|
+
);
|
|
2958
3409
|
}
|
|
2959
3410
|
const verifiedRecords = /* @__PURE__ */ new Set();
|
|
2960
3411
|
const sigsPath = (0, import_path4.join)(dir, "signatures.jsonl");
|
|
2961
|
-
const
|
|
2962
|
-
if (
|
|
3412
|
+
const signaturesPresent = (0, import_fs3.existsSync)(sigsPath);
|
|
3413
|
+
if (signaturesPresent) {
|
|
2963
3414
|
const sigLines = (0, import_fs3.readFileSync)(sigsPath, "utf-8").split("\n").filter((l) => l.length > 0);
|
|
2964
3415
|
const pubkey = opts.publicKey ?? manifest.producer.public_key;
|
|
2965
3416
|
if (!pubkey) {
|
|
@@ -2984,7 +3435,7 @@ function readMemoryPack(dir, opts = {}) {
|
|
|
2984
3435
|
}
|
|
2985
3436
|
}
|
|
2986
3437
|
const unsignedRecords = /* @__PURE__ */ new Set();
|
|
2987
|
-
if (!
|
|
3438
|
+
if (!signaturesPresent) {
|
|
2988
3439
|
for (const hash of lineByHash.keys()) unsignedRecords.add(hash);
|
|
2989
3440
|
}
|
|
2990
3441
|
if (opts.strictSignatures && unsignedRecords.size > 0) {
|
|
@@ -3000,25 +3451,404 @@ function readMemoryPack(dir, opts = {}) {
|
|
|
3000
3451
|
anchors.push(JSON.parse(line));
|
|
3001
3452
|
}
|
|
3002
3453
|
}
|
|
3003
|
-
|
|
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
|
+
);
|
|
3004
3688
|
}
|
|
3005
|
-
var import_fs3, import_path4;
|
|
3689
|
+
var import_fs3, import_child_process3, import_os, import_path4, SAFE_MEMBER_RE;
|
|
3006
3690
|
var init_reader = __esm({
|
|
3007
|
-
"packages/
|
|
3691
|
+
"packages/memorypack/src/reader.ts"() {
|
|
3008
3692
|
"use strict";
|
|
3009
3693
|
import_fs3 = require("fs");
|
|
3694
|
+
import_child_process3 = require("child_process");
|
|
3695
|
+
import_os = require("os");
|
|
3010
3696
|
import_path4 = require("path");
|
|
3011
3697
|
init_sign();
|
|
3698
|
+
SAFE_MEMBER_RE = /^[A-Za-z0-9._/-]+$/;
|
|
3012
3699
|
}
|
|
3013
3700
|
});
|
|
3014
3701
|
|
|
3015
|
-
// packages/
|
|
3016
|
-
var
|
|
3017
|
-
"packages/
|
|
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"() {
|
|
3018
3846
|
"use strict";
|
|
3019
3847
|
init_types();
|
|
3020
3848
|
init_writer();
|
|
3021
3849
|
init_reader();
|
|
3850
|
+
init_stream();
|
|
3851
|
+
init_chain_verify();
|
|
3022
3852
|
init_sign();
|
|
3023
3853
|
}
|
|
3024
3854
|
});
|
|
@@ -3386,13 +4216,18 @@ async function initDatabase2() {
|
|
|
3386
4216
|
is_active BOOLEAN DEFAULT TRUE,
|
|
3387
4217
|
metadata JSONB DEFAULT '{}',
|
|
3388
4218
|
owner_wallet TEXT,
|
|
3389
|
-
privy_did TEXT
|
|
4219
|
+
privy_did TEXT,
|
|
4220
|
+
email TEXT
|
|
3390
4221
|
);
|
|
3391
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
|
+
|
|
3392
4226
|
CREATE INDEX IF NOT EXISTS idx_agent_keys_api_key ON agent_keys(api_key);
|
|
3393
4227
|
CREATE INDEX IF NOT EXISTS idx_agent_keys_owner ON agent_keys(owner_wallet);
|
|
3394
4228
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_owner_unique ON agent_keys(owner_wallet) WHERE owner_wallet IS NOT NULL;
|
|
3395
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;
|
|
3396
4231
|
|
|
3397
4232
|
-- Cortex recall performance: owner_wallet scoped queries
|
|
3398
4233
|
CREATE INDEX IF NOT EXISTS idx_cortex_owner_recall ON memories(owner_wallet, decay_factor DESC, created_at DESC);
|
|
@@ -3826,6 +4661,19 @@ async function initDatabase2() {
|
|
|
3826
4661
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
3827
4662
|
);
|
|
3828
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);
|
|
3829
4677
|
`
|
|
3830
4678
|
});
|
|
3831
4679
|
if (error) {
|
|
@@ -4110,11 +4958,13 @@ var init_openrouter_client = __esm({
|
|
|
4110
4958
|
OPENROUTER_API_URL = "https://openrouter.ai/api/v1";
|
|
4111
4959
|
OPENROUTER_MODELS = {
|
|
4112
4960
|
// Frontier (Anthropic)
|
|
4961
|
+
"claude-opus-4.7": "anthropic/claude-opus-4.7",
|
|
4113
4962
|
"claude-opus-4.6": "anthropic/claude-opus-4.6",
|
|
4114
4963
|
"claude-sonnet-4.6": "anthropic/claude-sonnet-4.6",
|
|
4115
4964
|
"claude-opus-4.5": "anthropic/claude-opus-4.5",
|
|
4116
4965
|
"claude-sonnet-4.5": "anthropic/claude-sonnet-4.5",
|
|
4117
4966
|
// Frontier (Other providers)
|
|
4967
|
+
"gpt-5.5": "openai/gpt-5.5",
|
|
4118
4968
|
"gpt-5.4": "openai/gpt-5.4",
|
|
4119
4969
|
"grok-4.1": "x-ai/grok-4.1-fast",
|
|
4120
4970
|
"gemini-3-pro": "google/gemini-3-pro-preview",
|
|
@@ -4236,12 +5086,12 @@ __export(solana_client_exports, {
|
|
|
4236
5086
|
});
|
|
4237
5087
|
function getConnection() {
|
|
4238
5088
|
if (!connection) {
|
|
4239
|
-
connection = new
|
|
5089
|
+
connection = new import_web32.Connection(config.solana.rpcUrl, "confirmed");
|
|
4240
5090
|
}
|
|
4241
5091
|
return connection;
|
|
4242
5092
|
}
|
|
4243
5093
|
function _configureSolana(rpcUrl, privateKey) {
|
|
4244
|
-
connection = new
|
|
5094
|
+
connection = new import_web32.Connection(rpcUrl, "confirmed");
|
|
4245
5095
|
if (privateKey) {
|
|
4246
5096
|
try {
|
|
4247
5097
|
const raw = privateKey.trim();
|
|
@@ -4251,7 +5101,7 @@ function _configureSolana(rpcUrl, privateKey) {
|
|
|
4251
5101
|
} else {
|
|
4252
5102
|
secretKey = bs582.decode(raw);
|
|
4253
5103
|
}
|
|
4254
|
-
botWallet =
|
|
5104
|
+
botWallet = import_web32.Keypair.fromSecretKey(secretKey);
|
|
4255
5105
|
} catch (err) {
|
|
4256
5106
|
log6.error({ err }, "SDK: Failed to load bot wallet");
|
|
4257
5107
|
}
|
|
@@ -4267,7 +5117,7 @@ function getBotWallet() {
|
|
|
4267
5117
|
} else {
|
|
4268
5118
|
secretKey = bs582.decode(raw);
|
|
4269
5119
|
}
|
|
4270
|
-
botWallet =
|
|
5120
|
+
botWallet = import_web32.Keypair.fromSecretKey(secretKey);
|
|
4271
5121
|
log6.info({ publicKey: botWallet.publicKey.toBase58() }, "Bot wallet loaded");
|
|
4272
5122
|
} catch (err) {
|
|
4273
5123
|
log6.error({ err }, "Failed to load bot wallet");
|
|
@@ -4283,14 +5133,14 @@ async function writeMemo(memo) {
|
|
|
4283
5133
|
}
|
|
4284
5134
|
const conn = getConnection();
|
|
4285
5135
|
const truncatedMemo = memo.slice(0, MEMO_MAX_LENGTH);
|
|
4286
|
-
const instruction = new
|
|
5136
|
+
const instruction = new import_web32.TransactionInstruction({
|
|
4287
5137
|
keys: [{ pubkey: wallet.publicKey, isSigner: true, isWritable: true }],
|
|
4288
5138
|
programId: MEMO_PROGRAM_ID2,
|
|
4289
5139
|
data: Buffer.from(truncatedMemo, "utf-8")
|
|
4290
5140
|
});
|
|
4291
|
-
const transaction = new
|
|
5141
|
+
const transaction = new import_web32.Transaction().add(instruction);
|
|
4292
5142
|
try {
|
|
4293
|
-
const signature = await (0,
|
|
5143
|
+
const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
|
|
4294
5144
|
log6.info({ signature, memoLength: truncatedMemo.length }, "Memo written on-chain");
|
|
4295
5145
|
return signature;
|
|
4296
5146
|
} catch (err) {
|
|
@@ -4300,7 +5150,7 @@ async function writeMemo(memo) {
|
|
|
4300
5150
|
}
|
|
4301
5151
|
function getDevnetConnection() {
|
|
4302
5152
|
if (!devnetConnection) {
|
|
4303
|
-
devnetConnection = new
|
|
5153
|
+
devnetConnection = new import_web32.Connection("https://api.devnet.solana.com", "confirmed");
|
|
4304
5154
|
}
|
|
4305
5155
|
return devnetConnection;
|
|
4306
5156
|
}
|
|
@@ -4312,14 +5162,14 @@ async function writeMemoDevnet(memo) {
|
|
|
4312
5162
|
}
|
|
4313
5163
|
const conn = getDevnetConnection();
|
|
4314
5164
|
const truncatedMemo = memo.slice(0, MEMO_MAX_LENGTH);
|
|
4315
|
-
const instruction = new
|
|
5165
|
+
const instruction = new import_web32.TransactionInstruction({
|
|
4316
5166
|
keys: [{ pubkey: wallet.publicKey, isSigner: true, isWritable: true }],
|
|
4317
5167
|
programId: MEMO_PROGRAM_ID2,
|
|
4318
5168
|
data: Buffer.from(truncatedMemo, "utf-8")
|
|
4319
5169
|
});
|
|
4320
|
-
const transaction = new
|
|
5170
|
+
const transaction = new import_web32.Transaction().add(instruction);
|
|
4321
5171
|
try {
|
|
4322
|
-
const signature = await (0,
|
|
5172
|
+
const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
|
|
4323
5173
|
log6.info({ signature, memoLength: truncatedMemo.length }, "Memo written on devnet");
|
|
4324
5174
|
return signature;
|
|
4325
5175
|
} catch (err) {
|
|
@@ -4336,7 +5186,7 @@ function solscanTxUrl(signature) {
|
|
|
4336
5186
|
}
|
|
4337
5187
|
function _configureMemoryRegistry(programId) {
|
|
4338
5188
|
try {
|
|
4339
|
-
registryProgramId = new
|
|
5189
|
+
registryProgramId = new import_web32.PublicKey(programId);
|
|
4340
5190
|
log6.info({ programId: programId.slice(0, 12) + "..." }, "Memory registry program configured");
|
|
4341
5191
|
} catch (err) {
|
|
4342
5192
|
log6.error({ err }, "Invalid memory registry program ID");
|
|
@@ -4347,14 +5197,14 @@ function isRegistryEnabled() {
|
|
|
4347
5197
|
}
|
|
4348
5198
|
function deriveRegistryPDA(authority) {
|
|
4349
5199
|
if (!registryProgramId) throw new Error("Registry program not configured");
|
|
4350
|
-
return
|
|
5200
|
+
return import_web32.PublicKey.findProgramAddressSync(
|
|
4351
5201
|
[Buffer.from("memory-registry"), authority.toBuffer()],
|
|
4352
5202
|
registryProgramId
|
|
4353
5203
|
);
|
|
4354
5204
|
}
|
|
4355
5205
|
function anchorDiscriminator(name) {
|
|
4356
|
-
const { createHash:
|
|
4357
|
-
const hash =
|
|
5206
|
+
const { createHash: createHash5 } = require("crypto");
|
|
5207
|
+
const hash = createHash5("sha256").update(`global:${name}`).digest();
|
|
4358
5208
|
return hash.subarray(0, 8);
|
|
4359
5209
|
}
|
|
4360
5210
|
async function initializeRegistry() {
|
|
@@ -4370,18 +5220,18 @@ async function initializeRegistry() {
|
|
|
4370
5220
|
return;
|
|
4371
5221
|
}
|
|
4372
5222
|
const discriminator = anchorDiscriminator("initialize");
|
|
4373
|
-
const instruction = new
|
|
5223
|
+
const instruction = new import_web32.TransactionInstruction({
|
|
4374
5224
|
keys: [
|
|
4375
5225
|
{ pubkey: registryPDA, isSigner: false, isWritable: true },
|
|
4376
5226
|
{ pubkey: wallet.publicKey, isSigner: true, isWritable: true },
|
|
4377
|
-
{ pubkey:
|
|
5227
|
+
{ pubkey: import_web32.SystemProgram.programId, isSigner: false, isWritable: false }
|
|
4378
5228
|
],
|
|
4379
5229
|
programId: registryProgramId,
|
|
4380
5230
|
data: discriminator
|
|
4381
5231
|
});
|
|
4382
|
-
const transaction = new
|
|
5232
|
+
const transaction = new import_web32.Transaction().add(instruction);
|
|
4383
5233
|
try {
|
|
4384
|
-
const signature = await (0,
|
|
5234
|
+
const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
|
|
4385
5235
|
registryInitialized = true;
|
|
4386
5236
|
log6.info({ signature, registry: registryPDA.toBase58() }, "Registry PDA initialized on-chain");
|
|
4387
5237
|
} catch (err) {
|
|
@@ -4421,18 +5271,18 @@ async function registerMemoryOnChain(contentHash, memoryType, importance, memory
|
|
|
4421
5271
|
data.writeUInt8(importanceToTier(importance), 41);
|
|
4422
5272
|
data.writeBigUInt64LE(BigInt(memoryId), 42);
|
|
4423
5273
|
data.writeUInt8(encrypted ? 1 : 0, 50);
|
|
4424
|
-
const instruction = new
|
|
5274
|
+
const instruction = new import_web32.TransactionInstruction({
|
|
4425
5275
|
keys: [
|
|
4426
5276
|
{ pubkey: registryPDA, isSigner: false, isWritable: true },
|
|
4427
5277
|
{ pubkey: wallet.publicKey, isSigner: true, isWritable: true },
|
|
4428
|
-
{ pubkey:
|
|
5278
|
+
{ pubkey: import_web32.SystemProgram.programId, isSigner: false, isWritable: false }
|
|
4429
5279
|
],
|
|
4430
5280
|
programId: registryProgramId,
|
|
4431
5281
|
data
|
|
4432
5282
|
});
|
|
4433
|
-
const transaction = new
|
|
5283
|
+
const transaction = new import_web32.Transaction().add(instruction);
|
|
4434
5284
|
try {
|
|
4435
|
-
const signature = await (0,
|
|
5285
|
+
const signature = await (0, import_web32.sendAndConfirmTransaction)(conn, transaction, [wallet]);
|
|
4436
5286
|
log6.info({ signature: signature.slice(0, 16), memoryId }, "Memory registered on-chain");
|
|
4437
5287
|
return signature;
|
|
4438
5288
|
} catch (err) {
|
|
@@ -4500,11 +5350,11 @@ async function verifyMemoTransaction(signature, contentHash) {
|
|
|
4500
5350
|
return false;
|
|
4501
5351
|
}
|
|
4502
5352
|
}
|
|
4503
|
-
var
|
|
5353
|
+
var import_web32, bs58Module2, import_tweetnacl2, bs582, log6, connection, botWallet, MEMO_PROGRAM_ID2, devnetConnection, registryProgramId, registryInitialized;
|
|
4504
5354
|
var init_solana_client = __esm({
|
|
4505
5355
|
"packages/shared/src/core/solana-client.ts"() {
|
|
4506
5356
|
"use strict";
|
|
4507
|
-
|
|
5357
|
+
import_web32 = require("@solana/web3.js");
|
|
4508
5358
|
bs58Module2 = __toESM(require("bs58"));
|
|
4509
5359
|
init_config();
|
|
4510
5360
|
init_logger();
|
|
@@ -4513,12 +5363,126 @@ var init_solana_client = __esm({
|
|
|
4513
5363
|
bs582 = bs58Module2.default || bs58Module2;
|
|
4514
5364
|
log6 = createChildLogger("solana-client");
|
|
4515
5365
|
botWallet = null;
|
|
4516
|
-
MEMO_PROGRAM_ID2 = new
|
|
5366
|
+
MEMO_PROGRAM_ID2 = new import_web32.PublicKey(MEMO_PROGRAM_ID);
|
|
4517
5367
|
registryProgramId = null;
|
|
4518
5368
|
registryInitialized = false;
|
|
4519
5369
|
}
|
|
4520
5370
|
});
|
|
4521
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();
|
|
5483
|
+
}
|
|
5484
|
+
});
|
|
5485
|
+
|
|
4522
5486
|
// packages/shared/src/core/embeddings.ts
|
|
4523
5487
|
function getCachedEmbedding(text) {
|
|
4524
5488
|
const key = text.slice(0, 500).toLowerCase().trim();
|
|
@@ -4693,6 +5657,221 @@ var init_embeddings = __esm({
|
|
|
4693
5657
|
}
|
|
4694
5658
|
});
|
|
4695
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
|
+
|
|
4696
5875
|
// packages/brain/src/experimental/config.ts
|
|
4697
5876
|
function envBool(key, fallback) {
|
|
4698
5877
|
const val = process.env[key];
|
|
@@ -5134,6 +6313,7 @@ __export(memory_exports, {
|
|
|
5134
6313
|
getSelfModel: () => getSelfModel,
|
|
5135
6314
|
hydrateMemories: () => hydrateMemories,
|
|
5136
6315
|
inferConcepts: () => inferConcepts,
|
|
6316
|
+
invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
|
|
5137
6317
|
isValidHashId: () => isValidHashId,
|
|
5138
6318
|
listMemories: () => listMemories,
|
|
5139
6319
|
markJepaQueried: () => markJepaQueried,
|
|
@@ -5236,13 +6416,83 @@ function isDuplicateWrite(source, summary) {
|
|
|
5236
6416
|
}
|
|
5237
6417
|
return false;
|
|
5238
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
|
+
}
|
|
5239
6481
|
async function storeMemory(opts) {
|
|
5240
6482
|
if (opts.source.startsWith("shiro_") && isDuplicateWrite(opts.source, opts.summary)) {
|
|
5241
6483
|
log11.debug({ source: opts.source, summary: opts.summary.slice(0, 60) }, "Skipping duplicate memory write");
|
|
5242
6484
|
return null;
|
|
5243
6485
|
}
|
|
5244
6486
|
const db = getDb();
|
|
6487
|
+
const ownerWallet = getOwnerWallet();
|
|
5245
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
|
+
});
|
|
5246
6496
|
const hashId = generateHashId();
|
|
5247
6497
|
try {
|
|
5248
6498
|
const plaintextContent = opts.content.slice(0, MEMORY_MAX_CONTENT_LENGTH);
|
|
@@ -5253,7 +6503,7 @@ async function storeMemory(opts) {
|
|
|
5253
6503
|
memory_type: opts.type,
|
|
5254
6504
|
content: storedContent,
|
|
5255
6505
|
summary: opts.summary.slice(0, MEMORY_MAX_SUMMARY_LENGTH),
|
|
5256
|
-
tags:
|
|
6506
|
+
tags: taggedTags,
|
|
5257
6507
|
concepts,
|
|
5258
6508
|
emotional_valence: clamp(opts.emotionalValence ?? 0, -1, 1),
|
|
5259
6509
|
importance: clamp(opts.importance ?? 0.5, 0, 1),
|
|
@@ -5266,8 +6516,8 @@ async function storeMemory(opts) {
|
|
|
5266
6516
|
compacted: false,
|
|
5267
6517
|
encrypted: shouldEncrypt,
|
|
5268
6518
|
encryption_pubkey: shouldEncrypt ? getEncryptionPubkey() : null,
|
|
5269
|
-
owner_wallet:
|
|
5270
|
-
}).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();
|
|
5271
6521
|
if (error) {
|
|
5272
6522
|
log11.error({ error: error.message }, "Failed to store memory");
|
|
5273
6523
|
return null;
|
|
@@ -5285,7 +6535,9 @@ async function storeMemory(opts) {
|
|
|
5285
6535
|
memoryType: opts.type,
|
|
5286
6536
|
source: opts.source
|
|
5287
6537
|
});
|
|
5288
|
-
commitMemoryToChain(data.id, opts).catch(
|
|
6538
|
+
commitMemoryToChain(data.id, opts, data).catch(
|
|
6539
|
+
(err) => log11.warn({ err }, "On-chain memory commit failed")
|
|
6540
|
+
);
|
|
5289
6541
|
embedMemory(data.id, opts).catch((err) => log11.warn({ err }, "Embedding generation failed"));
|
|
5290
6542
|
autoLinkMemory(data.id, opts).catch((err) => log11.warn({ err }, "Auto-linking failed"));
|
|
5291
6543
|
extractAndLinkEntitiesForMemory(data.id, opts).catch((err) => log11.debug({ err }, "Entity extraction failed"));
|
|
@@ -5295,9 +6547,19 @@ async function storeMemory(opts) {
|
|
|
5295
6547
|
return null;
|
|
5296
6548
|
}
|
|
5297
6549
|
}
|
|
5298
|
-
async function commitMemoryToChain(memoryId, opts) {
|
|
5299
|
-
if (
|
|
5300
|
-
const
|
|
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");
|
|
5301
6563
|
let signature = null;
|
|
5302
6564
|
if (isRegistryEnabled()) {
|
|
5303
6565
|
const encrypted = isEncryptionEnabled();
|
|
@@ -5310,22 +6572,61 @@ async function commitMemoryToChain(memoryId, opts) {
|
|
|
5310
6572
|
);
|
|
5311
6573
|
}
|
|
5312
6574
|
if (!signature) {
|
|
5313
|
-
const
|
|
5314
|
-
const memo = `clude:v1:sha256:${contentHashHex}`;
|
|
6575
|
+
const memo = `clude:v1:sha256:${canonicalHash}`;
|
|
5315
6576
|
signature = await writeMemo(memo);
|
|
5316
6577
|
}
|
|
5317
|
-
if (!signature) return;
|
|
5318
6578
|
const db = getDb();
|
|
5319
|
-
|
|
5320
|
-
|
|
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)");
|
|
5321
6595
|
}
|
|
5322
6596
|
async function embedMemory(memoryId, opts) {
|
|
5323
6597
|
if (!isEmbeddingEnabled()) return;
|
|
5324
6598
|
const db = getDb();
|
|
5325
6599
|
const embeddings = await generateEmbeddings([opts.summary]);
|
|
5326
6600
|
const summaryEmbedding = embeddings[0];
|
|
5327
|
-
if (summaryEmbedding) {
|
|
5328
|
-
|
|
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");
|
|
5329
6630
|
}
|
|
5330
6631
|
log11.debug({ memoryId }, "Memory embedded");
|
|
5331
6632
|
}
|
|
@@ -5528,9 +6829,9 @@ async function recallMemories(opts) {
|
|
|
5528
6829
|
}
|
|
5529
6830
|
if (candidates.length === 0) return [];
|
|
5530
6831
|
const linkPathMap = /* @__PURE__ */ new Map();
|
|
5531
|
-
const addLinkPath = (id,
|
|
6832
|
+
const addLinkPath = (id, path9) => {
|
|
5532
6833
|
if (!linkPathMap.has(id)) linkPathMap.set(id, /* @__PURE__ */ new Set());
|
|
5533
|
-
linkPathMap.get(id).add(
|
|
6834
|
+
linkPathMap.get(id).add(path9);
|
|
5534
6835
|
};
|
|
5535
6836
|
for (const id of vectorScores.keys()) addLinkPath(id, "vector");
|
|
5536
6837
|
for (const id of bm25Scores.keys()) addLinkPath(id, "bm25");
|
|
@@ -6047,25 +7348,31 @@ async function getMemoryStats() {
|
|
|
6047
7348
|
embeddedCount: 0
|
|
6048
7349
|
};
|
|
6049
7350
|
try {
|
|
6050
|
-
let countQuery = db.from("memories").select("id", { count: "exact", head: true })
|
|
7351
|
+
let countQuery = db.from("memories").select("id", { count: "exact", head: true });
|
|
6051
7352
|
countQuery = scopeToOwner(countQuery);
|
|
6052
7353
|
const { count: totalCount } = await countQuery;
|
|
6053
7354
|
stats.total = totalCount || 0;
|
|
6054
|
-
let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).
|
|
7355
|
+
let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).not("embedding", "is", null);
|
|
6055
7356
|
embeddedQuery = scopeToOwner(embeddedQuery);
|
|
6056
7357
|
const { count: embCount } = await embeddedQuery;
|
|
6057
7358
|
stats.embeddedCount = embCount || 0;
|
|
6058
|
-
const
|
|
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;
|
|
6059
7368
|
let allMemories = [];
|
|
6060
|
-
let page = 0;
|
|
6061
|
-
|
|
6062
|
-
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);
|
|
6063
7371
|
pageQuery = scopeToOwner(pageQuery);
|
|
6064
7372
|
const { data: pageData } = await pageQuery;
|
|
6065
7373
|
if (!pageData || pageData.length === 0) break;
|
|
6066
7374
|
allMemories = allMemories.concat(pageData);
|
|
6067
7375
|
if (pageData.length < PAGE_SIZE) break;
|
|
6068
|
-
page++;
|
|
6069
7376
|
}
|
|
6070
7377
|
if (allMemories.length > 0) {
|
|
6071
7378
|
let impSum = 0;
|
|
@@ -6074,8 +7381,6 @@ async function getMemoryStats() {
|
|
|
6074
7381
|
const conceptCounts = {};
|
|
6075
7382
|
const users = /* @__PURE__ */ new Set();
|
|
6076
7383
|
for (const m of allMemories) {
|
|
6077
|
-
const type = m.memory_type;
|
|
6078
|
-
if (type in stats.byType) stats.byType[type]++;
|
|
6079
7384
|
impSum += m.importance;
|
|
6080
7385
|
decaySum += m.decay_factor;
|
|
6081
7386
|
if (m.related_user) users.add(m.related_user);
|
|
@@ -6263,7 +7568,7 @@ function moodToValence(mood) {
|
|
|
6263
7568
|
return 0;
|
|
6264
7569
|
}
|
|
6265
7570
|
}
|
|
6266
|
-
var import_crypto3, 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;
|
|
6267
7572
|
var init_memory = __esm({
|
|
6268
7573
|
"packages/brain/src/memory/memory.ts"() {
|
|
6269
7574
|
"use strict";
|
|
@@ -6272,7 +7577,9 @@ var init_memory = __esm({
|
|
|
6272
7577
|
init_utils();
|
|
6273
7578
|
init_claude_client();
|
|
6274
7579
|
init_solana_client();
|
|
7580
|
+
init_src2();
|
|
6275
7581
|
init_embeddings();
|
|
7582
|
+
init_wiki_packs();
|
|
6276
7583
|
init_config2();
|
|
6277
7584
|
init_bm25_search();
|
|
6278
7585
|
init_openrouter_client();
|
|
@@ -6289,6 +7596,16 @@ var init_memory = __esm({
|
|
|
6289
7596
|
log11 = createChildLogger("memory");
|
|
6290
7597
|
DEDUP_TTL_MS = 10 * 60 * 1e3;
|
|
6291
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
|
+
]);
|
|
6292
7609
|
STOPWORDS = /* @__PURE__ */ new Set([
|
|
6293
7610
|
"the",
|
|
6294
7611
|
"a",
|
|
@@ -6394,6 +7711,7 @@ __export(memory_exports2, {
|
|
|
6394
7711
|
getSelfModel: () => getSelfModel,
|
|
6395
7712
|
hydrateMemories: () => hydrateMemories,
|
|
6396
7713
|
inferConcepts: () => inferConcepts,
|
|
7714
|
+
invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
|
|
6397
7715
|
isValidHashId: () => isValidHashId,
|
|
6398
7716
|
listMemories: () => listMemories,
|
|
6399
7717
|
moodToValence: () => moodToValence,
|
|
@@ -6778,7 +8096,7 @@ async function runExport() {
|
|
|
6778
8096
|
writeMemoryPack(outputDir, records, {
|
|
6779
8097
|
producer: {
|
|
6780
8098
|
name: "clude",
|
|
6781
|
-
version: "3.0
|
|
8099
|
+
version: "3.1.0",
|
|
6782
8100
|
agent_id: config3?.agent?.id,
|
|
6783
8101
|
public_key: publicKey
|
|
6784
8102
|
},
|
|
@@ -6851,7 +8169,7 @@ var init_export = __esm({
|
|
|
6851
8169
|
"use strict";
|
|
6852
8170
|
import_fs5 = require("fs");
|
|
6853
8171
|
init_banner();
|
|
6854
|
-
|
|
8172
|
+
init_src();
|
|
6855
8173
|
}
|
|
6856
8174
|
});
|
|
6857
8175
|
|
|
@@ -6863,9 +8181,9 @@ __export(import_exports, {
|
|
|
6863
8181
|
function hasFlag2(args, flag) {
|
|
6864
8182
|
return args.includes(flag);
|
|
6865
8183
|
}
|
|
6866
|
-
function isMemoryPackDir(
|
|
8184
|
+
function isMemoryPackDir(path9) {
|
|
6867
8185
|
try {
|
|
6868
|
-
return (0, import_fs6.statSync)(
|
|
8186
|
+
return (0, import_fs6.statSync)(path9).isDirectory() && (0, import_fs6.existsSync)((0, import_path6.join)(path9, "manifest.json"));
|
|
6869
8187
|
} catch {
|
|
6870
8188
|
return false;
|
|
6871
8189
|
}
|
|
@@ -6889,19 +8207,19 @@ function parseMemoryPackDir(dir) {
|
|
|
6889
8207
|
source: r.source || "memorypack"
|
|
6890
8208
|
}));
|
|
6891
8209
|
}
|
|
6892
|
-
function isZipFile(
|
|
8210
|
+
function isZipFile(path9) {
|
|
6893
8211
|
try {
|
|
6894
|
-
const buf = (0, import_fs6.readFileSync)(
|
|
8212
|
+
const buf = (0, import_fs6.readFileSync)(path9);
|
|
6895
8213
|
return buf[0] === 80 && buf[1] === 75;
|
|
6896
8214
|
} catch {
|
|
6897
8215
|
return false;
|
|
6898
8216
|
}
|
|
6899
8217
|
}
|
|
6900
|
-
function isJsonFile(
|
|
6901
|
-
return
|
|
8218
|
+
function isJsonFile(path9) {
|
|
8219
|
+
return path9.endsWith(".json");
|
|
6902
8220
|
}
|
|
6903
|
-
function isMarkdownOrText(
|
|
6904
|
-
return
|
|
8221
|
+
function isMarkdownOrText(path9) {
|
|
8222
|
+
return path9.endsWith(".md") || path9.endsWith(".txt") || path9.endsWith(".markdown");
|
|
6905
8223
|
}
|
|
6906
8224
|
function isSubstantive(text) {
|
|
6907
8225
|
if (!text || text.length < 20) return false;
|
|
@@ -7018,18 +8336,18 @@ function extractFromChatGPTConversations(conversations) {
|
|
|
7018
8336
|
}
|
|
7019
8337
|
async function parseChatGPTZip(filePath) {
|
|
7020
8338
|
const { execSync: execSync2 } = require("child_process");
|
|
7021
|
-
const { mkdtempSync, readdirSync:
|
|
7022
|
-
const { join:
|
|
7023
|
-
const
|
|
7024
|
-
const tmpDir =
|
|
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-"));
|
|
7025
8343
|
try {
|
|
7026
8344
|
execSync2(`unzip -o -q "${filePath}" -d "${tmpDir}"`);
|
|
7027
8345
|
} catch {
|
|
7028
8346
|
printError('Failed to unzip file. Make sure "unzip" is installed.');
|
|
7029
8347
|
process.exit(1);
|
|
7030
8348
|
}
|
|
7031
|
-
const files =
|
|
7032
|
-
const convFile = files.find((f) => f === "conversations.json") ?
|
|
8349
|
+
const files = readdirSync3(tmpDir);
|
|
8350
|
+
const convFile = files.find((f) => f === "conversations.json") ? join13(tmpDir, "conversations.json") : null;
|
|
7033
8351
|
if (!convFile) {
|
|
7034
8352
|
const { execSync: exec2 } = require("child_process");
|
|
7035
8353
|
try {
|
|
@@ -7272,7 +8590,7 @@ var init_import = __esm({
|
|
|
7272
8590
|
import_fs6 = require("fs");
|
|
7273
8591
|
import_path6 = require("path");
|
|
7274
8592
|
init_banner();
|
|
7275
|
-
|
|
8593
|
+
init_src();
|
|
7276
8594
|
}
|
|
7277
8595
|
});
|
|
7278
8596
|
|
|
@@ -7423,13 +8741,13 @@ async function runInjectInstructions() {
|
|
|
7423
8741
|
const forceAgents = args.includes("--agents");
|
|
7424
8742
|
const forceClaude = args.includes("--claude");
|
|
7425
8743
|
const dirFlag = args.find((a) => a.startsWith("--dir="));
|
|
7426
|
-
const dir = dirFlag ?
|
|
8744
|
+
const dir = dirFlag ? path6.resolve(dirFlag.slice(6)) : process.cwd();
|
|
7427
8745
|
let useAgentsmd = forceAgents;
|
|
7428
8746
|
if (!forceAgents && !forceClaude) {
|
|
7429
|
-
useAgentsmd =
|
|
8747
|
+
useAgentsmd = fs4.existsSync(path6.join(dir, "AGENTS.md")) || fs4.existsSync(path6.join(dir, "agents"));
|
|
7430
8748
|
}
|
|
7431
8749
|
const targetFile = useAgentsmd ? "AGENTS.md" : "CLAUDE.md";
|
|
7432
|
-
printInfo(`Target: ${
|
|
8750
|
+
printInfo(`Target: ${path6.join(dir, targetFile)}`);
|
|
7433
8751
|
console.log("");
|
|
7434
8752
|
const result = injectInstructions(dir, { agentsmd: useAgentsmd });
|
|
7435
8753
|
if (result) {
|
|
@@ -7448,12 +8766,12 @@ async function runInjectInstructions() {
|
|
|
7448
8766
|
printDivider();
|
|
7449
8767
|
console.log("");
|
|
7450
8768
|
}
|
|
7451
|
-
var
|
|
8769
|
+
var path6, fs4;
|
|
7452
8770
|
var init_inject_instructions = __esm({
|
|
7453
8771
|
"packages/brain/src/cli/inject-instructions.ts"() {
|
|
7454
8772
|
"use strict";
|
|
7455
|
-
|
|
7456
|
-
|
|
8773
|
+
path6 = __toESM(require("path"));
|
|
8774
|
+
fs4 = __toESM(require("fs"));
|
|
7457
8775
|
init_banner();
|
|
7458
8776
|
init_setup();
|
|
7459
8777
|
}
|
|
@@ -7466,13 +8784,13 @@ __export(doctor_exports, {
|
|
|
7466
8784
|
});
|
|
7467
8785
|
async function runDoctor() {
|
|
7468
8786
|
console.log("\n CLUDE DIAGNOSTICS\n");
|
|
7469
|
-
const dbPath =
|
|
7470
|
-
if (
|
|
8787
|
+
const dbPath = path7.join(os4.homedir(), ".clude", "brain.db");
|
|
8788
|
+
if (fs5.existsSync(dbPath)) {
|
|
7471
8789
|
try {
|
|
7472
8790
|
const Database2 = require("better-sqlite3");
|
|
7473
8791
|
const db = new Database2(dbPath, { readonly: true });
|
|
7474
8792
|
const count = db.prepare("SELECT COUNT(*) as c FROM memories").get().c;
|
|
7475
|
-
const stats =
|
|
8793
|
+
const stats = fs5.statSync(dbPath);
|
|
7476
8794
|
const sizeMB = (stats.size / 1024 / 1024).toFixed(1);
|
|
7477
8795
|
console.log(` \u2713 Database ${dbPath} (${count} memories, ${sizeMB} MB)`);
|
|
7478
8796
|
db.close();
|
|
@@ -7482,15 +8800,15 @@ async function runDoctor() {
|
|
|
7482
8800
|
} else {
|
|
7483
8801
|
console.log(` \u2717 Database Not found (run 'npx @clude/sdk setup')`);
|
|
7484
8802
|
}
|
|
7485
|
-
const modelDir =
|
|
7486
|
-
const modelCached =
|
|
8803
|
+
const modelDir = path7.join(os4.homedir(), ".clude", "models");
|
|
8804
|
+
const modelCached = fs5.existsSync(modelDir) && fs5.readdirSync(modelDir).length > 0;
|
|
7487
8805
|
console.log(
|
|
7488
8806
|
modelCached ? " \u2713 Embeddings all-MiniLM-L6-v2 (cached)" : " \u2717 Embeddings Model not cached (will download on first use)"
|
|
7489
8807
|
);
|
|
7490
|
-
const configPath =
|
|
7491
|
-
if (
|
|
8808
|
+
const configPath = path7.join(os4.homedir(), ".clude", "config.json");
|
|
8809
|
+
if (fs5.existsSync(configPath)) {
|
|
7492
8810
|
try {
|
|
7493
|
-
const config3 = JSON.parse(
|
|
8811
|
+
const config3 = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
|
|
7494
8812
|
console.log(
|
|
7495
8813
|
config3.apiKey ? ` \u2713 Cloud ${config3.apiKey.slice(0, 8)}... (configured)` : " \u2717 Cloud No API key configured"
|
|
7496
8814
|
);
|
|
@@ -7513,13 +8831,13 @@ async function runDoctor() {
|
|
|
7513
8831
|
console.log(" \u2717 Ollama Not running");
|
|
7514
8832
|
}
|
|
7515
8833
|
const mcpPaths = [
|
|
7516
|
-
{ name: "Claude Code", path:
|
|
7517
|
-
{ name: "Cursor", path:
|
|
8834
|
+
{ name: "Claude Code", path: path7.join(os4.homedir(), ".claude", ".mcp.json") },
|
|
8835
|
+
{ name: "Cursor", path: path7.join(os4.homedir(), ".cursor", "mcp.json") }
|
|
7518
8836
|
];
|
|
7519
8837
|
for (const mcp of mcpPaths) {
|
|
7520
|
-
if (
|
|
8838
|
+
if (fs5.existsSync(mcp.path)) {
|
|
7521
8839
|
try {
|
|
7522
|
-
const config3 = JSON.parse(
|
|
8840
|
+
const config3 = JSON.parse(fs5.readFileSync(mcp.path, "utf-8"));
|
|
7523
8841
|
const hasClude = config3.mcpServers?.["clude-memory"] !== void 0;
|
|
7524
8842
|
console.log(
|
|
7525
8843
|
hasClude ? ` \u2713 MCP ${mcp.name}` : ` - MCP ${mcp.name} (no clude-memory entry)`
|
|
@@ -7531,7 +8849,7 @@ async function runDoctor() {
|
|
|
7531
8849
|
console.log(` \u2717 MCP ${mcp.name} (not detected)`);
|
|
7532
8850
|
}
|
|
7533
8851
|
}
|
|
7534
|
-
if (
|
|
8852
|
+
if (fs5.existsSync(dbPath)) {
|
|
7535
8853
|
try {
|
|
7536
8854
|
const Database2 = require("better-sqlite3");
|
|
7537
8855
|
const db = new Database2(dbPath, { readonly: true });
|
|
@@ -7543,13 +8861,13 @@ async function runDoctor() {
|
|
|
7543
8861
|
}
|
|
7544
8862
|
console.log("");
|
|
7545
8863
|
}
|
|
7546
|
-
var
|
|
8864
|
+
var path7, os4, fs5;
|
|
7547
8865
|
var init_doctor = __esm({
|
|
7548
8866
|
"packages/brain/src/cli/doctor.ts"() {
|
|
7549
8867
|
"use strict";
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
8868
|
+
path7 = __toESM(require("path"));
|
|
8869
|
+
os4 = __toESM(require("os"));
|
|
8870
|
+
fs5 = __toESM(require("fs"));
|
|
7553
8871
|
}
|
|
7554
8872
|
});
|
|
7555
8873
|
|
|
@@ -7819,8 +9137,8 @@ __export(dream_exports, {
|
|
|
7819
9137
|
});
|
|
7820
9138
|
async function runDream() {
|
|
7821
9139
|
console.log("\n DREAM CYCLE\n");
|
|
7822
|
-
const dbPath =
|
|
7823
|
-
if (!
|
|
9140
|
+
const dbPath = path8.join(os5.homedir(), ".clude", "brain.db");
|
|
9141
|
+
if (!fs6.existsSync(dbPath)) {
|
|
7824
9142
|
console.log(' \u2717 No database found. Run "npx @clude/sdk setup" first.\n');
|
|
7825
9143
|
return;
|
|
7826
9144
|
}
|
|
@@ -7850,13 +9168,300 @@ async function runDream() {
|
|
|
7850
9168
|
store.close();
|
|
7851
9169
|
console.log("\n Dream cycle complete.\n");
|
|
7852
9170
|
}
|
|
7853
|
-
var
|
|
9171
|
+
var path8, os5, fs6;
|
|
7854
9172
|
var init_dream = __esm({
|
|
7855
9173
|
"packages/brain/src/cli/dream.ts"() {
|
|
7856
9174
|
"use strict";
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
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();
|
|
7860
9465
|
}
|
|
7861
9466
|
});
|
|
7862
9467
|
|
|
@@ -8050,23 +9655,23 @@ var init_clinamen = __esm({
|
|
|
8050
9655
|
var server_exports = {};
|
|
8051
9656
|
async function getSqliteStore() {
|
|
8052
9657
|
if (_sqliteStore) return _sqliteStore;
|
|
8053
|
-
const
|
|
8054
|
-
const
|
|
8055
|
-
const
|
|
9658
|
+
const path9 = require("path");
|
|
9659
|
+
const os6 = require("os");
|
|
9660
|
+
const fs7 = require("fs");
|
|
8056
9661
|
const { SqliteStore: SqliteStore2 } = (init_sqlite_store(), __toCommonJS(sqlite_store_exports));
|
|
8057
9662
|
const { LocalEmbedder: LocalEmbedder2 } = (init_embedder(), __toCommonJS(embedder_exports));
|
|
8058
|
-
const dbDir =
|
|
8059
|
-
|
|
9663
|
+
const dbDir = path9.join(os6.homedir(), ".clude");
|
|
9664
|
+
fs7.mkdirSync(dbDir, { recursive: true });
|
|
8060
9665
|
const embedder = new LocalEmbedder2();
|
|
8061
9666
|
const store = new SqliteStore2({
|
|
8062
|
-
dbPath:
|
|
9667
|
+
dbPath: path9.join(dbDir, "brain.db"),
|
|
8063
9668
|
embedder
|
|
8064
9669
|
});
|
|
8065
9670
|
_sqliteStore = store;
|
|
8066
9671
|
return store;
|
|
8067
9672
|
}
|
|
8068
|
-
async function cortexFetch(method,
|
|
8069
|
-
const url = `${CORTEX_HOST_URL}${
|
|
9673
|
+
async function cortexFetch(method, path9, body) {
|
|
9674
|
+
const url = `${CORTEX_HOST_URL}${path9}`;
|
|
8070
9675
|
const controller = new AbortController();
|
|
8071
9676
|
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
8072
9677
|
try {
|
|
@@ -8086,7 +9691,7 @@ async function cortexFetch(method, path8, body) {
|
|
|
8086
9691
|
return await res.json();
|
|
8087
9692
|
} catch (err) {
|
|
8088
9693
|
if (err.name === "AbortError") {
|
|
8089
|
-
throw new Error(`Cortex API timeout after ${FETCH_TIMEOUT_MS / 1e3}s: ${method} ${
|
|
9694
|
+
throw new Error(`Cortex API timeout after ${FETCH_TIMEOUT_MS / 1e3}s: ${method} ${path9}`);
|
|
8090
9695
|
}
|
|
8091
9696
|
throw err;
|
|
8092
9697
|
} finally {
|
|
@@ -8962,7 +10567,9 @@ var require_package = __commonJS({
|
|
|
8962
10567
|
"@ai-sdk/openai": "^3.0.49",
|
|
8963
10568
|
"@ai-sdk/xai": "^3.0.75",
|
|
8964
10569
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
10570
|
+
"@clude/memorypack": "workspace:*",
|
|
8965
10571
|
"@clude/shared": "workspace:*",
|
|
10572
|
+
"@clude/tokenization": "workspace:*",
|
|
8966
10573
|
"@huggingface/transformers": "^4.1.0",
|
|
8967
10574
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
8968
10575
|
"@openrouter/ai-sdk-provider": "^2.3.3",
|
|
@@ -9019,6 +10626,12 @@ if (command === "setup") {
|
|
|
9019
10626
|
console.error("MCP install failed:", err.message);
|
|
9020
10627
|
process.exit(1);
|
|
9021
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
|
+
});
|
|
9022
10635
|
} else if (command === "ship") {
|
|
9023
10636
|
const message = process.argv.slice(3).join(" ");
|
|
9024
10637
|
const { runShip: runShip2 } = (init_ship(), __toCommonJS(ship_exports));
|
|
@@ -9068,6 +10681,28 @@ if (command === "setup") {
|
|
|
9068
10681
|
console.error("Dream failed:", err.message);
|
|
9069
10682
|
process.exit(1);
|
|
9070
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
|
+
}
|
|
9071
10706
|
} else if (command === "mcp-serve") {
|
|
9072
10707
|
init_server();
|
|
9073
10708
|
} else if (command === "start" || command === "bot") {
|
|
@@ -9089,10 +10724,13 @@ if (command === "setup") {
|
|
|
9089
10724
|
console.log(` ${c2.cyan}npx @clude/sdk status${c2.reset} Check if Clude is active + memory stats`);
|
|
9090
10725
|
console.log(` ${c2.cyan}npx @clude/sdk init${c2.reset} Advanced setup (self-hosted options)`);
|
|
9091
10726
|
console.log(` ${c2.cyan}npx @clude/sdk register${c2.reset} Get an API key only`);
|
|
9092
|
-
console.log(` ${c2.cyan}npx @clude/sdk
|
|
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)`);
|
|
9093
10729
|
console.log(` ${c2.cyan}npx @clude/sdk inject-instructions${c2.reset} Write usage instructions to CLAUDE.md`);
|
|
9094
|
-
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)`);
|
|
9095
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`);
|
|
9096
10734
|
console.log(` ${c2.cyan}npx @clude/sdk sync${c2.reset} Auto-update system prompt file`);
|
|
9097
10735
|
console.log(` ${c2.cyan}npx @clude/sdk ship "msg"${c2.reset} Broadcast to Telegram channel`);
|
|
9098
10736
|
console.log(` ${c2.cyan}npx @clude/sdk doctor${c2.reset} Run diagnostics`);
|