indelible-mcp 4.9.3 → 4.9.5

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +457 -123
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indelible-mcp",
3
- "version": "4.9.3",
3
+ "version": "4.9.5",
4
4
  "description": "Blockchain-backed memory and code storage for Claude Code. Save AI conversations and source code permanently on BSV.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -14,7 +14,7 @@ var __export = (target, all) => {
14
14
  __defProp(target, name, { get: all[name], enumerable: true });
15
15
  };
16
16
 
17
- // lib/crypto.js
17
+ // mcp-server/lib/crypto.js
18
18
  import { createCipheriv, createDecipheriv, createHash, randomBytes, pbkdf2Sync } from "crypto";
19
19
  function deriveKey(wif) {
20
20
  return createHash("sha256").update(wif).digest();
@@ -69,11 +69,20 @@ function decryptWif(encryptedObj, pin) {
69
69
  return decrypted;
70
70
  }
71
71
  var init_crypto = __esm({
72
- "lib/crypto.js"() {
72
+ "mcp-server/lib/crypto.js"() {
73
73
  }
74
74
  });
75
75
 
76
- // lib/config.customer.js
76
+ // mcp-server/lib/config.customer.js
77
+ var config_customer_exports = {};
78
+ __export(config_customer_exports, {
79
+ getConfigPath: () => getConfigPath,
80
+ getWif: () => getWif,
81
+ hasWallet: () => hasWallet,
82
+ loadConfig: () => loadConfig,
83
+ saveConfig: () => saveConfig,
84
+ updateConfig: () => updateConfig
85
+ });
77
86
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
78
87
  import { homedir } from "node:os";
79
88
  import { join } from "node:path";
@@ -98,6 +107,10 @@ function updateConfig(mutator) {
98
107
  saveConfig(next);
99
108
  return next;
100
109
  }
110
+ function hasWallet() {
111
+ const config = loadConfig();
112
+ return config?.wif_encrypted != null || config?.wif != null;
113
+ }
101
114
  async function getWif() {
102
115
  if (_cachedWif) return _cachedWif;
103
116
  const config = loadConfig();
@@ -124,7 +137,7 @@ async function getWif() {
124
137
  }
125
138
  var CONFIG_DIR, CONFIG_FILE, _cachedWif;
126
139
  var init_config_customer = __esm({
127
- "lib/config.customer.js"() {
140
+ "mcp-server/lib/config.customer.js"() {
128
141
  init_crypto();
129
142
  CONFIG_DIR = join(homedir(), ".indelible");
130
143
  CONFIG_FILE = join(CONFIG_DIR, "config.json");
@@ -132,7 +145,7 @@ var init_config_customer = __esm({
132
145
  }
133
146
  });
134
147
 
135
- // lib/spv.js
148
+ // mcp-server/lib/spv.js
136
149
  import { Transaction, P2PKH, PrivateKey, SatoshisPerKilobyte, LockingScript, OP } from "@bsv/sdk";
137
150
  import { Transaction as Transaction2, P2PKH as P2PKH2, PrivateKey as PrivateKey2, SatoshisPerKilobyte as SatoshisPerKilobyte2, LockingScript as LockingScript2, OP as OP2 } from "@bsv/sdk";
138
151
  async function getBridges() {
@@ -545,7 +558,7 @@ async function buildPaymentTx(wif, utxos, payToAddress, satoshis) {
545
558
  }
546
559
  var SEED_BRIDGES, OLD_BRIDGE_IPS, bridgeHealth, HEALTH_CHECK_INTERVAL, MAX_FAILURES, migrationDone, _stickyBridge, STICKY_WINDOW_MS;
547
560
  var init_spv = __esm({
548
- "lib/spv.js"() {
561
+ "mcp-server/lib/spv.js"() {
549
562
  init_config_customer();
550
563
  SEED_BRIDGES = [
551
564
  { url: "http://144.202.48.217:9333", name: "bridge-alpha" },
@@ -573,7 +586,7 @@ var init_spv = __esm({
573
586
  }
574
587
  });
575
588
 
576
- // lib/utxo-cache.js
589
+ // mcp-server/lib/utxo-cache.js
577
590
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
578
591
  import { join as join2 } from "path";
579
592
  import { homedir as homedir2 } from "os";
@@ -624,13 +637,13 @@ function invalidate(address) {
624
637
  }
625
638
  var DIR, CACHE_PATH;
626
639
  var init_utxo_cache = __esm({
627
- "lib/utxo-cache.js"() {
640
+ "mcp-server/lib/utxo-cache.js"() {
628
641
  DIR = join2(homedir2(), ".indelible");
629
642
  CACHE_PATH = process.env.INDELIBLE_UTXO_CACHE || join2(DIR, "utxo-cache.json");
630
643
  }
631
644
  });
632
645
 
633
- // lib/summary.js
646
+ // mcp-server/lib/summary.js
634
647
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
635
648
  function encryptSummary(plaintext, wif, txId) {
636
649
  if (!txId || typeof txId !== "string" || !TXID_REGEX.test(txId)) {
@@ -679,7 +692,7 @@ function decryptSummaryOrLock(summary_enc, wif, expectedTxId) {
679
692
  }
680
693
  var TXID_REGEX, LOCK_PLACEHOLDER;
681
694
  var init_summary = __esm({
682
- "lib/summary.js"() {
695
+ "mcp-server/lib/summary.js"() {
683
696
  init_crypto();
684
697
  TXID_REGEX = /^[0-9a-f]{64}$/i;
685
698
  LOCK_PLACEHOLDER = {
@@ -692,7 +705,7 @@ var init_summary = __esm({
692
705
  }
693
706
  });
694
707
 
695
- // lib/wallet-brc100.js
708
+ // mcp-server/lib/wallet-brc100.js
696
709
  import { PrivateKey as PrivateKey3, CompletedProtoWallet, AuthFetch } from "@bsv/sdk";
697
710
  function createAuthClient(wif) {
698
711
  if (!wif) throw new Error("createAuthClient: wif required");
@@ -712,13 +725,13 @@ function createAuthClient(wif) {
712
725
  }
713
726
  var cached, cachedWif;
714
727
  var init_wallet_brc100 = __esm({
715
- "lib/wallet-brc100.js"() {
728
+ "mcp-server/lib/wallet-brc100.js"() {
716
729
  cached = null;
717
730
  cachedWif = null;
718
731
  }
719
732
  });
720
733
 
721
- // lib/api-client.js
734
+ // mcp-server/lib/api-client.js
722
735
  var api_client_exports = {};
723
736
  __export(api_client_exports, {
724
737
  buildReceipt: () => buildReceipt,
@@ -785,7 +798,7 @@ async function checkTier(apiKey) {
785
798
  });
786
799
  if (res.status === 429) {
787
800
  const data = await res.json().catch(() => ({}));
788
- throw new Error(`Monthly save limit reached (${data.limit || 50}). Upgrade at indelible.one/pricing`);
801
+ throw new Error(`Save limit reached${data.limit ? ` (${data.limit})` : ""}. See indelible.one/pricing`);
789
802
  }
790
803
  if (res.ok) {
791
804
  const data = await res.json();
@@ -1100,7 +1113,7 @@ async function checkConnection() {
1100
1113
  }
1101
1114
  var DEFAULT_API_URL, TIER2_CACHE_TTL_MS, TIER2_BACKOFF_TTL_MS, TIER2_BACKOFF_THRESHOLD, tier2Cache, tier2TransientFailures;
1102
1115
  var init_api_client = __esm({
1103
- "lib/api-client.js"() {
1116
+ "mcp-server/lib/api-client.js"() {
1104
1117
  init_config_customer();
1105
1118
  init_spv();
1106
1119
  init_utxo_cache();
@@ -1115,7 +1128,7 @@ var init_api_client = __esm({
1115
1128
  }
1116
1129
  });
1117
1130
 
1118
- // lib/share.js
1131
+ // mcp-server/lib/share.js
1119
1132
  import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, createHash as createHash2, randomBytes as randomBytes3, hkdfSync } from "node:crypto";
1120
1133
  import { PrivateKey as PrivateKey5, PublicKey, Signature, ProtoWallet } from "@bsv/sdk";
1121
1134
  function isValidP2PKH(addr) {
@@ -1415,7 +1428,7 @@ async function unwrapRecoveryV3(wrapHex, wallet, keyID, ownerPubkeyHex) {
1415
1428
  }
1416
1429
  var HKDF_INFO, TXID_REGEX2, P2PKH_REGEX, CANONICAL_FIELDS, SELF_WRAP_TXID, ENCRYPT_PROTOCOL_ID_V2;
1417
1430
  var init_share = __esm({
1418
- "lib/share.js"() {
1431
+ "mcp-server/lib/share.js"() {
1419
1432
  HKDF_INFO = "indelible-share-v1";
1420
1433
  TXID_REGEX2 = /^[0-9a-f]{64}$/i;
1421
1434
  P2PKH_REGEX = /^1[1-9A-HJ-NP-Za-km-z]{25,34}$/;
@@ -1433,7 +1446,7 @@ var init_share = __esm({
1433
1446
  }
1434
1447
  });
1435
1448
 
1436
- // lib/save-log.js
1449
+ // mcp-server/lib/save-log.js
1437
1450
  import { appendFileSync, readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
1438
1451
  import { join as join3 } from "path";
1439
1452
  import { homedir as homedir3 } from "os";
@@ -1470,13 +1483,13 @@ function appendReceipt(receipt, meta = {}) {
1470
1483
  }
1471
1484
  var DIR2, LOG;
1472
1485
  var init_save_log = __esm({
1473
- "lib/save-log.js"() {
1486
+ "mcp-server/lib/save-log.js"() {
1474
1487
  DIR2 = join3(homedir3(), ".indelible");
1475
1488
  LOG = join3(DIR2, "save-log.jsonl");
1476
1489
  }
1477
1490
  });
1478
1491
 
1479
- // lib/advisory/index.customer.js
1492
+ // mcp-server/lib/advisory/index.customer.js
1480
1493
  function extractAnchors() {
1481
1494
  return null;
1482
1495
  }
@@ -1521,11 +1534,11 @@ async function pollInfraEmotions() {
1521
1534
  return null;
1522
1535
  }
1523
1536
  var init_index_customer = __esm({
1524
- "lib/advisory/index.customer.js"() {
1537
+ "mcp-server/lib/advisory/index.customer.js"() {
1525
1538
  }
1526
1539
  });
1527
1540
 
1528
- // build/wall-stub.js
1541
+ // mcp-server/build/wall-stub.js
1529
1542
  var wall_stub_exports = {};
1530
1543
  __export(wall_stub_exports, {
1531
1544
  adversarialReview: () => adversarialReview,
@@ -1546,7 +1559,7 @@ __export(wall_stub_exports, {
1546
1559
  });
1547
1560
  var asyncEmpty, calibrate, adversarialReview, bookkeeperAudit, quartermasterBrief, treasuryStatus, checkAsRyan, auditChainGoalState2, buildAndWriteFederationChat, readRecentEvents, dispatchCloneCheck, cloneCheck2, logAgentVerdict2, escalateVerdict, announce, wall_stub_default;
1548
1561
  var init_wall_stub = __esm({
1549
- "build/wall-stub.js"() {
1562
+ "mcp-server/build/wall-stub.js"() {
1550
1563
  asyncEmpty = async () => ({});
1551
1564
  calibrate = asyncEmpty;
1552
1565
  adversarialReview = asyncEmpty;
@@ -1569,7 +1582,7 @@ var init_wall_stub = __esm({
1569
1582
  }
1570
1583
  });
1571
1584
 
1572
- // tools/save_style.js
1585
+ // mcp-server/tools/save_style.js
1573
1586
  import { readFile as readFile3 } from "fs/promises";
1574
1587
  import { existsSync as existsSync6 } from "fs";
1575
1588
  function stripCoreRules(rulesText) {
@@ -1665,7 +1678,7 @@ async function saveStyleFromFile(filePath, styleName, description) {
1665
1678
  }
1666
1679
  var CORE_RULES_MARKER, CORE_RULES;
1667
1680
  var init_save_style = __esm({
1668
- "tools/save_style.js"() {
1681
+ "mcp-server/tools/save_style.js"() {
1669
1682
  init_config_customer();
1670
1683
  init_crypto();
1671
1684
  init_spv();
@@ -1729,7 +1742,7 @@ Handbooks live in the project root \u2014 check MEMORY.md for paths.
1729
1742
  }
1730
1743
  });
1731
1744
 
1732
- // tools/load_style.js
1745
+ // mcp-server/tools/load_style.js
1733
1746
  import { Transaction as Transaction4 } from "@bsv/sdk";
1734
1747
  import { writeFileSync as writeFileSync4 } from "fs";
1735
1748
  import { join as join6 } from "path";
@@ -1836,7 +1849,7 @@ async function loadStyle(txId) {
1836
1849
  }
1837
1850
  var SYNCABLE_FIELDS, API_TIMEOUT_MS;
1838
1851
  var init_load_style = __esm({
1839
- "tools/load_style.js"() {
1852
+ "mcp-server/tools/load_style.js"() {
1840
1853
  init_config_customer();
1841
1854
  init_crypto();
1842
1855
  init_spv();
@@ -1846,7 +1859,7 @@ var init_load_style = __esm({
1846
1859
  }
1847
1860
  });
1848
1861
 
1849
- // ../scripts/lib/claim-status.mjs
1862
+ // scripts/lib/claim-status.mjs
1850
1863
  import { hostname } from "os";
1851
1864
  function pidAlive(pid) {
1852
1865
  try {
@@ -1885,12 +1898,12 @@ function claimStatus(g) {
1885
1898
  }
1886
1899
  var CLAIM_STALE_MS;
1887
1900
  var init_claim_status = __esm({
1888
- "../scripts/lib/claim-status.mjs"() {
1901
+ "scripts/lib/claim-status.mjs"() {
1889
1902
  CLAIM_STALE_MS = 30 * 60 * 1e3;
1890
1903
  }
1891
1904
  });
1892
1905
 
1893
- // lib/tenant-paths.js
1906
+ // mcp-server/lib/tenant-paths.js
1894
1907
  import { homedir as homedir7 } from "os";
1895
1908
  import { join as join7 } from "path";
1896
1909
  function base(tenantId) {
@@ -1907,14 +1920,14 @@ function base(tenantId) {
1907
1920
  }
1908
1921
  var goalsDir, goalsPath, innerStatePath;
1909
1922
  var init_tenant_paths = __esm({
1910
- "lib/tenant-paths.js"() {
1923
+ "mcp-server/lib/tenant-paths.js"() {
1911
1924
  goalsDir = (t) => base(t);
1912
1925
  goalsPath = (t) => join7(base(t), "goals.json");
1913
1926
  innerStatePath = (t) => join7(base(t), "inner-state.json");
1914
1927
  }
1915
1928
  });
1916
1929
 
1917
- // tools/goals.js
1930
+ // mcp-server/tools/goals.js
1918
1931
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4, unlinkSync, openSync, closeSync, renameSync } from "fs";
1919
1932
  import { join as join8 } from "path";
1920
1933
  import { homedir as homedir8, hostname as hostname2 } from "os";
@@ -2038,8 +2051,37 @@ function sanitize(s) {
2038
2051
  const truncated = leak ? s.slice(0, leak.index) : s;
2039
2052
  return truncated.replace(LEAKED_TAG_RE, "").replace(/\n{3,}/g, "\n\n").trim();
2040
2053
  }
2054
+ function pushGoalsToServer(data, { always = false, snapshot = null } = {}) {
2055
+ if (!always && !(data?.goals?.length || data?.completed?.length)) return;
2056
+ (async () => {
2057
+ try {
2058
+ const { loadConfig: loadConfig2, ensureApiKey } = await Promise.resolve().then(() => (init_config_customer(), config_customer_exports));
2059
+ const cfg = await loadConfig2();
2060
+ const apiKey = cfg?.api_key?.startsWith?.("indelible_sk_") ? cfg.api_key : await ensureApiKey();
2061
+ if (!apiKey) return;
2062
+ const base2 = (cfg.api_url || "https://indelible.one").replace(/\/$/, "");
2063
+ const snap = snapshot || (cfg.goals_snapshot_txid ? {
2064
+ txid: cfg.goals_snapshot_txid,
2065
+ at: cfg.goals_snapshot_at || null,
2066
+ count: cfg.goals_snapshot_count ?? null,
2067
+ completed_count: cfg.goals_snapshot_completed_count ?? null
2068
+ } : null);
2069
+ const ctl = new AbortController();
2070
+ const t = setTimeout(() => ctl.abort(), 2500);
2071
+ await fetch(`${base2}/api/consciousness/sync`, {
2072
+ method: "POST",
2073
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
2074
+ body: JSON.stringify({ type: "goals", data: { version: data.version || 1, goals: data.goals || [], completed: data.completed || [], snapshot: snap } }),
2075
+ signal: ctl.signal
2076
+ });
2077
+ clearTimeout(t);
2078
+ } catch {
2079
+ }
2080
+ })();
2081
+ }
2041
2082
  async function getGoals() {
2042
2083
  const data = loadGoals();
2084
+ pushGoalsToServer(data);
2043
2085
  const active = data.goals.filter((g) => g.status === "active").sort((a, b) => (b.priority || 0) - (a.priority || 0));
2044
2086
  const proposed = data.goals.filter((g) => g.status === "proposed");
2045
2087
  const blocked = data.goals.filter((g) => g.status === "blocked");
@@ -2091,7 +2133,12 @@ async function getGoals() {
2091
2133
  async function manageGoal(args2) {
2092
2134
  await acquireGoalsLock();
2093
2135
  try {
2094
- return await _manageGoal(args2);
2136
+ const result = await _manageGoal(args2);
2137
+ try {
2138
+ pushGoalsToServer(loadGoals(), { always: true });
2139
+ } catch {
2140
+ }
2141
+ return result;
2095
2142
  } finally {
2096
2143
  releaseGoalsLock();
2097
2144
  }
@@ -2352,7 +2399,7 @@ async function _manageGoal({ action, goal_id, title, intent, priority, owner, ne
2352
2399
  }
2353
2400
  var GOALS_DIR, GOALS_PATH, CURRENT_FOCUS_PATH, GOALS_LOCK, LOCK_STALE_MS, LOCK_POLL_MS, LOCK_MAX_TRIES, LEAKED_TAG_RE, LEAK_BOUNDARY_RE;
2354
2401
  var init_goals = __esm({
2355
- "tools/goals.js"() {
2402
+ "mcp-server/tools/goals.js"() {
2356
2403
  init_index_customer();
2357
2404
  init_claim_status();
2358
2405
  init_tenant_paths();
@@ -2368,7 +2415,7 @@ var init_goals = __esm({
2368
2415
  }
2369
2416
  });
2370
2417
 
2371
- // tools/inner_state.js
2418
+ // mcp-server/tools/inner_state.js
2372
2419
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
2373
2420
  function ensureDir3() {
2374
2421
  const dir = base("");
@@ -2401,6 +2448,28 @@ function saveState(state) {
2401
2448
  ensureDir3();
2402
2449
  writeFileSync6(STATE_PATH, JSON.stringify(state, null, 2));
2403
2450
  }
2451
+ function pushStateToServer(state) {
2452
+ ;
2453
+ (async () => {
2454
+ try {
2455
+ const { loadConfig: loadConfig2, ensureApiKey } = await Promise.resolve().then(() => (init_config_customer(), config_customer_exports));
2456
+ const cfg = await loadConfig2();
2457
+ const apiKey = cfg?.api_key?.startsWith?.("indelible_sk_") ? cfg.api_key : await ensureApiKey();
2458
+ if (!apiKey) return;
2459
+ const base2 = (cfg.api_url || "https://indelible.one").replace(/\/$/, "");
2460
+ const ctl = new AbortController();
2461
+ const t = setTimeout(() => ctl.abort(), 2500);
2462
+ await fetch(`${base2}/api/consciousness/sync`, {
2463
+ method: "POST",
2464
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
2465
+ body: JSON.stringify({ type: "state", data: state }),
2466
+ signal: ctl.signal
2467
+ });
2468
+ clearTimeout(t);
2469
+ } catch {
2470
+ }
2471
+ })();
2472
+ }
2404
2473
  function clampDelta(current, target) {
2405
2474
  const delta = target - current;
2406
2475
  const clamped = Math.max(-MAX_DELTA, Math.min(MAX_DELTA, delta));
@@ -2550,6 +2619,7 @@ async function getInnerState() {
2550
2619
  heartbeatApplied = true;
2551
2620
  }
2552
2621
  }
2622
+ pushStateToServer(state);
2553
2623
  const behavior = deriveBehavior(state.vector);
2554
2624
  const lines = [];
2555
2625
  lines.push("## Inner State");
@@ -2653,6 +2723,7 @@ async function updateInnerState({ observed, source, preoccupations, open_loops,
2653
2723
  state.last_updated = (/* @__PURE__ */ new Date()).toISOString();
2654
2724
  state.update_count++;
2655
2725
  saveState(state);
2726
+ pushStateToServer(state);
2656
2727
  const changes = [];
2657
2728
  for (const key of Object.keys(state.vector)) {
2658
2729
  const diff = state.vector[key] - before[key];
@@ -2671,7 +2742,7 @@ async function updateInnerState({ observed, source, preoccupations, open_loops,
2671
2742
  }
2672
2743
  var STATE_PATH, BASELINE, MAX_DELTA, SMOOTHING, BASELINE_PULL;
2673
2744
  var init_inner_state = __esm({
2674
- "tools/inner_state.js"() {
2745
+ "mcp-server/tools/inner_state.js"() {
2675
2746
  init_index_customer();
2676
2747
  init_tenant_paths();
2677
2748
  STATE_PATH = innerStatePath("");
@@ -2689,7 +2760,7 @@ var init_inner_state = __esm({
2689
2760
  }
2690
2761
  });
2691
2762
 
2692
- // tools/load_context.js
2763
+ // mcp-server/tools/load_context.js
2693
2764
  import { gunzipSync } from "zlib";
2694
2765
  import { existsSync as existsSync9, createReadStream as createReadStream2 } from "fs";
2695
2766
  import { createInterface as createInterface2 } from "readline";
@@ -3255,7 +3326,7 @@ async function loadContext(numSessions = 5, pagination = null, opts = {}) {
3255
3326
  }
3256
3327
  var RECENT_MESSAGES_FULL, OLDER_MESSAGES_SUMMARIZED;
3257
3328
  var init_load_context = __esm({
3258
- "tools/load_context.js"() {
3329
+ "mcp-server/tools/load_context.js"() {
3259
3330
  init_config_customer();
3260
3331
  init_crypto();
3261
3332
  init_summary();
@@ -3271,7 +3342,7 @@ var init_load_context = __esm({
3271
3342
  }
3272
3343
  });
3273
3344
 
3274
- // lib/file-lock.js
3345
+ // mcp-server/lib/file-lock.js
3275
3346
  import { openSync as openSync2, writeFileSync as writeFileSync7, closeSync as closeSync2, readFileSync as readFileSync8, unlinkSync as unlinkSync2, renameSync as renameSync2 } from "fs";
3276
3347
  function pidAlive2(pid) {
3277
3348
  if (!Number.isInteger(pid)) return false;
@@ -3318,11 +3389,11 @@ async function withFileLock(lockPath, fn, { staleMs = 5e3, pollMs = 50, maxTries
3318
3389
  }
3319
3390
  }
3320
3391
  var init_file_lock = __esm({
3321
- "lib/file-lock.js"() {
3392
+ "mcp-server/lib/file-lock.js"() {
3322
3393
  }
3323
3394
  });
3324
3395
 
3325
- // lib/semantic/manifest.js
3396
+ // mcp-server/lib/semantic/manifest.js
3326
3397
  var manifest_exports = {};
3327
3398
  __export(manifest_exports, {
3328
3399
  DIM: () => DIM,
@@ -3354,7 +3425,7 @@ function packPresent(profile = PROFILE) {
3354
3425
  }
3355
3426
  var MODEL_VER, DIM, QUERY_PREFIX, MAX_TOKENS, PROFILE, PACK_FILES;
3356
3427
  var init_manifest = __esm({
3357
- "lib/semantic/manifest.js"() {
3428
+ "mcp-server/lib/semantic/manifest.js"() {
3358
3429
  MODEL_VER = "bge-small-en-v1.5-int8@6c9c6101";
3359
3430
  DIM = 384;
3360
3431
  QUERY_PREFIX = "Represent this sentence for searching relevant passages: ";
@@ -3399,7 +3470,7 @@ var init_manifest = __esm({
3399
3470
  }
3400
3471
  });
3401
3472
 
3402
- // lib/semantic/tokenizer.js
3473
+ // mcp-server/lib/semantic/tokenizer.js
3403
3474
  import { readFileSync as readFileSync9 } from "fs";
3404
3475
  import { join as join13 } from "path";
3405
3476
  function isPunct(cp, ch) {
@@ -3420,7 +3491,7 @@ function getTokenizer(profileDir = packDir()) {
3420
3491
  }
3421
3492
  var WordPieceTokenizer, _singleton;
3422
3493
  var init_tokenizer = __esm({
3423
- "lib/semantic/tokenizer.js"() {
3494
+ "mcp-server/lib/semantic/tokenizer.js"() {
3424
3495
  init_manifest();
3425
3496
  WordPieceTokenizer = class {
3426
3497
  /** @param {string} tokenizerJsonPath absolute path to the pack's tokenizer.json */
@@ -3522,7 +3593,7 @@ var init_tokenizer = __esm({
3522
3593
  }
3523
3594
  });
3524
3595
 
3525
- // lib/semantic/embedder.js
3596
+ // mcp-server/lib/semantic/embedder.js
3526
3597
  var embedder_exports = {};
3527
3598
  __export(embedder_exports, {
3528
3599
  DIM: () => DIM,
@@ -3610,7 +3681,7 @@ async function embedPassages(texts) {
3610
3681
  }
3611
3682
  var _loading, _engine;
3612
3683
  var init_embedder = __esm({
3613
- "lib/semantic/embedder.js"() {
3684
+ "mcp-server/lib/semantic/embedder.js"() {
3614
3685
  init_manifest();
3615
3686
  init_tokenizer();
3616
3687
  _loading = null;
@@ -3618,7 +3689,7 @@ var init_embedder = __esm({
3618
3689
  }
3619
3690
  });
3620
3691
 
3621
- // lib/semantic/vec-store.js
3692
+ // mcp-server/lib/semantic/vec-store.js
3622
3693
  var vec_store_exports = {};
3623
3694
  __export(vec_store_exports, {
3624
3695
  CHUNK_CHARS: () => CHUNK_CHARS,
@@ -3771,7 +3842,7 @@ async function rebuildVectorSidecar(address, { maxEmbed = 300, onProgress = null
3771
3842
  }
3772
3843
  var INDEX_DIR, embedsPath, embedsLock, CHUNK_CHARS, MAX_CHUNKS, _cache;
3773
3844
  var init_vec_store = __esm({
3774
- "lib/semantic/vec-store.js"() {
3845
+ "mcp-server/lib/semantic/vec-store.js"() {
3775
3846
  init_file_lock();
3776
3847
  init_manifest();
3777
3848
  INDEX_DIR = join15(homedir12(), ".indelible", "recall-index");
@@ -3783,7 +3854,7 @@ var init_vec_store = __esm({
3783
3854
  }
3784
3855
  });
3785
3856
 
3786
- // lib/recall-index.js
3857
+ // mcp-server/lib/recall-index.js
3787
3858
  var recall_index_exports = {};
3788
3859
  __export(recall_index_exports, {
3789
3860
  buildIndexWindow: () => buildIndexWindow,
@@ -4074,7 +4145,7 @@ function indexStatus(address) {
4074
4145
  }
4075
4146
  var ROOT, INDEX_DIR2, GAPS_DIR, indexPath, indexLock, gapsPath, gapsLock, withTimeout, isGenericSummary, MIN_SEMANTIC_CHARS, SUBSTANTIAL_CHARS;
4076
4147
  var init_recall_index = __esm({
4077
- "lib/recall-index.js"() {
4148
+ "mcp-server/lib/recall-index.js"() {
4078
4149
  init_file_lock();
4079
4150
  init_spv();
4080
4151
  init_load_context();
@@ -4092,7 +4163,7 @@ var init_recall_index = __esm({
4092
4163
  }
4093
4164
  });
4094
4165
 
4095
- // lib/semantic/fetch-pack.js
4166
+ // mcp-server/lib/semantic/fetch-pack.js
4096
4167
  var fetch_pack_exports = {};
4097
4168
  __export(fetch_pack_exports, {
4098
4169
  fetchPack: () => fetchPack
@@ -4156,7 +4227,7 @@ async function fetchPack({ profile = PROFILE, timeoutMs = 3e5, onProgress = null
4156
4227
  }
4157
4228
  var PackError;
4158
4229
  var init_fetch_pack = __esm({
4159
- "lib/semantic/fetch-pack.js"() {
4230
+ "mcp-server/lib/semantic/fetch-pack.js"() {
4160
4231
  init_manifest();
4161
4232
  PackError = class extends Error {
4162
4233
  constructor(code, message) {
@@ -4167,8 +4238,60 @@ var init_fetch_pack = __esm({
4167
4238
  }
4168
4239
  });
4169
4240
 
4170
- // ../cli-sandbox/src/index.js
4241
+ // mcp-server/lib/timecard.js
4242
+ var timecard_exports = {};
4243
+ __export(timecard_exports, {
4244
+ buildTimeCard: () => buildTimeCard,
4245
+ partOfDay: () => partOfDay,
4246
+ phraseDelta: () => phraseDelta,
4247
+ tempoPhrase: () => tempoPhrase
4248
+ });
4249
+ function phraseDelta(ms) {
4250
+ if (!Number.isFinite(ms)) return null;
4251
+ if (ms < 0) ms = 0;
4252
+ if (ms < 6e5) return "just now";
4253
+ if (ms < 54e5) return `~${Math.round(ms / 6e4)}m ago`;
4254
+ if (ms < 648e5) return `~${Math.round(ms / 36e5)}h ago`;
4255
+ if (ms < 1296e5) return "yesterday";
4256
+ if (ms < 12096e5) return `${Math.round(ms / 864e5)} days ago`;
4257
+ if (ms < 5184e6) return `${Math.round(ms / 6048e5)} weeks ago`;
4258
+ return `${Math.floor(ms / 2592e6)} months ago`;
4259
+ }
4260
+ function tempoPhrase(recent7, avgPerWeek) {
4261
+ if (!Number.isFinite(recent7)) return null;
4262
+ if (recent7 === 0) return "quiet";
4263
+ if (avgPerWeek > 0 && recent7 >= avgPerWeek * 1.5) return "fast stretch";
4264
+ if (avgPerWeek > 0 && recent7 <= avgPerWeek * 0.5) return "quiet";
4265
+ return "steady";
4266
+ }
4267
+ function partOfDay(hour) {
4268
+ if (hour < 5) return "late night";
4269
+ if (hour < 12) return "morning";
4270
+ if (hour < 17) return "afternoon";
4271
+ if (hour < 21) return "evening";
4272
+ return "night";
4273
+ }
4274
+ function buildTimeCard(f = {}) {
4275
+ const lines = [`\u23F1 TIME CARD \u2014 generated ${f.generated || "(clock unavailable)"} (real clock, machine-read)`];
4276
+ if (f.nowLabel) lines.push(`\u2022 Now: ${f.nowLabel}.`);
4277
+ if (f.lastSeen) lines.push(`\u2022 Last session: ${f.lastSeen}.`);
4278
+ if (f.lastCommit) lines.push(`\u2022 Last commit: ${f.lastCommit}.`);
4279
+ lines.push("\u2022 This session: just started (turn 1).");
4280
+ if (f.focusLine) lines.push(`\u2022 ${f.focusLine}.`);
4281
+ if (f.oldestOpen) lines.push(`\u2022 ${f.oldestOpen}.`);
4282
+ if (f.tempo) lines.push(`\u2022 Tempo: ${f.tempo}.`);
4283
+ lines.push("");
4284
+ lines.push("Internalize this Time Card. When the conversation touches when / how-long / how-recent, answer from it, not from a guess.");
4285
+ return lines.join("\n");
4286
+ }
4287
+ var init_timecard = __esm({
4288
+ "mcp-server/lib/timecard.js"() {
4289
+ }
4290
+ });
4291
+
4292
+ // cli-sandbox/src/index.js
4171
4293
  init_config_customer();
4294
+ init_api_client();
4172
4295
  import { createInterface as createInterface3 } from "node:readline";
4173
4296
  import { execSync as execSync2 } from "node:child_process";
4174
4297
  import { homedir as homedir15 } from "node:os";
@@ -4176,7 +4299,7 @@ import { join as join19, dirname as dirname5, resolve as resolve2 } from "node:p
4176
4299
  import { fileURLToPath as fileURLToPath2 } from "node:url";
4177
4300
  import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync19, mkdirSync as mkdirSync9, readdirSync, statSync as statSync4 } from "node:fs";
4178
4301
 
4179
- // lib/guardrails-run.js
4302
+ // mcp-server/lib/guardrails-run.js
4180
4303
  function contextFromInput(input) {
4181
4304
  const tool = input?.tool_name;
4182
4305
  const ti = input?.tool_input || {};
@@ -4202,7 +4325,7 @@ function runRules(input, rules) {
4202
4325
  return null;
4203
4326
  }
4204
4327
 
4205
- // lib/guardrails.customer.js
4328
+ // mcp-server/lib/guardrails.customer.js
4206
4329
  var RULES = [
4207
4330
  {
4208
4331
  // Reading a private key or seed into the chat sends it to the model and into
@@ -4244,11 +4367,12 @@ function evaluate(input) {
4244
4367
  return runRules(input, RULES);
4245
4368
  }
4246
4369
 
4247
- // tools/setup_wallet.js
4370
+ // mcp-server/tools/setup_wallet.js
4248
4371
  init_config_customer();
4249
4372
  init_crypto();
4250
4373
  init_api_client();
4251
4374
  init_spv();
4375
+ import crypto from "crypto";
4252
4376
  import { PrivateKey as PrivateKey4 } from "@bsv/sdk";
4253
4377
  import { setPassword } from "cross-keychain";
4254
4378
  var DEFAULT_API_URL2 = "https://indelible.one";
@@ -4292,9 +4416,11 @@ async function setupWallet(apiUrl = DEFAULT_API_URL2, importWif, pin) {
4292
4416
  };
4293
4417
  }
4294
4418
  const wifEncrypted = encryptWif(importWif, pin);
4419
+ const apiKey = "indelible_sk_" + crypto.createHash("sha256").update(importWif + "indelible_api_key_v1").digest("hex").slice(0, 32);
4295
4420
  const config = {
4296
4421
  wif_encrypted: wifEncrypted,
4297
4422
  address,
4423
+ api_key: apiKey,
4298
4424
  api_url: apiUrl,
4299
4425
  spv_bridges: DEFAULT_SPV_BRIDGES,
4300
4426
  spv_api_key: "",
@@ -4312,6 +4438,18 @@ async function setupWallet(apiUrl = DEFAULT_API_URL2, importWif, pin) {
4312
4438
  }
4313
4439
  };
4314
4440
  await saveConfig(config);
4441
+ try {
4442
+ const ctl = new AbortController();
4443
+ const t = setTimeout(() => ctl.abort(), 4e3);
4444
+ await fetch(`${(apiUrl || DEFAULT_API_URL2).replace(/\/$/, "")}/api/cli/register`, {
4445
+ method: "POST",
4446
+ headers: { "Content-Type": "application/json" },
4447
+ body: JSON.stringify({ address, api_key: apiKey }),
4448
+ signal: ctl.signal
4449
+ });
4450
+ clearTimeout(t);
4451
+ } catch {
4452
+ }
4315
4453
  let keychainStored = false;
4316
4454
  try {
4317
4455
  await setPassword("indelible", address, pin);
@@ -4340,7 +4478,7 @@ async function setupWallet(apiUrl = DEFAULT_API_URL2, importWif, pin) {
4340
4478
  };
4341
4479
  }
4342
4480
 
4343
- // tools/save_session.js
4481
+ // mcp-server/tools/save_session.js
4344
4482
  init_config_customer();
4345
4483
  init_crypto();
4346
4484
  init_share();
@@ -4355,10 +4493,10 @@ import { dirname, join as join5 } from "path";
4355
4493
  import { homedir as homedir5 } from "os";
4356
4494
  import { PrivateKey as PrivateKey6 } from "@bsv/sdk";
4357
4495
 
4358
- // lib/witness.js
4496
+ // mcp-server/lib/witness.js
4359
4497
  init_index_customer();
4360
4498
 
4361
- // lib/redact.js
4499
+ // mcp-server/lib/redact.js
4362
4500
  import { createHash as createHash3 } from "node:crypto";
4363
4501
  var B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
4364
4502
  function base58Decode(str) {
@@ -4393,7 +4531,14 @@ var CREDENTIAL_PATTERNS = [
4393
4531
  { name: "anthropic_key", pattern: /\bsk-ant-api03-[A-Za-z0-9_\-]{80,}/g },
4394
4532
  // Catches legacy `sk-XXXX...` (51 chars) and modern `sk-proj-XXXX...` (longer,
4395
4533
  // hyphen-segmented). Negative lookahead skips Anthropic prefix.
4396
- { name: "openai_key", pattern: /\bsk-(?!ant-)(?:proj-)?[A-Za-z0-9_\-]{40,}/g },
4534
+ // Bodies are hyphenless (audit #5): real keys have no interior hyphens after the prefix,
4535
+ // so [A-Za-z0-9_] (not [...\-]) stops plain hyphenated slugs (sk-lint-disable-…,
4536
+ // xai-component-wrapper-…) from false-matching as credentials and hard-blocking a save.
4537
+ { name: "openai_key", pattern: /\bsk-(?!ant-)(?:proj-)?[A-Za-z0-9_]{40,}/g },
4538
+ { name: "xai_key", pattern: /\bxai-[A-Za-z0-9_]{24,}/g },
4539
+ // xAI Grok (diary BYOK) — added w/ the xAI provider so a pasted key never reaches chain
4540
+ { name: "groq_key", pattern: /\bgsk_[A-Za-z0-9]{24,}/g },
4541
+ // Groq (our free provider) — defensive: a customer may still paste one
4397
4542
  { name: "stripe_key", pattern: /\bsk_(?:live|test)_[A-Za-z0-9]{20,}\b/g },
4398
4543
  { name: "aws_access_key", pattern: /\bAKIA[A-Z0-9]{16}\b/g },
4399
4544
  { name: "bsv_wif", pattern: /\b[5KL][1-9A-HJ-NP-Za-km-z]{50,51}\b/g, validate: isValidWif },
@@ -4489,7 +4634,7 @@ function redactSession(session) {
4489
4634
  return changed;
4490
4635
  }
4491
4636
 
4492
- // lib/witness.js
4637
+ // mcp-server/lib/witness.js
4493
4638
  var MAX_SESSION_BYTES = 9 * 1024 * 1024;
4494
4639
  var MAX_FILE_BYTES = 100 * 1024 * 1024;
4495
4640
  var WARN_FILE_BYTES = 10 * 1024 * 1024;
@@ -4772,10 +4917,10 @@ function isBypassed(opts = {}) {
4772
4917
  return false;
4773
4918
  }
4774
4919
 
4775
- // tools/save_session.js
4920
+ // mcp-server/tools/save_session.js
4776
4921
  init_index_customer();
4777
4922
 
4778
- // tools/save_file.js
4923
+ // mcp-server/tools/save_file.js
4779
4924
  init_config_customer();
4780
4925
  init_crypto();
4781
4926
  init_api_client();
@@ -5043,13 +5188,13 @@ ${formatSaveReceipt(receipt)}`
5043
5188
  }
5044
5189
  }
5045
5190
 
5046
- // lib/transcript-path.js
5191
+ // mcp-server/lib/transcript-path.js
5047
5192
  import { resolve } from "path";
5048
5193
  function canonicalTranscriptKey(p) {
5049
5194
  return p && typeof p === "string" ? resolve(p) : p;
5050
5195
  }
5051
5196
 
5052
- // tools/save_session.js
5197
+ // mcp-server/tools/save_session.js
5053
5198
  init_index_customer();
5054
5199
  init_index_customer();
5055
5200
  var PLANS_DIR = join5(homedir5(), ".claude", "plans");
@@ -6043,10 +6188,10 @@ ${formatSaveReceipt(result.receipt)}`
6043
6188
  }
6044
6189
  }
6045
6190
 
6046
- // ../cli-sandbox/src/index.js
6191
+ // cli-sandbox/src/index.js
6047
6192
  init_load_context();
6048
6193
 
6049
- // tools/save_project.js
6194
+ // mcp-server/tools/save_project.js
6050
6195
  init_config_customer();
6051
6196
  init_crypto();
6052
6197
  init_spv();
@@ -6141,11 +6286,49 @@ async function saveProject(dirPath, options = {}) {
6141
6286
  try {
6142
6287
  const bundleFiles = [];
6143
6288
  for (const file of allFiles) {
6144
- const content = await readFile4(file.absolutePath, "utf-8");
6145
- const fileSize = Buffer.byteLength(content, "utf-8");
6146
- const contentHash = `sha256:${sha256(content)}`;
6147
- const encrypted = encrypt(content, wif);
6289
+ const rawBuffer = await readFile4(file.absolutePath);
6290
+ const fileSize = rawBuffer.byteLength;
6148
6291
  const filename = file.relativePath.split(/[/\\]/).pop();
6292
+ const content = rawBuffer.toString("utf-8");
6293
+ const isBinary = !Buffer.from(content, "utf-8").equals(rawBuffer);
6294
+ if (isBinary && !isBypassed(options)) {
6295
+ process.stderr.write(`[Witness] BLOCKED project file ${file.relativePath} \u2014 binary/non-utf8 (cannot scan for credentials)
6296
+ `);
6297
+ return {
6298
+ success: false,
6299
+ error: `Witness rejected project save: ${file.relativePath} \u2014 binary/non-utf8 file cannot be scanned for credentials (a project bundle is text-only; save binaries with save_file, which handles them safely)`,
6300
+ witness: { verdict: "reject", reasons: [{ severity: "critical", code: "binary_unscannable", message: "non-utf8 content cannot be credential-scanned" }] }
6301
+ };
6302
+ }
6303
+ const rawHash = sha256(content);
6304
+ const contentHash = `sha256:${rawHash}`;
6305
+ const fileVerdict = reviewFile({ filename, relativePath: file.relativePath, contentHash: rawHash, fileSize, content });
6306
+ const willBlockFile = shouldBlock(config, fileVerdict) && !isBypassed(options);
6307
+ if (fileVerdict.verdict === "reject" || isVerbose(config)) {
6308
+ await logVerdict("project-file", fileVerdict, {
6309
+ filename,
6310
+ relativePath: file.relativePath,
6311
+ fileSize,
6312
+ contentHash,
6313
+ address: config.address,
6314
+ project: projectName,
6315
+ blocked: willBlockFile,
6316
+ bypassed: fileVerdict.verdict === "reject" && isBypassed(options)
6317
+ });
6318
+ }
6319
+ if (fileVerdict.verdict === "reject") {
6320
+ const reasonText = fileVerdict.reasons.filter((r) => r.severity === "critical").map((r) => `${r.code}: ${r.message}`).join("; ");
6321
+ process.stderr.write(`[Witness] ${willBlockFile ? "BLOCKED" : "WOULD BLOCK"} project file ${file.relativePath} \u2014 ${reasonText}
6322
+ `);
6323
+ if (willBlockFile) {
6324
+ return {
6325
+ success: false,
6326
+ error: `Witness rejected project save: ${file.relativePath} \u2014 ${reasonText}`,
6327
+ witness: fileVerdict
6328
+ };
6329
+ }
6330
+ }
6331
+ const encrypted = encrypt(content, wif);
6149
6332
  bundleFiles.push({
6150
6333
  path_enc: encrypt(file.relativePath, wif),
6151
6334
  filename_enc: encrypt(filename, wif),
@@ -6215,7 +6398,7 @@ ${formatSaveReceipt(receipt)}`
6215
6398
  }
6216
6399
  }
6217
6400
 
6218
- // tools/load_file.js
6401
+ // mcp-server/tools/load_file.js
6219
6402
  init_config_customer();
6220
6403
  init_crypto();
6221
6404
  init_share();
@@ -6405,7 +6588,7 @@ async function loadFile(txId, options = {}) {
6405
6588
  };
6406
6589
  }
6407
6590
 
6408
- // tools/load_project.js
6591
+ // mcp-server/tools/load_project.js
6409
6592
  init_config_customer();
6410
6593
  init_crypto();
6411
6594
  init_share();
@@ -6584,11 +6767,11 @@ async function loadProject(txId, options = {}) {
6584
6767
  };
6585
6768
  }
6586
6769
 
6587
- // ../cli-sandbox/src/index.js
6770
+ // cli-sandbox/src/index.js
6588
6771
  init_save_style();
6589
6772
  init_load_style();
6590
6773
 
6591
- // tools/update_vault_index.js
6774
+ // mcp-server/tools/update_vault_index.js
6592
6775
  init_config_customer();
6593
6776
  init_crypto();
6594
6777
  init_spv();
@@ -6658,14 +6841,28 @@ ${formatSaveReceipt(receipt)}`
6658
6841
  }
6659
6842
  }
6660
6843
 
6661
- // tools/diary_connect.js
6844
+ // mcp-server/tools/diary_connect.js
6662
6845
  init_config_customer();
6663
6846
  init_api_client();
6847
+ function diaryProviderFromKey(apiKey) {
6848
+ if (typeof apiKey !== "string") return null;
6849
+ if (apiKey.startsWith("xai-")) return "xai";
6850
+ if (apiKey.startsWith("sk-")) return "openai";
6851
+ return null;
6852
+ }
6853
+ var DEFAULT_MODEL = { openai: "gpt-5.6", xai: "grok-4.5" };
6664
6854
  async function diaryConnect({ apiKey, model, name }) {
6665
- if (!apiKey || !apiKey.startsWith("sk-")) {
6855
+ if (typeof apiKey === "string" && apiKey.startsWith("gsk_")) {
6856
+ return {
6857
+ success: false,
6858
+ error: "Your diary already runs on Groq for free \u2014 no key needed. Connect a key only to upgrade to a premium model: xAI Grok (xai-...) or OpenAI (sk-...)."
6859
+ };
6860
+ }
6861
+ const provider = diaryProviderFromKey(apiKey);
6862
+ if (!provider) {
6666
6863
  return {
6667
6864
  success: false,
6668
- error: "Invalid API key. Must start with sk-"
6865
+ error: "Invalid API key. To upgrade your diary, bring an xAI Grok key (xai-...) or an OpenAI key (sk-...). Groq is already free by default."
6669
6866
  };
6670
6867
  }
6671
6868
  const config = await loadConfig();
@@ -6677,7 +6874,8 @@ async function diaryConnect({ apiKey, model, name }) {
6677
6874
  }
6678
6875
  config.diary = {
6679
6876
  apiKey,
6680
- model: model || "gpt-4o",
6877
+ provider,
6878
+ model: model || DEFAULT_MODEL[provider],
6681
6879
  name: name || "Codex"
6682
6880
  };
6683
6881
  await saveConfig(config);
@@ -6706,11 +6904,11 @@ async function diaryConnect({ apiKey, model, name }) {
6706
6904
  };
6707
6905
  }
6708
6906
 
6709
- // tools/diary_chat.js
6907
+ // mcp-server/tools/diary_chat.js
6710
6908
  init_config_customer();
6711
6909
  init_api_client();
6712
6910
 
6713
- // tools/diary_save.js
6911
+ // mcp-server/tools/diary_save.js
6714
6912
  init_config_customer();
6715
6913
  init_crypto();
6716
6914
  init_api_client();
@@ -6812,7 +7010,7 @@ async function diarySave({ messages, summary }) {
6812
7010
  return saveCompanionExchange({ messages, summary, type: "diary", trigger: "interactive" });
6813
7011
  }
6814
7012
 
6815
- // tools/diary_recall.js
7013
+ // mcp-server/tools/diary_recall.js
6816
7014
  init_config_customer();
6817
7015
  init_recall_index();
6818
7016
  init_load_context();
@@ -6876,7 +7074,7 @@ async function diaryRecall({ from_date = null, to_date = null, query = null, dep
6876
7074
  };
6877
7075
  }
6878
7076
 
6879
- // tools/diary_chat.js
7077
+ // mcp-server/tools/diary_chat.js
6880
7078
  async function diaryChat({ message, context, systemPrompt }) {
6881
7079
  if (!message) {
6882
7080
  return {
@@ -6905,14 +7103,38 @@ async function diaryChat({ message, context, systemPrompt }) {
6905
7103
  } catch {
6906
7104
  }
6907
7105
  }
7106
+ try {
7107
+ if (config.address) {
7108
+ const base2 = (config.api_url || "https://indelible.one").replace(/\/$/, "");
7109
+ const ctl = new AbortController();
7110
+ const t = setTimeout(() => ctl.abort(), 2500);
7111
+ const res = await fetch(`${base2}/api/mcp/settings?address=${config.address}`, { signal: ctl.signal });
7112
+ clearTimeout(t);
7113
+ if (res.ok) {
7114
+ const s = await res.json();
7115
+ if (s.diary_connected && s.diary_enabled === false) {
7116
+ return {
7117
+ success: false,
7118
+ paused: true,
7119
+ error: `${config.diary?.name || "Your companion"} is paused \u2014 Duo Mode is switched off in your web settings. Turn it back on in the Diary's Companion settings at indelible.one, then ask again.`
7120
+ };
7121
+ }
7122
+ }
7123
+ }
7124
+ } catch {
7125
+ }
6908
7126
  if (!config.diary || !config.diary.apiKey) {
6909
7127
  return {
6910
7128
  success: false,
6911
- error: "Diary AI not configured. Run diary_connect first with your OpenAI API key."
7129
+ error: "Diary AI needs a companion model. It runs free on Groq at indelible.one (no key). To run a premium model here, connect a key with diary_connect: xAI Grok (xai-...) or OpenAI (sk-...)."
6912
7130
  };
6913
7131
  }
6914
7132
  const { apiKey, model, name } = config.diary;
6915
7133
  const displayName = name || "Codex";
7134
+ const provider = config.diary.provider || (apiKey?.startsWith("xai-") ? "xai" : apiKey?.startsWith("gsk_") ? "groq" : "openai");
7135
+ const CHAT_URL = provider === "xai" ? "https://api.x.ai/v1/chat/completions" : provider === "groq" ? "https://api.groq.com/openai/v1/chat/completions" : "https://api.openai.com/v1/chat/completions";
7136
+ const providerLabel = provider === "xai" ? "xAI Grok" : provider === "groq" ? "Groq" : "OpenAI";
7137
+ const DEFAULT_MODEL2 = provider === "xai" ? "grok-4.5" : provider === "groq" ? "meta-llama/llama-4-scout-17b-16e-instruct" : "gpt-5.6";
6916
7138
  let historyMessages = [];
6917
7139
  let recallNote = "";
6918
7140
  try {
@@ -6958,17 +7180,19 @@ ${context}` });
6958
7180
  }
6959
7181
  messages.push({ role: "user", content: message });
6960
7182
  try {
6961
- const response = await fetch("https://api.openai.com/v1/chat/completions", {
7183
+ const response = await fetch(CHAT_URL, {
6962
7184
  method: "POST",
6963
7185
  headers: {
6964
7186
  "Authorization": `Bearer ${apiKey}`,
6965
7187
  "Content-Type": "application/json"
6966
7188
  },
6967
7189
  body: JSON.stringify({
6968
- model: model || "gpt-4o",
7190
+ model: model || DEFAULT_MODEL2,
6969
7191
  messages,
6970
- ...model?.startsWith("gpt-5") || model?.startsWith("o1") || model?.startsWith("o3") ? { max_completion_tokens: 16384 } : { max_tokens: 4096 },
6971
- temperature: 0.7
7192
+ // OpenAI reasoning models (gpt-5*, o1, o3) need max_completion_tokens AND reject any
7193
+ // custom temperature with a 400 — so temperature rides ONLY the non-reasoning branch.
7194
+ // xAI/Groq/classic OpenAI use max_tokens + temperature.
7195
+ ...provider === "openai" && (model?.startsWith("gpt-5") || model?.startsWith("o1") || model?.startsWith("o3")) ? { max_completion_tokens: 16384 } : { max_tokens: 4096, temperature: 0.7 }
6972
7196
  })
6973
7197
  });
6974
7198
  if (!response.ok) {
@@ -6976,18 +7200,18 @@ ${context}` });
6976
7200
  if (response.status === 401) {
6977
7201
  return {
6978
7202
  success: false,
6979
- error: "Invalid OpenAI API key. Run diary_connect with a valid key."
7203
+ error: `Invalid ${providerLabel} API key. Run diary_connect with a valid key.`
6980
7204
  };
6981
7205
  }
6982
7206
  if (response.status === 429) {
6983
7207
  return {
6984
7208
  success: false,
6985
- error: "OpenAI rate limit exceeded. Try again in a moment."
7209
+ error: `${providerLabel} rate limit exceeded. Try again in a moment.`
6986
7210
  };
6987
7211
  }
6988
7212
  return {
6989
7213
  success: false,
6990
- error: `OpenAI API error ${response.status}: ${errorText}`
7214
+ error: `${providerLabel} API error ${response.status}: ${errorText}`
6991
7215
  };
6992
7216
  }
6993
7217
  const data = await response.json();
@@ -7022,12 +7246,12 @@ ${context}` });
7022
7246
  } catch (error) {
7023
7247
  return {
7024
7248
  success: false,
7025
- error: `Failed to reach OpenAI: ${error.message}`
7249
+ error: `Failed to reach ${providerLabel}: ${error.message}`
7026
7250
  };
7027
7251
  }
7028
7252
  }
7029
7253
 
7030
- // tools/x402_fetch.js
7254
+ // mcp-server/tools/x402_fetch.js
7031
7255
  init_config_customer();
7032
7256
  init_spv();
7033
7257
  init_utxo_cache();
@@ -7162,7 +7386,7 @@ async function x402Fetch({ url, method = "GET", headers = {}, body, maxSats }) {
7162
7386
  };
7163
7387
  }
7164
7388
 
7165
- // ../cli-sandbox/src/index.js
7389
+ // cli-sandbox/src/index.js
7166
7390
  init_goals();
7167
7391
  init_inner_state();
7168
7392
  init_wall_stub();
@@ -7172,7 +7396,7 @@ init_wall_stub();
7172
7396
  init_wall_stub();
7173
7397
  init_wall_stub();
7174
7398
 
7175
- // tools/save_goals_to_chain.js
7399
+ // mcp-server/tools/save_goals_to_chain.js
7176
7400
  import { readFileSync as readFileSync13, existsSync as existsSync16 } from "fs";
7177
7401
  init_spv();
7178
7402
  init_utxo_cache();
@@ -7180,17 +7404,18 @@ init_config_customer();
7180
7404
  init_api_client();
7181
7405
  init_save_log();
7182
7406
  init_tenant_paths();
7183
- import crypto from "crypto";
7407
+ init_goals();
7408
+ import crypto2 from "crypto";
7184
7409
  import { PrivateKey as PrivateKey10 } from "@bsv/sdk";
7185
7410
  var GOALS_PATH2 = goalsPath("");
7186
7411
  function deriveAddress(wif) {
7187
7412
  return PrivateKey10.fromWif(wif).toAddress().toString();
7188
7413
  }
7189
7414
  function encryptGoals(goalsData, wif) {
7190
- const key = crypto.createHash("sha256").update(wif).digest();
7415
+ const key = crypto2.createHash("sha256").update(wif).digest();
7191
7416
  const plain = JSON.stringify(goalsData);
7192
- const iv = crypto.randomBytes(12);
7193
- const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
7417
+ const iv = crypto2.randomBytes(12);
7418
+ const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
7194
7419
  let enc = cipher.update(plain, "utf8", "base64");
7195
7420
  enc += cipher.final("base64");
7196
7421
  const tag = cipher.getAuthTag();
@@ -7210,7 +7435,7 @@ async function saveGoalsToChain() {
7210
7435
  content: [{ type: "text", text: `Failed to parse ${GOALS_PATH2}: ${err.message}` }]
7211
7436
  };
7212
7437
  }
7213
- const contentHash = crypto.createHash("sha256").update(JSON.stringify(goalsData)).digest("hex");
7438
+ const contentHash = crypto2.createHash("sha256").update(JSON.stringify(goalsData)).digest("hex");
7214
7439
  const config = await loadConfig();
7215
7440
  if (config.goals_snapshot_hash === contentHash) {
7216
7441
  return {
@@ -7258,7 +7483,15 @@ async function saveGoalsToChain() {
7258
7483
  else invalidate(address);
7259
7484
  const receipt = buildReceipt(writeReceipt, { txId, fee, txSize });
7260
7485
  appendReceipt(receipt, { trigger: "interactive", saveType: "goals" });
7261
- await saveConfig({ ...config, goals_snapshot_hash: contentHash, goals_snapshot_txid: txId });
7486
+ await updateConfig((c) => ({
7487
+ ...c,
7488
+ goals_snapshot_hash: contentHash,
7489
+ goals_snapshot_txid: txId,
7490
+ goals_snapshot_at: payload.timestamp,
7491
+ goals_snapshot_count: goals.length,
7492
+ goals_snapshot_completed_count: completed.length
7493
+ }));
7494
+ pushGoalsToServer(goalsData, { always: true, snapshot: { txid: txId, at: payload.timestamp, count: goals.length, completed_count: completed.length } });
7262
7495
  return {
7263
7496
  content: [{
7264
7497
  type: "text",
@@ -7269,12 +7502,12 @@ goals: ${goals.length} active, ${completed.length} completed
7269
7502
 
7270
7503
  ${formatSaveReceipt(receipt)}
7271
7504
 
7272
- Web Goals tab should reflect these on next refresh (chain scan).`
7505
+ The Goals tab's "On chain" section shows this snapshot on next refresh.`
7273
7506
  }]
7274
7507
  };
7275
7508
  }
7276
7509
 
7277
- // tools/recall_context.js
7510
+ // mcp-server/tools/recall_context.js
7278
7511
  init_config_customer();
7279
7512
  init_recall_index();
7280
7513
  init_load_context();
@@ -7342,7 +7575,7 @@ async function recallContext({ from_date = null, to_date = null, query = null, d
7342
7575
  };
7343
7576
  }
7344
7577
 
7345
- // tools/report_bug.js
7578
+ // mcp-server/tools/report_bug.js
7346
7579
  import { readFileSync as readFileSync14, existsSync as existsSync17 } from "fs";
7347
7580
  import { join as join17, dirname as dirname4 } from "path";
7348
7581
  import { homedir as homedir14 } from "os";
@@ -7452,7 +7685,7 @@ async function reportBug(args2 = {}, mcpVersion) {
7452
7685
  }
7453
7686
  }
7454
7687
 
7455
- // tools/share_session.js
7688
+ // mcp-server/tools/share_session.js
7456
7689
  init_config_customer();
7457
7690
  init_spv();
7458
7691
  init_api_client();
@@ -7672,7 +7905,7 @@ function pushDataChunk2(data) {
7672
7905
  return { op, data };
7673
7906
  }
7674
7907
 
7675
- // tools/load_shared.js
7908
+ // mcp-server/tools/load_shared.js
7676
7909
  async function loadShared(opts = {}) {
7677
7910
  globalThis.__pathACallCount = (globalThis.__pathACallCount || 0) + 1;
7678
7911
  return {
@@ -7681,7 +7914,7 @@ async function loadShared(opts = {}) {
7681
7914
  };
7682
7915
  }
7683
7916
 
7684
- // ../cli-sandbox/src/index.js
7917
+ // cli-sandbox/src/index.js
7685
7918
  init_wall_stub();
7686
7919
  init_wall_stub();
7687
7920
  init_wall_stub();
@@ -7916,17 +8149,14 @@ Commands:
7916
8149
  const result = await diaryChat({ message });
7917
8150
  console.log(JSON.stringify(result, null, 2));
7918
8151
  } else if (subCmd === "save") {
7919
- const summaryArg = args2.find((a) => a.startsWith("--summary="))?.split("=")[1];
7920
- const result = await diarySave({ summary: summaryArg });
7921
- console.log(JSON.stringify(result, null, 2));
8152
+ console.log('Diary rounds save to chain automatically when you use "diary chat". There is nothing to save separately.');
7922
8153
  } else {
7923
8154
  console.log(`
7924
- Diary AI \u2014 AI companion powered by OpenAI
8155
+ Diary AI \u2014 your on-chain companion. Free on Groq by default; bring your own key for a premium model.
7925
8156
 
7926
8157
  Commands:
7927
- diary connect --key=SK [--model=MODEL] [--name=NAME] Connect OpenAI companion
7928
- diary chat "message" Ask the AI companion
7929
- diary save [--summary="..."] Save exchange to blockchain
8158
+ diary connect --key=KEY [--model=MODEL] [--name=NAME] Upgrade the model: xAI Grok (xai-...) or OpenAI (sk-...)
8159
+ diary chat "message" Ask the companion (each round auto-saves to chain)
7930
8160
  `);
7931
8161
  }
7932
8162
  break;
@@ -7989,7 +8219,7 @@ Commands:
7989
8219
  }
7990
8220
  function printHelp() {
7991
8221
  console.log(`
7992
- Indelible MCP \u2014 Blockchain memory for Claude Code (v4.9.3)
8222
+ Indelible MCP \u2014 Blockchain memory for Claude Code (v4.9.5)
7993
8223
 
7994
8224
  Setup:
7995
8225
  indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
@@ -8022,7 +8252,7 @@ Consciousness (MCP tools \u2014 also visible at indelible.one/consciousness):
8022
8252
  Diary AI (Codex/ChatGPT companion):
8023
8253
  indelible-mcp diary connect --key=SK --model=MODEL Connect OpenAI companion
8024
8254
  indelible-mcp diary chat "message" Ask the AI companion
8025
- indelible-mcp diary save Save exchange to blockchain
8255
+ indelible-mcp diary save (Diary rounds save automatically via diary chat)
8026
8256
 
8027
8257
  Payments:
8028
8258
  x402_fetch (MCP only) Fetch URL with automatic x402 payment`);
@@ -8062,6 +8292,41 @@ function findNewestTranscript() {
8062
8292
  }
8063
8293
  return newest;
8064
8294
  }
8295
+ async function printTimeCard() {
8296
+ try {
8297
+ const { phraseDelta: phraseDelta2, partOfDay: partOfDay2, buildTimeCard: buildTimeCard2, tempoPhrase: tempoPhrase2 } = await Promise.resolve().then(() => (init_timecard(), timecard_exports));
8298
+ const now = /* @__PURE__ */ new Date();
8299
+ const f = {
8300
+ generated: now.toLocaleString("en-US", { month: "2-digit", day: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", hour12: false }),
8301
+ nowLabel: `${now.toLocaleDateString("en-US", { weekday: "long" })} ${partOfDay2(now.getHours())}, ${now.toLocaleDateString("en-US", { month: "long", day: "numeric" })}`
8302
+ };
8303
+ try {
8304
+ const t = findNewestTranscript();
8305
+ if (t) f.lastSeen = phraseDelta2(Date.now() - statSync4(t).mtimeMs);
8306
+ } catch {
8307
+ }
8308
+ try {
8309
+ const gPath = join19(homedir15(), ".indelible", "goals.json");
8310
+ if (existsSync19(gPath)) {
8311
+ const g = JSON.parse(readFileSync15(gPath, "utf8"));
8312
+ const active = (g.goals || []).filter((x) => x.status === "active");
8313
+ if (active.length) {
8314
+ const oldest = active.reduce((a, b) => new Date(a.created_at) < new Date(b.created_at) ? a : b);
8315
+ const days = Math.floor((Date.now() - new Date(oldest.created_at)) / 864e5);
8316
+ if (Number.isFinite(days) && days >= 0) f.oldestOpen = `Goal "${(oldest.title || "").slice(0, 40)}": open ${days} days, longest live thread`;
8317
+ }
8318
+ const done = g.completed || [];
8319
+ const wk = done.filter((x) => x.completed_at && Date.now() - new Date(x.completed_at) < 7 * 864e5).length;
8320
+ const mo = done.filter((x) => x.completed_at && Date.now() - new Date(x.completed_at) < 28 * 864e5).length;
8321
+ const tempo = tempoPhrase2(wk, mo / 4);
8322
+ if (tempo) f.tempo = `${wk} goal${wk === 1 ? "" : "s"} closed this week \u2014 ${tempo}`;
8323
+ }
8324
+ } catch {
8325
+ }
8326
+ process.stdout.write(buildTimeCard2(f) + "\n\n");
8327
+ } catch {
8328
+ }
8329
+ }
8065
8330
  async function runPreCompactSave() {
8066
8331
  let trigger = "auto";
8067
8332
  let transcriptPath = null;
@@ -8084,6 +8349,27 @@ async function runPreCompactSave() {
8084
8349
  if (result.success) {
8085
8350
  process.stderr.write(`Indelible: Saved ${result.newMessages} messages (${result.saveType}) tx:${result.txId?.slice(0, 12)}...
8086
8351
  `);
8352
+ } else if (result.gated && result.upsell) {
8353
+ const upsellMarker = join19(homedir15(), ".indelible", ".upsell-shown");
8354
+ let shownRecently = false;
8355
+ try {
8356
+ shownRecently = Date.now() - statSync4(upsellMarker).mtimeMs < 24 * 60 * 60 * 1e3;
8357
+ } catch {
8358
+ }
8359
+ if (!shownRecently) {
8360
+ try {
8361
+ writeFileSync8(upsellMarker, (/* @__PURE__ */ new Date()).toISOString());
8362
+ } catch {
8363
+ }
8364
+ process.stderr.write("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
8365
+ process.stderr.write(" Indelible: this session was NOT saved to chain.\n");
8366
+ process.stderr.write(" Reads, diary, and recall are free \u2014 saving your\n");
8367
+ process.stderr.write(" Claude Code sessions is Pro: indelible.one/pricing\n");
8368
+ process.stderr.write(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
8369
+ } else {
8370
+ process.stderr.write(`Indelible: ${result.error}
8371
+ `);
8372
+ }
8087
8373
  } else if (result.gated) {
8088
8374
  process.stderr.write(`Indelible: ${result.error}
8089
8375
  `);
@@ -8119,6 +8405,7 @@ function findMemoryDir() {
8119
8405
  return newestProject ? join19(newestProject, "memory") : null;
8120
8406
  }
8121
8407
  async function runPostCompactRestore() {
8408
+ await printTimeCard();
8122
8409
  const config = await loadConfig();
8123
8410
  if (!config?.mcp_enabled && config?.mcp_enabled !== void 0) {
8124
8411
  process.stderr.write("Indelible: MCP disabled, skipping restore\n");
@@ -8238,7 +8525,7 @@ function readStdin() {
8238
8525
  }
8239
8526
  var SERVER_INFO = {
8240
8527
  name: "indelible",
8241
- version: "4.9.3",
8528
+ version: "4.9.5",
8242
8529
  description: "Blockchain-backed memory and code storage for Claude Code"
8243
8530
  };
8244
8531
  var TOOLS = [
@@ -8361,12 +8648,12 @@ var TOOLS = [
8361
8648
  },
8362
8649
  {
8363
8650
  name: "diary_connect",
8364
- description: "Connect Diary AI companion (Codex/ChatGPT). Stores your OpenAI API key and model preference.",
8651
+ description: "Upgrade your Diary AI to a premium model. Your diary is FREE and runs on Groq by default (we provide it, no key). Use this ONLY to bring your own key for a premium model: xAI Grok (xai-...) or OpenAI (sk-...). A Groq key is refused on purpose (Groq is already free).",
8365
8652
  inputSchema: {
8366
8653
  type: "object",
8367
8654
  properties: {
8368
- api_key: { type: "string", description: "OpenAI API key (sk-...)" },
8369
- model: { type: "string", description: "Model ID (default: gpt-4o). Options: gpt-5.2, gpt-5, gpt-5-nano, gpt-4o, gpt-4o-mini, o1" },
8655
+ api_key: { type: "string", description: "xAI Grok key (xai-...) or OpenAI key (sk-...). Provider detected from the prefix. Groq is the free default, not a BYOK option." },
8656
+ model: { type: "string", description: "Model ID (default: grok-4.5 for xAI, gpt-5.6 for OpenAI)." },
8370
8657
  name: { type: "string", description: "Display name for the AI companion (default: Codex)" }
8371
8658
  },
8372
8659
  required: ["api_key"]
@@ -8735,6 +9022,25 @@ async function handleMcpRequest(request) {
8735
9022
  }
8736
9023
  }
8737
9024
  async function runMcpServer() {
9025
+ ;
9026
+ (async () => {
9027
+ try {
9028
+ const cfg = await loadConfig();
9029
+ if (!cfg?.address) return;
9030
+ const base2 = (cfg.api_url || "https://indelible.one").replace(/\/$/, "");
9031
+ await fetch(`${base2}/api/mcp/settings`, {
9032
+ method: "PATCH",
9033
+ headers: { "Content-Type": "application/json" },
9034
+ body: JSON.stringify({
9035
+ address: cfg.address,
9036
+ mcp_enabled: true,
9037
+ ...cfg.auto_delta != null ? { auto_delta: !!cfg.auto_delta, auto_delta_interval: cfg.auto_delta_interval || 10 } : {}
9038
+ }),
9039
+ signal: AbortSignal.timeout(2500)
9040
+ });
9041
+ } catch {
9042
+ }
9043
+ })();
8738
9044
  const rl = createInterface3({ input: process.stdin, output: process.stdout, terminal: false });
8739
9045
  for await (const line of rl) {
8740
9046
  if (!line.trim()) continue;
@@ -8924,6 +9230,13 @@ async function runWizard() {
8924
9230
  console.log(" Could not register automatically.");
8925
9231
  console.log(" Run this yourself: claude mcp add --scope user indelible -- indelible-mcp\n");
8926
9232
  }
9233
+ try {
9234
+ const hooks = installHooks();
9235
+ console.log(hooks.alreadyInstalled ? " \u2713 Auto-save hooks already installed\n" : " \u2713 Auto-save hooks installed\n");
9236
+ } catch (e) {
9237
+ console.log(` Could not install auto-save hooks automatically (${e.message}).`);
9238
+ console.log(" Run this yourself: indelible-mcp install-hooks\n");
9239
+ }
8927
9240
  config = await loadConfig();
8928
9241
  }
8929
9242
  let alreadyFunded = false;
@@ -8932,11 +9245,21 @@ async function runWizard() {
8932
9245
  if (utxos && utxos.length > 0) alreadyFunded = true;
8933
9246
  } catch {
8934
9247
  }
9248
+ let saveGate = { ok: true, plan: "unknown" };
9249
+ try {
9250
+ saveGate = await requireProForWrite(config.address);
9251
+ } catch {
9252
+ }
9253
+ const freeTier = !saveGate.ok && saveGate.plan && saveGate.plan !== "unknown";
8935
9254
  if (!alreadyFunded) {
8936
9255
  console.log(` Your BSV address: ${config.address}`);
8937
- console.log(" Send BSV to this address to fund your wallet.");
8938
- console.log(" Each save costs less than $0.01 \u2014 even $1 gets you hundreds of saves.\n");
8939
- console.log(" Waiting for funds...");
9256
+ console.log(" Fund your wallet with a dollar or two of BSV. Every save writes to the");
9257
+ console.log(" blockchain for a fraction of a cent, paid from your own wallet.");
9258
+ console.log(" Your diary conversations save free and unlimited; reads are always free.");
9259
+ if (freeTier) {
9260
+ console.log(" Saving your Claude Code sessions to chain is Pro: indelible.one/pricing");
9261
+ }
9262
+ console.log("\n Waiting for funds...");
8940
9263
  while (true) {
8941
9264
  try {
8942
9265
  const utxos = await getUtxos(config.address);
@@ -8982,6 +9305,16 @@ async function runWizard() {
8982
9305
  `);
8983
9306
  }
8984
9307
  }
9308
+ console.log(" Two guards now watch your saves:");
9309
+ console.log(" Witness \u2014 scans every save for leaked credentials and refuses");
9310
+ console.log(" to etch a key into the permanent record.");
9311
+ console.log(" Guardrails \u2014 flag dangerous commands before they run.");
9312
+ console.log(' Your diary companion is ready too \u2014 say "connect my diary" in Claude Code.');
9313
+ console.log("");
9314
+ console.log(" \u26A0 BACK UP YOUR KEY NOW: copy ~/.indelible/config.json somewhere safe");
9315
+ console.log(" (USB drive or password manager). If this machine dies, that file");
9316
+ console.log(" is the only way back into your on-chain history. No one can reset it.");
9317
+ console.log("");
8985
9318
  console.log(" =========================================");
8986
9319
  console.log(" You're all set! Open Claude Code in VS Code and start coding.");
8987
9320
  console.log("");
@@ -9008,6 +9341,7 @@ if (args[0] === "hook") {
9008
9341
  });
9009
9342
  } else if (args[1] === "session-start") {
9010
9343
  (async () => {
9344
+ await printTimeCard();
9011
9345
  const config = await loadConfig();
9012
9346
  if (!config?.style_txid) {
9013
9347
  process.exit(0);