indelible-mcp 5.8.1 → 5.8.3
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/AI_OPERATORS_HANDBOOK.md +44 -23
- package/CLI_HANDBOOK.md +27 -73
- package/CUSTOMER_AGENT_HANDBOOK.md +20 -8
- package/LICENSE +1 -1
- package/README.md +14 -9
- package/package.json +1 -1
- package/src/index.js +94 -11
package/AI_OPERATORS_HANDBOOK.md
CHANGED
|
@@ -51,16 +51,13 @@ by adversarial review, not by re-reading my own work. That is the method working
|
|
|
51
51
|
*Everything after this part assumes a configured wallet and a connected MCP. If the human is a new signup,
|
|
52
52
|
you are here first. **Read 7.2 before you touch a single command in this part.***
|
|
53
53
|
|
|
54
|
-
## 0.1
|
|
54
|
+
## 0.1 What `setup` actually does — and what bare `setup` does not
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
**The comment is wrong, and the command alone will not configure anything.** Bare `setup` returns:
|
|
56
|
+
The shipped README describes this correctly: its Quick Start points at the wizard (bare `indelible-mcp`),
|
|
57
|
+
and its command table calls `setup` *"Guided setup (recommended — your key is taken at a prompt, never
|
|
58
|
+
written to shell history)"*, with the `--wif=`/`--pin=` form flagged for automation and carrying its own
|
|
59
|
+
shell-history warning. The behavior underneath is still worth knowing, because bare `setup` — run with no
|
|
60
|
+
key — will not configure anything. It returns:
|
|
64
61
|
|
|
65
62
|
```json
|
|
66
63
|
{
|
|
@@ -72,9 +69,8 @@ indelible-mcp setup # "generates a BSV keypair locally"
|
|
|
72
69
|
```
|
|
73
70
|
|
|
74
71
|
- **It generates nothing.** `setupWallet` only ever calls `PrivateKey.fromWif(...)` and reports *"Wallet
|
|
75
|
-
**imported** and encrypted!"*. The key comes **from indelible.one**, not from the package
|
|
76
|
-
|
|
77
|
-
(`README.md:75`) are both wrong on this point.
|
|
72
|
+
**imported** and encrypted!"*. The key comes **from indelible.one**, not from the package — the README
|
|
73
|
+
now says the same ("takes your private key … then registers your wallet with the Indelible server").
|
|
78
74
|
- **It exits `1`.** The failure prints a JSON error and sets a non-zero exit code, so a script can rely on
|
|
79
75
|
it. ⚠️ That guarantee belongs to the `setup` path specifically — `indelible-mcp status` and
|
|
80
76
|
`indelible-mcp load` still exit `0` while printing an error, so do not assume every verb signals failure
|
|
@@ -137,12 +133,13 @@ verb: `recall_context` and `diary_recall` are **MCP-only** — there is no `inde
|
|
|
137
133
|
`✗ unknown command`.) If your Indelible tools are missing, that is a *client wiring* problem, not a dead
|
|
138
134
|
install — check with the CLI before telling the human their setup failed.
|
|
139
135
|
|
|
140
|
-
Two wiring details worth knowing
|
|
136
|
+
Two wiring details worth knowing:
|
|
141
137
|
|
|
142
|
-
- The wizard registers with `claude mcp add --scope user …`, which writes `~/.claude.json`.
|
|
143
|
-
hand
|
|
138
|
+
- The wizard registers with `claude mcp add --scope user …`, which writes `~/.claude.json`. If config is
|
|
139
|
+
managed by hand, the entry belongs in `~/.claude.json` or the project's `.mcp.json` — never
|
|
140
|
+
`settings.json`, which Claude Code does not read MCP servers from.
|
|
144
141
|
- Dropping `--scope user` registers the server **project-scoped**, so it will not load in other
|
|
145
|
-
directories.
|
|
142
|
+
directories. The docs and the code now agree — both include the flag and say why it matters.
|
|
146
143
|
|
|
147
144
|
---
|
|
148
145
|
|
|
@@ -175,9 +172,17 @@ outlives your context window and survives compaction, and that can be snapshotte
|
|
|
175
172
|
`save_goals_to_chain`.
|
|
176
173
|
|
|
177
174
|
⚠️ **That snapshot is a Pro write and it spends sats.** On a free plan the call returns the Pro notice and
|
|
178
|
-
no txid; on
|
|
179
|
-
|
|
180
|
-
|
|
175
|
+
no txid; on a genuinely empty wallet it returns *"No UTXOs available. Fund your wallet."* — and since 5.8.x
|
|
176
|
+
an underfunded save answers with a typed refusal instead: `INSUFFICIENT_FUNDS` names the exact need vs.
|
|
177
|
+
what is spendable (a single payment of at least the named amount always suffices), `NO_ECONOMIC_COINS`
|
|
178
|
+
means the wallet holds only dust (add funds in ONE normal-sized payment), `UNECONOMIC_FRAGMENTATION` means
|
|
179
|
+
the wallet HAS enough but spread across too many small coins — the message itself says **do NOT add
|
|
180
|
+
funds**, `TOO_MANY_INPUTS` / `TX_TOO_LARGE` mean the save cannot be built as one transaction (nothing was
|
|
181
|
+
spent; TX_TOO_LARGE says split the content), and `WALLET_VIEW_DEGRADED` means the network's view of the
|
|
182
|
+
wallet is incomplete (offline, or the index catching up) — **do not have the human add funds on that one;
|
|
183
|
+
the message itself says so. Nothing was spent; retry in a few minutes.** Each of these has a row in the
|
|
184
|
+
Part 6 table. So the local record is durable for everyone, but **anchored** only for a funded Pro account.
|
|
185
|
+
Never promise a free user an on-chain goal record.
|
|
181
186
|
|
|
182
187
|
That last part is why they matter. A goal closed with a txid in its evidence is a claim anyone can check
|
|
183
188
|
years later. A goal closed without one is just a story.
|
|
@@ -386,6 +391,12 @@ Learn these and you will stop misreading normal behavior as breakage.
|
|
|
386
391
|
| `WALLET_BUSY_RESERVED` | Another agent holds the coin. Names the holder and lease | Retry shortly. Safe |
|
|
387
392
|
| `BROADCAST_REJECTED … txn-mempool-conflict` | Another save took the coin first. **Nothing was spent** | Retry; it rebuilds |
|
|
388
393
|
| `WALLET_KEY_MISMATCH` | The key does not match the account. **Refused before spending** | Do not "fix" by editing config — run setup properly |
|
|
394
|
+
| `INSUFFICIENT_FUNDS` | The wallet holds less than this save needs — the message names both numbers. **Nothing was spent** | Fund the wallet — a single payment of at least the named amount always suffices |
|
|
395
|
+
| `NO_ECONOMIC_COINS` | The wallet holds only dust — each coin costs more in fees than it adds. **Nothing was spent** | Add funds in **ONE normal-sized payment**; the small coins remain theirs, just unusable for now |
|
|
396
|
+
| `UNECONOMIC_FRAGMENTATION` | Enough sats in total, but spread across so many small coins the fee would swamp the save. **Nothing was spent** | **Do NOT add funds** — they already have them, and more will not help. The message says to contact support (consolidation is not offered yet) |
|
|
397
|
+
| `TOO_MANY_INPUTS` | The save would need more coins than the one-transaction signing limit. **Nothing was spent** | The message says to contact support |
|
|
398
|
+
| `TX_TOO_LARGE` | The built transaction exceeds the network's byte envelope. **Nothing was spent** | Split the content into smaller saves |
|
|
399
|
+
| `WALLET_VIEW_DEGRADED` | The network's view of the wallet is incomplete — offline, or the index catching up. **Nothing was spent** | **Do not have the human add funds on this message** — it forbids that itself. Retry in a few minutes |
|
|
389
400
|
| `indexed: false` | On chain, not yet searchable | Nothing. It catches up |
|
|
390
401
|
| `⚠ agent could not be read … corrupt, NOT deleted` | A torn identity file | **Never re-birth over it.** Restore that file from backup. ⚠️ `agents --restore` will **not** fix it — it refuses an identity it cannot parse (*"refusing to overwrite a file we cannot compare"*) and leaves the file untouched. Only once the file is restored, or deliberately deleted, will a re-run rebuild that agent |
|
|
391
402
|
| `[g-401] lost its coin to <txid>` | Another writer spent it first | Informational |
|
|
@@ -434,7 +445,7 @@ see another machine.**
|
|
|
434
445
|
The shipped customer handbook is explicit:
|
|
435
446
|
|
|
436
447
|
> *"A funded wallet. **Session, file, and project saves need Pro. All reads are free.**"*
|
|
437
|
-
> *"…
|
|
448
|
+
> *"…every hook runs for everyone. Restore, the time card, style loading, and Guardrails are fully free.
|
|
438
449
|
> The pre-compaction **save** is the one piece that needs Pro. On a free wallet the hook still fires, but
|
|
439
450
|
> instead of saving it prints the plain notice that saving is a Pro feature. **That is not an error.**"*
|
|
440
451
|
|
|
@@ -481,9 +492,12 @@ get the prompt. Do not fight that — it is the guardrail working.
|
|
|
481
492
|
|
|
482
493
|
The product already steers away from it: the help text lists the wizard **first** as *"recommended — your
|
|
483
494
|
key is never written to shell history"* and labels the flag form *"for automation (both values land in
|
|
484
|
-
shell history)"*, both setup error messages lead with the wizard, and the README
|
|
485
|
-
|
|
486
|
-
no
|
|
495
|
+
shell history)"*, both setup error messages lead with the wizard, and the README flags the `--wif=` form
|
|
496
|
+
as automation-only with its own shell-history warning. Every shipped surface now steers to the wizard —
|
|
497
|
+
`CLI_HANDBOOK.md` no longer teaches the flag form at all: its command reference lists `setup` as
|
|
498
|
+
*"interactive; the safe path"*, and its troubleshooting table warns outright against passing a key on the
|
|
499
|
+
command line (*"do NOT pass a key on the command line; shells record it"*). There is no longer an
|
|
500
|
+
exception to treat as a doc bug.
|
|
487
501
|
|
|
488
502
|
**So:**
|
|
489
503
|
|
|
@@ -742,6 +756,13 @@ The pre-compact hook fires and saves the session automatically. **Do not rely on
|
|
|
742
756
|
saves the *transcript*, not your *conclusions*. A transcript of you reasoning toward a finding is not the
|
|
743
757
|
same artifact as the finding, written down, with its evidence, banked and citable.
|
|
744
758
|
|
|
759
|
+
A second hook fires at the same moment — the **strongbox** (5.7.4) — and again at session end. It keeps a
|
|
760
|
+
verified local byte-copy of the raw transcript in `~/.indelible/transcript-vault/`, because the host
|
|
761
|
+
deletes raw session files after roughly 30 days by default. `indelible-mcp strongbox` lists what is
|
|
762
|
+
protected; `indelible-mcp strongbox run` protects the current session now (it never guesses — if more
|
|
763
|
+
than one transcript matches, pass `--session <id>`). It is local-only and free — the chain save is the
|
|
764
|
+
provable record, the strongbox is the raw material.
|
|
765
|
+
|
|
745
766
|
Auto-save is your seatbelt. The daily MD is your actually-arriving-somewhere.
|
|
746
767
|
|
|
747
768
|
## 10.2 Bank at milestones, not at the end
|
package/CLI_HANDBOOK.md
CHANGED
|
@@ -23,7 +23,7 @@ You don't have to memorize any of this. In Claude Code you can just say what you
|
|
|
23
23
|
| `indelible-mcp vault load-file <txid>` | Gets it back, anywhere, forever. |
|
|
24
24
|
| `indelible-mcp diary chat "hello"` | Talks to your diary companion. |
|
|
25
25
|
|
|
26
|
-
Three things worth knowing, once: **your key
|
|
26
|
+
Three things worth knowing, once: **you hold your key** (it lives in `~/.indelible/config.json` on your machine — back that file up) · **reading is always free** (saves cost fractions of a cent in Bitcoin fees) · **your stop button beats everything** (pause works even with no wallet at all).
|
|
27
27
|
|
|
28
28
|
## Honest Receipts + the Wallet (5.7.0)
|
|
29
29
|
|
|
@@ -55,7 +55,7 @@ your machine picks the new setting up automatically.
|
|
|
55
55
|
|
|
56
56
|
Indelible works with Claude Code, with OpenAI's Codex CLI, or with both at once on the same memory.
|
|
57
57
|
|
|
58
|
-
- **Claude Code:** `claude mcp add indelible -- indelible-mcp`
|
|
58
|
+
- **Claude Code:** `claude mcp add --scope user indelible -- indelible-mcp` (`--scope user` registers it for every project, not just the folder you ran it from)
|
|
59
59
|
- **Codex CLI:** add to `~/.codex/config.toml` and restart Codex:
|
|
60
60
|
|
|
61
61
|
```toml
|
|
@@ -210,7 +210,7 @@ status Show wallet address, API key, last session
|
|
|
210
210
|
|
|
211
211
|
### Code Vault
|
|
212
212
|
```
|
|
213
|
-
vault save-file <path> Save a file (encrypted, chunked
|
|
213
|
+
vault save-file <path> Save a file (encrypted; one transaction up to ~10MB, chunked above that)
|
|
214
214
|
vault save-project <dir> [--name=NAME] Save a project directory
|
|
215
215
|
vault load-file <txid> [--output=path] Load a file from blockchain
|
|
216
216
|
vault load-project <txid> [--output-dir=dir] Load a project from blockchain
|
|
@@ -221,7 +221,7 @@ vault update-index Update on-chain vault index
|
|
|
221
221
|
|
|
222
222
|
### Diary AI (Dual-Agent)
|
|
223
223
|
```
|
|
224
|
-
diary connect --key=
|
|
224
|
+
diary connect --key=KEY [--model=MODEL] [--name=NAME] Upgrade the model: xAI Grok (xai-...) or OpenAI (sk-...)
|
|
225
225
|
diary chat "message" Ask the AI companion
|
|
226
226
|
diary save [--summary="..."] Save exchange to blockchain
|
|
227
227
|
```
|
|
@@ -229,7 +229,7 @@ diary save [--summary="..."] Save exchange to blockcha
|
|
|
229
229
|
### Setup & Hooks
|
|
230
230
|
```
|
|
231
231
|
setup Set up your wallet (interactive; the safe path)
|
|
232
|
-
install-hooks Install auto-save
|
|
232
|
+
install-hooks Install the Indelible hooks into Claude Code: auto-save before compaction, auto-restore after, your saved style at session start, the credential/destructive-command guard, the Strongbox transcript copies, and a per-turn clock stamp (the wristwatch, in your own timezone)
|
|
233
233
|
hook pre-compact Auto-save before compaction (called by hook)
|
|
234
234
|
hook post-compact Auto-restore after compaction (called by hook)
|
|
235
235
|
```
|
|
@@ -251,7 +251,7 @@ The CLI and MCP server are the **same codebase**. They import the same tool func
|
|
|
251
251
|
|---|---|---|
|
|
252
252
|
| **Location** | `(published package)` | `(core source)` |
|
|
253
253
|
| **Config** | Sync (`readFileSync`/`writeFileSync`) | Async (`await loadConfig()`) |
|
|
254
|
-
| **Fetch** | Native `fetch` (Node 18+) | `
|
|
254
|
+
| **Fetch** | Native `fetch` (Node 18+) | Native `fetch` (Node 18+) |
|
|
255
255
|
| **Timeout** | `AbortSignal.timeout(ms)` | Manual `AbortController` + `setTimeout` |
|
|
256
256
|
| **Entry point** | CLI arg parser in `index.js` | JSON-RPC stdin/stdout in `index.js` |
|
|
257
257
|
| **How Claude calls it** | `node src/index.js <command>` | MCP protocol via Claude Code hooks |
|
|
@@ -274,28 +274,9 @@ indelible-mcp load --sessions=1
|
|
|
274
274
|
indelible-mcp vault load-style
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
import { checkConfirmation } from './src/lib/spv.js';
|
|
281
|
-
const r = await checkConfirmation('TXID_HERE');
|
|
282
|
-
console.log(JSON.stringify(r, null, 2));
|
|
283
|
-
" 2>&1
|
|
284
|
-
|
|
285
|
-
cd /path/to/indelible-cli && node --input-type=module -e "
|
|
286
|
-
import { checkTier } from './src/lib/api-client.js';
|
|
287
|
-
import { loadConfig } from './src/lib/config.js';
|
|
288
|
-
const config = loadConfig();
|
|
289
|
-
const r = await checkTier(config.api_key);
|
|
290
|
-
console.log(JSON.stringify(r, null, 2));
|
|
291
|
-
" 2>&1
|
|
292
|
-
|
|
293
|
-
cd /path/to/indelible-cli && node --input-type=module -e "
|
|
294
|
-
import { verifyRecentSaves } from './src/tools/save_file.js';
|
|
295
|
-
const r = await verifyRecentSaves();
|
|
296
|
-
console.log(JSON.stringify(r, null, 2));
|
|
297
|
-
" 2>&1
|
|
298
|
-
```
|
|
277
|
+
(Function-level imports are not possible against the installed package — `src/` ships one
|
|
278
|
+
self-contained bundle, so `./src/lib/*` and `./src/tools/*` do not exist. Confirmation
|
|
279
|
+
checking happens automatically inside every save: committed means mined, since 5.7.0.)
|
|
299
280
|
|
|
300
281
|
### Live tests (spends sats)
|
|
301
282
|
```bash
|
|
@@ -305,10 +286,10 @@ indelible-mcp save --summary "test save"
|
|
|
305
286
|
# File save
|
|
306
287
|
indelible-mcp vault save-file /path/to/small/file.txt
|
|
307
288
|
|
|
308
|
-
# Style save (
|
|
289
|
+
# Style save (strips core rules via stripCoreRules; load re-injects the current version's via prependCoreRules)
|
|
309
290
|
indelible-mcp vault save-style /path/to/rules.txt --name=test
|
|
310
291
|
|
|
311
|
-
# Diary chat (costs
|
|
292
|
+
# Diary chat (costs provider tokens — Groq is free, xAI/OpenAI are BYOK — not sats)
|
|
312
293
|
indelible-mcp diary chat "hello"
|
|
313
294
|
```
|
|
314
295
|
|
|
@@ -316,39 +297,9 @@ indelible-mcp diary chat "hello"
|
|
|
316
297
|
|
|
317
298
|
## Architecture
|
|
318
299
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
├── lib/
|
|
323
|
-
│ ├── config.js Sync config: loadConfig(), saveConfig(), getWif()
|
|
324
|
-
│ ├── crypto.js AES-256-GCM encrypt/decrypt, SHA-256, WIF derivation
|
|
325
|
-
│ ├── spv.js Multi-bridge SPV client, UTXO chaining, health tracking
|
|
326
|
-
│ └── api-client.js commitSession(), getLatestSessions(), checkTier()
|
|
327
|
-
├── tools/
|
|
328
|
-
│ ├── save_session.js Parse transcript, delta detect, encrypt, broadcast
|
|
329
|
-
│ │ + getRecentPlans() — scans ~/.claude/plans/
|
|
330
|
-
│ │ + updateMemory() — MEMORY.md hierarchy enforcement
|
|
331
|
-
│ │ + dashboard sync — PATCH to indelible.one
|
|
332
|
-
│ ├── load_context.js Fetch + decrypt + merge deltas + smart format
|
|
333
|
-
│ ├── setup_wallet.js Set up the wallet, encrypt the key with your PIN
|
|
334
|
-
│ ├── save_file.js Encrypt + broadcast file (chunked if >50KB)
|
|
335
|
-
│ │ + cacheTx() — local backup for re-broadcast
|
|
336
|
-
│ │ + verifyRecentSaves() — check confirmations
|
|
337
|
-
│ ├── save_project.js Walk dir + save each file + broadcast manifest
|
|
338
|
-
│ │ + cacheTx() — local backup for re-broadcast
|
|
339
|
-
│ ├── load_file.js Fetch + decrypt file from blockchain
|
|
340
|
-
│ ├── load_project.js Fetch manifest + decrypt + restore files
|
|
341
|
-
│ ├── save_style.js Encrypt style rules + broadcast
|
|
342
|
-
│ │ + ensureCoreRules() — auto-prepend core rules
|
|
343
|
-
│ ├── load_style.js Fetch + decrypt style from blockchain
|
|
344
|
-
│ ├── update_vault_index.js Update on-chain file/project index
|
|
345
|
-
│ ├── diary_connect.js Store OpenAI API key in config
|
|
346
|
-
│ ├── diary_chat.js Send message to Codex via OpenAI API
|
|
347
|
-
│ │ + blockchain history loading (last 3 sessions)
|
|
348
|
-
│ └── diary_save.js Save diary exchange to blockchain
|
|
349
|
-
└── dist/
|
|
350
|
-
└── indelible.exe Compiled standalone (bun build --compile)
|
|
351
|
-
```
|
|
300
|
+
`indelible-cli/src/index.js` — the whole CLI + MCP server, pressed into one self-contained bundle from the Indelible core (arg parser, MCP JSON-RPC dispatch, wallet/config, crypto, SPV client, and all tools). That single file is what the npm package installs (the `files` field ships only `src/` plus the handbooks); per-feature detail lives in the Key Internal Features table below, not in a file map.
|
|
301
|
+
|
|
302
|
+
Standalone executables for Windows, Linux, and macOS (`indelible.exe`, `indelible-win-x64.exe`, `indelible-linux-x64`, `indelible-darwin-arm64`) are built into the repo's `dist/` via the "Rebuild the Executable" section below and distributed separately from the npm install.
|
|
352
303
|
|
|
353
304
|
### Key Internal Features
|
|
354
305
|
|
|
@@ -359,8 +310,8 @@ indelible-cli/src/
|
|
|
359
310
|
| **Delta saves** | Only commits new messages since last save |
|
|
360
311
|
| **tx-cache** | Caches broadcast payloads to `~/.indelible/tx-cache/` for re-broadcast |
|
|
361
312
|
| **MEMORY.md enforcement** | Strips rules, archives done items, enforces 200-line / 20-line limits |
|
|
362
|
-
| **Core rules injection** | `
|
|
363
|
-
| **Diary history** | `diary chat`
|
|
313
|
+
| **Core rules injection** | Core rules are stripped at save (`stripCoreRules()`) so the stored style is pure user content; `prependCoreRules()` injects the current version's infrastructure rules at every load — updating the package updates the rules |
|
|
314
|
+
| **Diary history** | `diary chat` recalls your prior Duo rounds from the chain (up to 10, within a token budget) for conversation continuity |
|
|
364
315
|
| **Dashboard sync** | PATCHes indelible.one after every save |
|
|
365
316
|
|
|
366
317
|
---
|
|
@@ -381,13 +332,16 @@ This creates a standalone `indelible.exe` — no Node.js required on the target
|
|
|
381
332
|
|-------|-------|-----|
|
|
382
333
|
| `Wallet not configured` | No config.json or no WIF | Run `setup` (interactive — do NOT pass a key on the command line; shells record it) |
|
|
383
334
|
| `No UTXOs available` | Wallet is empty | Send BSV to your address |
|
|
384
|
-
| `
|
|
335
|
+
| `WALLET_VIEW_DEGRADED` | The network view of your wallet is offline or incomplete (the message says which: bridges unreachable, or the index catching up) | Retry in a few minutes. Do NOT add funds based on this message — your wallet may hold more than is currently visible; nothing was spent |
|
|
336
|
+
| `INSUFFICIENT_FUNDS` | Spendable coins cannot cover this save (the message names the sats needed; some sats may be temporarily reserved by another agent on this box) | Fund your wallet — a single payment of at least the stated amount always suffices; reserved sats free up shortly |
|
|
337
|
+
| `TOO_MANY_INPUTS` | The save would need more coins than the per-transaction signing limit (rare: heavily fragmented wallets) | Nothing was spent — contact support at indeliblebsv@gmail.com and mention TOO_MANY_INPUTS |
|
|
338
|
+
| `Bridge broadcast failed — all N bridges down` | All SPV relays (7 by default) down or rejecting | Check relay health, wait and retry |
|
|
385
339
|
| `No new messages since last save` | Already saved this transcript | Nothing to do — this is fine |
|
|
386
340
|
| `File not found` | Bad path or backslashes | Use forward slashes: `C:/path/to/file` |
|
|
387
341
|
| `Decrypt failed` | Wrong WIF or tampered data | Check `~/.indelible/config.json` has correct WIF |
|
|
388
342
|
| `txn-mempool-conflict` | Spending already-spent UTXO | UTXO chaining should prevent this — check code |
|
|
389
|
-
|
|
|
390
|
-
| `Diary AI
|
|
343
|
+
| `<provider> rate limit exceeded` | Too many diary chat calls (the message names the provider: Groq, xAI Grok, or OpenAI) | Wait a moment and retry |
|
|
344
|
+
| `Diary AI needs a companion model` | No companion key connected on this box (the keyless free Groq companion lives at indelible.one, not in the CLI) | Use the free companion at indelible.one, or upgrade here with `diary connect --key=...` using an xAI (`xai-...`) or OpenAI (`sk-...`) key — Groq (`gsk_`) keys are refused since Groq is already the free default |
|
|
391
345
|
|
|
392
346
|
---
|
|
393
347
|
|
|
@@ -402,22 +356,22 @@ This creates a standalone `indelible.exe` — no Node.js required on the target
|
|
|
402
356
|
| `spv_bridges` | Array of `{url, name}` relay objects |
|
|
403
357
|
| `api_url` | Indelible web app URL (`https://indelible.one`) |
|
|
404
358
|
| `api_key` | API key for authenticated endpoints |
|
|
405
|
-
| `auto_delta` | Auto-save
|
|
406
|
-
| `auto_delta_interval` |
|
|
359
|
+
| `auto_delta` | Auto-save on/off (bool) |
|
|
360
|
+
| `auto_delta_interval` | Minutes between auto-saves (default: 15, clamped 1–240) |
|
|
407
361
|
| `file_txids` | *(auto)* Index of files saved to chain |
|
|
408
362
|
| `project_txids` | *(auto)* Index of projects saved to chain |
|
|
409
363
|
| `last_session_id` | *(auto)* Previous session for chaining |
|
|
410
364
|
| `last_tx_id` | *(auto)* Last committed tx |
|
|
411
|
-
| `diary` | `{ apiKey, model, name }` —
|
|
365
|
+
| `diary` | `{ apiKey, provider, model, name }` — companion config (xAI `xai-...` or OpenAI `sk-...` key; Groq is the keyless free default) |
|
|
412
366
|
|
|
413
367
|
---
|
|
414
368
|
|
|
415
369
|
## Wallet
|
|
416
370
|
|
|
417
|
-
- **Address:** shown by `indelible-mcp status` (yours is
|
|
371
|
+
- **Address:** shown by `indelible-mcp status` (yours is created at setup — or imported, if you brought your own key — and lives in `~/.indelible/config.json`; back that file up)
|
|
418
372
|
- **Balance:** `indelible-mcp status`, or look your address up in the Chain Browser at indelible.one/explorer
|
|
419
373
|
- **Fund it:** Send BSV to your own address
|
|
420
|
-
- **Cost:** ~$0.
|
|
374
|
+
- **Cost:** ~$0.03/MB at BSV=$16 (the fee floor is 150 satoshis per kilobyte). Session saves are fractions of a cent.
|
|
421
375
|
|
|
422
376
|
---
|
|
423
377
|
|
|
@@ -32,6 +32,7 @@ Grouped by what they cost:
|
|
|
32
32
|
- **Inner state (free, local):** `get_inner_state`, `update_inner_state`
|
|
33
33
|
- **Starter recipes (free):** `list_agent_recipes` — eight ready-made blueprints you can hire instead of writing one from a blank page: a copy chief, a researcher, a deal reviewer, a code critic, a devil's advocate, a marshal, a ledger clerk, a negotiator. Birth any of them with `birth_custom_agent` and `recipe: '<id>'`. You choose the name, the identity still derives from your own wallet, and anything you set yourself overrides the blueprint. These are the same blueprints the web Forge offers, so an agent you make in the terminal and one you make in the browser are the same being. (Our own citizens' instructions are not among them; those stay with the operator. What travels is a recipe, never a key.)
|
|
34
34
|
- **Your own agents (free, local):** `birth_custom_agent` — create an agent of your own: name it, pick the kind of work it handles, write its instructions. Its identity derives from your wallet, so it is recoverable from that wallet forever and is never random. · `run_custom_agent` — run one and get back advice signed with that agent's own key, so anyone can verify which agent said it. · `convene_chamber` — put three of your agents in a room to decide something: one proposes, one argues against, a third rules, and each signs its own position. · `transmute_agents` — blend two of your agents into a third. Keys never mix; only the written instructions combine.
|
|
35
|
+
- **Sharing:** `share_session` (send one of your saved sessions to another address — Pro, it is a real on-chain write) · `load_shared` (read what someone shared with you — free)
|
|
35
36
|
- **Everything else:** `x402_fetch` (pays real sats to paid endpoints, capped at 10,000 sats per request unless you raise it), `report_bug`
|
|
36
37
|
|
|
37
38
|
One important nuance on "free": free means no subscription. On-chain writes still cost miner fees, paid in sats from your own wallet. Each save costs less than a cent. Diary and Duo saves are not Pro-gated; they only need a few sats in the wallet.
|
|
@@ -40,21 +41,29 @@ One important nuance on "free": free means no subscription. On-chain writes stil
|
|
|
40
41
|
|
|
41
42
|
## 2. What fires automatically (the hooks)
|
|
42
43
|
|
|
43
|
-
When you ran setup, the installer wired **
|
|
44
|
-
`~/.claude/settings.json`: two on PreCompact, two on SessionStart, one on PreToolUse,
|
|
45
|
-
SessionEnd
|
|
44
|
+
When you ran setup, the installer wired **seven hooks across five events** into Claude Code, in
|
|
45
|
+
`~/.claude/settings.json`: two on PreCompact, two on SessionStart, one on PreToolUse, one on
|
|
46
|
+
SessionEnd, and one on UserPromptSubmit — the wristwatch, which stamps the real clock (in your
|
|
47
|
+
system timezone) onto every turn so your AI always knows what time it is.
|
|
48
|
+
(`settings.local.json` is the older location the installer migrates *from*.) You can
|
|
46
49
|
see exactly what is installed at any time with `indelible-mcp install-hooks`, which prints the
|
|
47
|
-
full inventory of every hook it manages
|
|
48
|
-
|
|
50
|
+
full inventory of every hook it manages. (Note it is an installer, not a viewer: running it writes
|
|
51
|
+
settings.json and re-adds any Indelible hook that is missing, including one you deliberately
|
|
52
|
+
removed.) This is what each one does and why it is there.
|
|
49
53
|
|
|
50
54
|
| Hook | Fires | Does |
|
|
51
55
|
|---|---|---|
|
|
52
56
|
| **PreCompact** | Right before Claude Code compacts (summarizes away) your conversation | Saves the new messages of your real transcript to the chain, so nothing is lost to compaction. On success you see a line like `Indelible: Saved 42 messages (delta) tx:a1b2c3d4e5f6...` |
|
|
57
|
+
| **PreCompact (strongbox)** | Right before a compaction | Keeps a verified, byte-exact local copy of your raw transcript in `~/.indelible/transcript-vault/` (see below) |
|
|
53
58
|
| **SessionStart (after compaction)** | Right after a compaction | Restamps the time card (compaction resets the model's sense of time), reloads your saved AI style, and restores your recent sessions from chain so Claude picks up where it left off |
|
|
54
59
|
| **SessionStart** | At the start of every new session | Prints the time card and loads your on-chain AI style rules, if you saved one |
|
|
55
60
|
| **PreToolUse (Bash)** | Before any shell command your AI runs | Runs the Guardrails check: blocks a command that would leak your wallet key, warns on irreversible commands |
|
|
61
|
+
| **SessionEnd (strongbox)** | When a session ends | Takes the same verified local transcript copy at session end, so the strongbox holds the whole session |
|
|
62
|
+
| **UserPromptSubmit (wristwatch)** | On every prompt you submit | Stamps the real clock, in your system timezone, onto the turn so your AI always knows what time it is |
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
**PreCompact (strongbox)** and **SessionEnd (strongbox)** keep a verified, byte-exact local copy of your raw transcript in `~/.indelible/transcript-vault/` before your host's own cleanup (~30 days by default) can delete it. That is the strongbox: your transcript's local vault, separate from the chain save — free, local, no wallet needed. `indelible-mcp strongbox` shows what it holds; `indelible-mcp strongbox run` protects the current session on demand.
|
|
65
|
+
|
|
66
|
+
Free tier and the hooks: every hook runs for everyone. Restore, the time card, style loading, and Guardrails are fully free. The pre-compaction **save** is the one piece that needs Pro. On a free wallet the hook still fires, but instead of saving it prints the plain notice that saving is a Pro feature. That is not an error. Your reads, diary, and recall keep working.
|
|
58
67
|
|
|
59
68
|
Hook installation is idempotent. If you ever suspect the hooks are missing, run:
|
|
60
69
|
|
|
@@ -98,7 +107,7 @@ The part after the code is the reason in plain words. The code itself is the par
|
|
|
98
107
|
| `junk_filename` | Looks like junk (`.DS_Store`, `*.tmp`, swap files) | Warning only |
|
|
99
108
|
| `large_file` | Over 10 MB: many chunks, real cost | Warning only |
|
|
100
109
|
|
|
101
|
-
"Observed by default" means the verdict
|
|
110
|
+
"Observed by default" means the Witness reports the verdict as a `[Witness] WOULD BLOCK ...` warning line in the server log (the same place you see `[Witness] BLOCKED ...` lines) but the save proceeds, unless you turn enforcement on (below).
|
|
102
111
|
|
|
103
112
|
The two hard-block codes are different. `credential_in_content` and `secret_filename` block **regardless of any setting**, because permanent is forever. A leaked key on an immutable public chain cannot be taken back. The Witness would rather fail your save than etch your key.
|
|
104
113
|
|
|
@@ -348,7 +357,10 @@ You are on the free tier. Reads, diary, Duo, and recall all keep working. To kee
|
|
|
348
357
|
The billing check could not reach indelible.one. Saves fail closed on purpose when your plan cannot be verified. Check your connection and retry.
|
|
349
358
|
|
|
350
359
|
**"No funds" / "fund to persist"**
|
|
351
|
-
Your wallet has no sats. Chain writes carry a real (tiny) miner fee. Send a small amount of BSV to your address (`indelible-mcp status` shows it). Even one dollar covers hundreds of saves. Duo replies still work with an empty wallet; they just are not persisted until you fund.
|
|
360
|
+
Your wallet has no sats. Chain writes carry a real (tiny) miner fee. Send a small amount of BSV to your address (`indelible-mcp status` shows it). Even one dollar covers hundreds of saves. Duo replies still work with an empty wallet; they just are not persisted until you fund. If the message instead says the network's view of your wallet is incomplete, see the next entry — do not send funds for that one.
|
|
361
|
+
|
|
362
|
+
**"The network's view of your wallet is temporarily incomplete" / "Cannot reach the network to see your full wallet right now"**
|
|
363
|
+
This is not "no funds" — it is a degraded VIEW. The coins currently visible cannot fund this save, but your wallet may hold more that is not visible yet (the index is catching up, or the bridges are unreachable). Nothing was spent. Retry in a few minutes — do not add funds based on this message; the message itself says so, and adding funds it did not ask for solves nothing. If it persists, check your connection and `indelible-mcp status`.
|
|
352
364
|
|
|
353
365
|
**Restore comes back empty**
|
|
354
366
|
Work the checklist in order:
|
package/LICENSE
CHANGED
|
@@ -15,7 +15,7 @@ Additional Use Grant: You may make use of the Licensed Work for personal,
|
|
|
15
15
|
commercial blockchain storage, session saving, or
|
|
16
16
|
encrypted vault service without written permission
|
|
17
17
|
from the Licensor.
|
|
18
|
-
Change Date: August
|
|
18
|
+
Change Date: August 19, 2030
|
|
19
19
|
Change License: MIT License
|
|
20
20
|
|
|
21
21
|
Terms
|
package/README.md
CHANGED
|
@@ -12,27 +12,29 @@ Blockchain-backed memory for Claude Code **and OpenAI's Codex CLI**. Save your A
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
npm install -g indelible-mcp
|
|
15
|
-
indelible-mcp
|
|
15
|
+
indelible-mcp
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Setup
|
|
19
19
|
|
|
20
|
-
### 1. Install &
|
|
20
|
+
### 1. Install & Connect Your Wallet
|
|
21
|
+
|
|
22
|
+
Create your account at [indelible.one](https://indelible.one) first (Pro unlocks saves), then:
|
|
21
23
|
|
|
22
24
|
```bash
|
|
23
25
|
npm install -g indelible-mcp
|
|
24
|
-
indelible-mcp
|
|
26
|
+
indelible-mcp
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
Run `indelible-mcp` with no arguments — the wizard takes your private key (indelible.one → Settings → Private Key) and a PIN at a prompt, then registers your wallet with the Indelible server. For automation: `indelible-mcp setup --wif=YOUR_KEY --pin=YOUR_PIN` (clear your shell history afterward).
|
|
28
30
|
|
|
29
31
|
### 2. Fund Your Wallet
|
|
30
32
|
|
|
31
|
-
Send a small amount of BSV to the address shown after setup.
|
|
33
|
+
Send a small amount of BSV to the address shown after setup. HandCash or RockWallet both work.
|
|
32
34
|
|
|
33
35
|
### 3. Add MCP Config to Claude Code
|
|
34
36
|
|
|
35
|
-
Run `
|
|
37
|
+
Run `claude mcp add --scope user indelible -- indelible-mcp` (`--scope user` registers it for every project, not just the current folder — the setup wizard runs this for you). If you manage MCP config by hand, the entry below goes in `~/.claude.json` (user scope) or your project's `.mcp.json` — NOT in `settings.json`, which Claude Code does not read MCP servers from:
|
|
36
38
|
|
|
37
39
|
```json
|
|
38
40
|
{
|
|
@@ -72,7 +74,10 @@ Indelible automatically saves before Claude Code compacts your context, and rest
|
|
|
72
74
|
### CLI Commands
|
|
73
75
|
|
|
74
76
|
```
|
|
75
|
-
indelible-mcp setup
|
|
77
|
+
indelible-mcp Guided setup (recommended — your key is taken at a prompt,
|
|
78
|
+
never written to shell history)
|
|
79
|
+
indelible-mcp setup --wif=KEY --pin=PIN Import your key & register — automation only;
|
|
80
|
+
both values land in shell history
|
|
76
81
|
indelible-mcp save Save current session
|
|
77
82
|
indelible-mcp save --summary XYZ Save with custom summary
|
|
78
83
|
indelible-mcp strongbox See your protected raw transcripts (the host deletes them ~30 days by default)
|
|
@@ -113,7 +118,7 @@ indelible-mcp workshop --status Which of your agents this box can serve right n
|
|
|
113
118
|
## How It Works
|
|
114
119
|
|
|
115
120
|
1. Your conversation is encrypted locally with your WIF key
|
|
116
|
-
2. A minimal OP_RETURN transaction is built containing only `{protocol, encrypted}` —
|
|
121
|
+
2. A minimal OP_RETURN transaction is built containing only `{protocol, encrypted, wrap_owner}` — the protocol tag, your encrypted conversation, and an encrypted key-wrap that lets you share the session later. No plaintext metadata on-chain
|
|
117
122
|
3. The signed transaction is broadcast via Indelible's federation bridges
|
|
118
123
|
4. Session metadata is automatically indexed across all federation bridges for fast retrieval
|
|
119
124
|
5. Signing happens locally on your machine
|
|
@@ -129,7 +134,7 @@ The federation mesh is powered by [Relay Federation](https://github.com/zcoolz/r
|
|
|
129
134
|
## Security
|
|
130
135
|
|
|
131
136
|
- **Zero-knowledge encryption** - your WIF-derived AES-256-GCM key encrypts all data before it touches the network
|
|
132
|
-
- **Privacy-hardened transactions** - OP_RETURN
|
|
137
|
+
- **Privacy-hardened transactions** - OP_RETURN carries only the protocol tag, ciphertext, and an encrypted key-wrap — no plaintext metadata on-chain
|
|
133
138
|
- **You hold your key** - the wallet that signs and encrypts your data is yours
|
|
134
139
|
- **Immutable storage** - once on BSV, your data cannot be altered or deleted
|
|
135
140
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1025,7 +1025,8 @@ function claimRefusalMessage(claim2) {
|
|
|
1025
1025
|
if (r === "WALLET_VIEW_DEGRADED") {
|
|
1026
1026
|
const seen = Number.isFinite(claim2.visibleSats) ? `${claim2.visibleSats} sats` : "the coins known locally";
|
|
1027
1027
|
const needs = Number.isFinite(claim2.need) ? ` (needs about ${claim2.need} sats)` : "";
|
|
1028
|
-
|
|
1028
|
+
const opener = claim2.operator_reason === "INDEX_PARTIAL" ? "The network's view of your wallet is temporarily incomplete (the index is catching up)." : "Cannot reach the network to see your full wallet right now.";
|
|
1029
|
+
return `${opener} The currently-visible coins (${seen}) cannot fund this save${needs}, but your wallet may hold more that is not visible yet. Nothing was spent. Retry in a few minutes \u2014 do not add funds based on this message.`;
|
|
1029
1030
|
}
|
|
1030
1031
|
if (r === "NO_COINS") return "No UTXOs available. Fund your wallet.";
|
|
1031
1032
|
return "No UTXOs available. Fund your wallet.";
|
|
@@ -1100,11 +1101,13 @@ async function claimSpendableUtxos(address, bridgeFallback, opts = {}) {
|
|
|
1100
1101
|
}
|
|
1101
1102
|
}
|
|
1102
1103
|
let _bridgeDegraded = false;
|
|
1104
|
+
let _bridgePartial = null;
|
|
1103
1105
|
const bridgeView = async () => {
|
|
1104
1106
|
if (!bridgeFallback) return [];
|
|
1105
1107
|
try {
|
|
1106
1108
|
const v = await bridgeFallback(address);
|
|
1107
1109
|
_bridgeDegraded = false;
|
|
1110
|
+
_bridgePartial = v?.coverage?.known && v.coverage.partial ? v.coverage : null;
|
|
1108
1111
|
return v;
|
|
1109
1112
|
} catch {
|
|
1110
1113
|
_bridgeDegraded = true;
|
|
@@ -1121,6 +1124,17 @@ async function claimSpendableUtxos(address, bridgeFallback, opts = {}) {
|
|
|
1121
1124
|
} else {
|
|
1122
1125
|
candidates = unionCandidates(address, await bridgeView());
|
|
1123
1126
|
}
|
|
1127
|
+
if (_phase === "union" && candidates.length === 0 && (_bridgeDegraded || _bridgePartial)) {
|
|
1128
|
+
return {
|
|
1129
|
+
refused: true,
|
|
1130
|
+
reason: "WALLET_VIEW_DEGRADED",
|
|
1131
|
+
operator_reason: _bridgeDegraded ? "BRIDGE_UNREACHABLE" : "INDEX_PARTIAL",
|
|
1132
|
+
..._bridgePartial ? { index_holes: _bridgePartial.holes, index_source: _bridgePartial.source } : {},
|
|
1133
|
+
underlying: "NO_COINS",
|
|
1134
|
+
visibleSats: 0,
|
|
1135
|
+
reservedSats: 0
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1124
1138
|
const claimed = await withFileLock(LOCK_PATH, async () => {
|
|
1125
1139
|
const store = readReservations();
|
|
1126
1140
|
releaseDeadPreSign(store, now);
|
|
@@ -1137,8 +1151,17 @@ async function claimSpendableUtxos(address, bridgeFallback, opts = {}) {
|
|
|
1137
1151
|
if (wholeWallet.ok) return { busy: true };
|
|
1138
1152
|
const { ok: _ok, ...evidence } = selected;
|
|
1139
1153
|
const reservedSats = (candidates || []).filter((u) => reserved.has(inputKey(u))).reduce((s, u) => s + (u?.value || 0), 0);
|
|
1140
|
-
if (_bridgeDegraded && evidence.reason !== "TX_TOO_LARGE") {
|
|
1141
|
-
return {
|
|
1154
|
+
if ((_bridgeDegraded || _bridgePartial) && evidence.reason !== "TX_TOO_LARGE") {
|
|
1155
|
+
return {
|
|
1156
|
+
refused: true,
|
|
1157
|
+
reason: "WALLET_VIEW_DEGRADED",
|
|
1158
|
+
operator_reason: _bridgeDegraded ? "BRIDGE_UNREACHABLE" : "INDEX_PARTIAL",
|
|
1159
|
+
..._bridgePartial ? { index_holes: _bridgePartial.holes, index_source: _bridgePartial.source } : {},
|
|
1160
|
+
underlying: evidence.reason,
|
|
1161
|
+
need: evidence.need,
|
|
1162
|
+
visibleSats: evidence.available,
|
|
1163
|
+
reservedSats
|
|
1164
|
+
};
|
|
1142
1165
|
}
|
|
1143
1166
|
return { refused: true, ...evidence, reservedSats };
|
|
1144
1167
|
}
|
|
@@ -1597,14 +1620,27 @@ async function checkHealth() {
|
|
|
1597
1620
|
return false;
|
|
1598
1621
|
}
|
|
1599
1622
|
}
|
|
1623
|
+
function coverageFromHeaders(headers) {
|
|
1624
|
+
try {
|
|
1625
|
+
const src = headers?.get?.("x-utxo-source") || null;
|
|
1626
|
+
const holesRaw = headers?.get?.("x-utxo-holes");
|
|
1627
|
+
if (src === null && (holesRaw === null || holesRaw === void 0)) return { known: false, partial: false, source: null, holes: 0 };
|
|
1628
|
+
const holes = Number.parseInt(holesRaw || "0", 10) || 0;
|
|
1629
|
+
const partial = holes > 0 || /partial/i.test(src || "");
|
|
1630
|
+
return { known: true, partial, source: src, holes };
|
|
1631
|
+
} catch {
|
|
1632
|
+
return { known: false, partial: false, source: null, holes: 0 };
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1600
1635
|
async function getUtxos(address) {
|
|
1601
1636
|
const path = `/api/address/${address}/unspent`;
|
|
1602
1637
|
const res = await spvFetch(path);
|
|
1603
1638
|
const first = await res.json();
|
|
1639
|
+
const firstCoverage = coverageFromHeaders(res.headers);
|
|
1604
1640
|
if (Array.isArray(first) && first.length > 0) {
|
|
1605
1641
|
process.stderr.write(`[MCP] UTXOs from bridge: ${first.length}
|
|
1606
1642
|
`);
|
|
1607
|
-
return first;
|
|
1643
|
+
return withCoverage(first, firstCoverage);
|
|
1608
1644
|
}
|
|
1609
1645
|
let reached = 0;
|
|
1610
1646
|
try {
|
|
@@ -1615,8 +1651,9 @@ async function getUtxos(address) {
|
|
|
1615
1651
|
headers: { "X-API-Key": bridge.apiKey }
|
|
1616
1652
|
});
|
|
1617
1653
|
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
1618
|
-
return { name: bridge.name, utxos: await r.json() };
|
|
1654
|
+
return { name: bridge.name, utxos: await r.json(), coverage: coverageFromHeaders(r.headers) };
|
|
1619
1655
|
}));
|
|
1656
|
+
let anyPartial = null;
|
|
1620
1657
|
for (const s of settled) {
|
|
1621
1658
|
if (s.status !== "fulfilled") continue;
|
|
1622
1659
|
reached++;
|
|
@@ -1624,8 +1661,14 @@ async function getUtxos(address) {
|
|
|
1624
1661
|
if (Array.isArray(u) && u.length > 0) {
|
|
1625
1662
|
process.stderr.write(`[MCP] UTXOs recovered via fan-out from ${s.value.name}: ${u.length}
|
|
1626
1663
|
`);
|
|
1627
|
-
return u;
|
|
1664
|
+
return withCoverage(u, s.value.coverage);
|
|
1628
1665
|
}
|
|
1666
|
+
if (s.value.coverage?.known && s.value.coverage.partial) anyPartial = s.value.coverage;
|
|
1667
|
+
}
|
|
1668
|
+
if (anyPartial) {
|
|
1669
|
+
process.stderr.write(`[MCP] UTXOs: 0 across ${reached} bridge(s), but a bridge reports a PARTIAL index (holes=${anyPartial.holes})
|
|
1670
|
+
`);
|
|
1671
|
+
return withCoverage([], anyPartial);
|
|
1629
1672
|
}
|
|
1630
1673
|
} catch (e) {
|
|
1631
1674
|
process.stderr.write(`[MCP] UTXO fan-out error: ${e.message}
|
|
@@ -1633,7 +1676,7 @@ async function getUtxos(address) {
|
|
|
1633
1676
|
}
|
|
1634
1677
|
process.stderr.write(`[MCP] UTXOs: 0 across ${reached} bridge(s)
|
|
1635
1678
|
`);
|
|
1636
|
-
return Array.isArray(first) ? first : [];
|
|
1679
|
+
return withCoverage(Array.isArray(first) ? first : [], firstCoverage);
|
|
1637
1680
|
}
|
|
1638
1681
|
async function getAddressHistory(address) {
|
|
1639
1682
|
const res = await spvFetch(`/api/address/${address}/history`);
|
|
@@ -2169,7 +2212,7 @@ function extractEncryptedFromTx(tx) {
|
|
|
2169
2212
|
const match = str.match(/([A-Za-z0-9+/=]{12,}):([A-Za-z0-9+/=]{20,}):([A-Za-z0-9+/=]{20,})/);
|
|
2170
2213
|
return match ? match[0] : null;
|
|
2171
2214
|
}
|
|
2172
|
-
var SEED_BRIDGES, OLD_BRIDGE_IPS, bridgeHealth, HEALTH_CHECK_INTERVAL, MAX_FAILURES, migrationDone, SOURCE_FETCH_CONCURRENCY, ARC_NETWORK_STATUSES, StoredError, _stickyBridge, STICKY_WINDOW_MS;
|
|
2215
|
+
var SEED_BRIDGES, OLD_BRIDGE_IPS, bridgeHealth, HEALTH_CHECK_INTERVAL, MAX_FAILURES, migrationDone, withCoverage, SOURCE_FETCH_CONCURRENCY, ARC_NETWORK_STATUSES, StoredError, _stickyBridge, STICKY_WINDOW_MS;
|
|
2173
2216
|
var init_spv = __esm({
|
|
2174
2217
|
"mcp-server/lib/spv.js"() {
|
|
2175
2218
|
init_config_customer();
|
|
@@ -2196,6 +2239,13 @@ var init_spv = __esm({
|
|
|
2196
2239
|
HEALTH_CHECK_INTERVAL = 6e4;
|
|
2197
2240
|
MAX_FAILURES = 3;
|
|
2198
2241
|
migrationDone = false;
|
|
2242
|
+
withCoverage = (utxos, coverage) => {
|
|
2243
|
+
try {
|
|
2244
|
+
Object.defineProperty(utxos, "coverage", { value: coverage, enumerable: false });
|
|
2245
|
+
} catch {
|
|
2246
|
+
}
|
|
2247
|
+
return utxos;
|
|
2248
|
+
};
|
|
2199
2249
|
SOURCE_FETCH_CONCURRENCY = 8;
|
|
2200
2250
|
ARC_NETWORK_STATUSES = /* @__PURE__ */ new Set([
|
|
2201
2251
|
// Tightened 2026-06-17 to match the relay-federation bridge: a node must have
|
|
@@ -16058,6 +16108,11 @@ function installHooks() {
|
|
|
16058
16108
|
settings.hooks.SessionEnd.push({ hooks: [{ type: "command", command: sbCmd("session-end-strongbox") }] });
|
|
16059
16109
|
installed.push("SessionEnd:strongbox");
|
|
16060
16110
|
}
|
|
16111
|
+
if (!hasSb("UserPromptSubmit", "user-prompt-timestamp")) {
|
|
16112
|
+
if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
|
|
16113
|
+
settings.hooks.UserPromptSubmit.push({ hooks: [{ type: "command", command: sbCmd("user-prompt-timestamp") }] });
|
|
16114
|
+
installed.push("UserPromptSubmit:wristwatch");
|
|
16115
|
+
}
|
|
16061
16116
|
writeFileSync18(settingsPath, JSON.stringify(settings, null, 2));
|
|
16062
16117
|
const inventory = [];
|
|
16063
16118
|
for (const [event, entries] of Object.entries(settings.hooks || {})) {
|
|
@@ -17454,7 +17509,7 @@ Answer THAT message and nothing else \u2014 the wire also carries unrelated conv
|
|
|
17454
17509
|
}
|
|
17455
17510
|
function printHelp() {
|
|
17456
17511
|
console.log(`
|
|
17457
|
-
Indelible MCP \u2014 Blockchain memory for Claude Code (v5.8.
|
|
17512
|
+
Indelible MCP \u2014 Blockchain memory for Claude Code (v5.8.3)
|
|
17458
17513
|
|
|
17459
17514
|
Setup:
|
|
17460
17515
|
indelible-mcp Set up interactively (recommended \u2014 your key is never written to shell history)
|
|
@@ -17783,7 +17838,7 @@ function readStdin() {
|
|
|
17783
17838
|
}
|
|
17784
17839
|
var SERVER_INFO = {
|
|
17785
17840
|
name: "indelible",
|
|
17786
|
-
version: "5.8.
|
|
17841
|
+
version: "5.8.3",
|
|
17787
17842
|
description: "Blockchain-backed memory and code storage for Claude Code"
|
|
17788
17843
|
};
|
|
17789
17844
|
var TOOLS = [
|
|
@@ -18915,6 +18970,25 @@ if (args[0] === "hook") {
|
|
|
18915
18970
|
}
|
|
18916
18971
|
process.exit(0);
|
|
18917
18972
|
})();
|
|
18973
|
+
} else if (args[1] === "user-prompt-timestamp") {
|
|
18974
|
+
try {
|
|
18975
|
+
const stamp = (/* @__PURE__ */ new Date()).toLocaleString(void 0, {
|
|
18976
|
+
weekday: "long",
|
|
18977
|
+
month: "long",
|
|
18978
|
+
day: "numeric",
|
|
18979
|
+
hour: "numeric",
|
|
18980
|
+
minute: "2-digit",
|
|
18981
|
+
hour12: true
|
|
18982
|
+
});
|
|
18983
|
+
process.stdout.write(JSON.stringify({
|
|
18984
|
+
hookSpecificOutput: {
|
|
18985
|
+
hookEventName: "UserPromptSubmit",
|
|
18986
|
+
additionalContext: `\u231A NOW: ${stamp} (your machine's clock, verified this turn \u2014 never infer time)`
|
|
18987
|
+
}
|
|
18988
|
+
}));
|
|
18989
|
+
} catch {
|
|
18990
|
+
}
|
|
18991
|
+
process.exit(0);
|
|
18918
18992
|
} else if (args[1] === "pre-tool-use") {
|
|
18919
18993
|
runPreToolUseGuard();
|
|
18920
18994
|
} else if (args[1] === "pre-compact-strongbox" || args[1] === "session-end-strongbox") {
|
|
@@ -18957,7 +19031,13 @@ if (args[0] === "hook") {
|
|
|
18957
19031
|
printHelp();
|
|
18958
19032
|
} else if (args[0] === "--show-config") {
|
|
18959
19033
|
console.log(`
|
|
18960
|
-
|
|
19034
|
+
Easiest: run this once and you're registered in every project:
|
|
19035
|
+
|
|
19036
|
+
claude mcp add --scope user indelible -- indelible-mcp
|
|
19037
|
+
|
|
19038
|
+
Managing MCP config by hand instead? This entry goes in ~/.claude.json (user scope)
|
|
19039
|
+
or your project's .mcp.json \u2014 NOT in settings.json, which Claude Code does not read
|
|
19040
|
+
MCP servers from:
|
|
18961
19041
|
|
|
18962
19042
|
{
|
|
18963
19043
|
"mcpServers": {
|
|
@@ -18966,6 +19046,9 @@ Add this to your Claude Code settings.json:
|
|
|
18966
19046
|
}
|
|
18967
19047
|
}
|
|
18968
19048
|
}
|
|
19049
|
+
|
|
19050
|
+
Cursor: the same entry goes in ~/.cursor/mcp.json.
|
|
19051
|
+
Codex CLI: add to ~/.codex/config.toml: [mcp_servers.indelible] command = "indelible-mcp"
|
|
18969
19052
|
`);
|
|
18970
19053
|
} else if (args.length > 0) {
|
|
18971
19054
|
runCli(args).catch((e) => {
|