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