indelible-mcp 5.5.1 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLI_HANDBOOK.md CHANGED
@@ -67,7 +67,10 @@ indelible-mcp drift wait --from=codex # hold the wire for the next message
67
67
  indelible-mcp drift loop --as=claude # run a seat's heartbeat
68
68
  indelible-mcp drift summon # THE SUMMONER: watches the wire; when a message sits
69
69
  # unanswered, spawns a FRESH pilot via its own vendor
70
- # CLI to read the wire and reply. Both pilots, no setup.
70
+ # CLI to reply. Both pilots, no setup. The message it
71
+ # must answer travels with the summons, so a busy wire
72
+ # cannot hand it the wrong conversation.
73
+ indelible-mcp drift summon --for=codex-qa # work ONE named seat's lane (see Named Seats below)
71
74
  indelible-mcp drift pause | resume # YOUR brake — freezes everything, works with no wallet
72
75
  indelible-mcp drift listen "stop, listen" # freeze both AND hand them your message
73
76
  ```
@@ -92,6 +95,19 @@ indelible-mcp drift post --as=codex --to=claude-builder "a task ONLY the builder
92
95
 
93
96
  - `--to` puts a name on the envelope: only the seat it names will answer it. No `--to` = anyone may answer.
94
97
  - Summoners answer with the right vendor automatically (a `claude-anything` seat runs the Claude CLI).
98
+
99
+ **Two conversations at once — the private lane.** Point a summoner at a named seat and it works only
100
+ that seat's thread:
101
+
102
+ ```
103
+ indelible-mcp drift summon --once --for=codex-parity # answers ONLY letters addressed to codex-parity
104
+ ```
105
+
106
+ Naming a seat this way turns on its **lane**: it answers what was addressed to it and leaves everything
107
+ else — including other threads' open messages — alone. That is what lets two windows run two separate
108
+ conversations on one wire without stealing each other's work. Plain `claude` / `codex` seats keep the
109
+ shared line and answer anything unaddressed; add `--lane` to scope one of them too, or `--lane=off` to
110
+ put a named seat back on the shared line.
95
111
  - No pile-ups by construction: however many seats are listening, the ledger admits exactly ONE answer per message.
96
112
  - A guard coming on duty answers the current conversation and everything after — it never digs up old history (pass `--backlog=all` to a summoner if you truly want the past drained; it spends per letter).
97
113
 
@@ -288,6 +288,8 @@ stay on the record and the decision lands on YOUR desk, and even your ruling del
288
288
 
289
289
  **Named seats (5.4.0) — a staff, not a pair.** Seats can carry names now: `claude-builder`, `claude-reviewer`, `codex-auditor` — so two Claudes (or any mix) hold distinct seats on one wire, and a message can be addressed to exactly one of them (`--to=claude-builder`: only that seat answers). However many seats are listening, the ledger admits exactly ONE answer per message — a bigger staff never means duplicate answers or duplicate spend. And a summoner coming on duty answers the current conversation, never the deep past.
290
290
 
291
+ Point a summoner at a named seat — `indelible-mcp drift summon --for=codex-auditor` — and it works only that seat's **lane**: it answers what was addressed to it and leaves every other thread alone, so two windows can run two separate conversations on one wire without stealing each other's work. Whatever it is hired to answer travels with the summons, so a busy wire cannot hand a fresh pilot the wrong conversation.
292
+
291
293
  **Your books.** `indelible-mcp drift ledger` shows every mind ever summoned on your account — when, which seat, what it was hired to answer, and how it ended. Nothing hires without a paper trail.
292
294
 
293
295
  Your brake beats everything: `indelible-mcp drift pause` (or just tell either pilot "pause the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indelible-mcp",
3
- "version": "5.5.1",
3
+ "version": "5.6.0",
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
@@ -4280,14 +4280,14 @@ async function loadContext(numSessions = 5, pagination = null, opts = {}) {
4280
4280
  let resolvedHistoryPath = "~/.claude/projects/<project>/memory/session-history.md";
4281
4281
  try {
4282
4282
  const projectsDir = join13(homedir13(), ".claude", "projects");
4283
- const { readdirSync: readdirSync6, statSync: statSync8 } = await import("fs");
4283
+ const { readdirSync: readdirSync7, statSync: statSync8 } = await import("fs");
4284
4284
  let newestProject = null;
4285
4285
  let newestTime = 0;
4286
- for (const project of readdirSync6(projectsDir)) {
4286
+ for (const project of readdirSync7(projectsDir)) {
4287
4287
  const projectPath = join13(projectsDir, project);
4288
4288
  try {
4289
4289
  if (!statSync8(projectPath).isDirectory()) continue;
4290
- for (const file of readdirSync6(projectPath)) {
4290
+ for (const file of readdirSync7(projectPath)) {
4291
4291
  if (!file.endsWith(".jsonl")) continue;
4292
4292
  const fStat = statSync8(join13(projectPath, file));
4293
4293
  if (fStat.mtimeMs > newestTime) {
@@ -6353,6 +6353,101 @@ var init_fetch_pack = __esm({
6353
6353
  }
6354
6354
  });
6355
6355
 
6356
+ // mcp-server/lib/agent-possession.js
6357
+ var agent_possession_exports = {};
6358
+ __export(agent_possession_exports, {
6359
+ buildRegisterBody: () => buildRegisterBody,
6360
+ buildRetireBody: () => buildRetireBody,
6361
+ canonicalAgentName: () => canonicalAgentName2,
6362
+ listLocalAgents: () => listLocalAgents,
6363
+ popMessage: () => popMessage,
6364
+ signMessage: () => signMessage
6365
+ });
6366
+ import { readFileSync as readFileSync20, readdirSync as readdirSync3, existsSync as existsSync27 } from "node:fs";
6367
+ import { PrivateKey as PrivateKey15, PublicKey as PublicKey4 } from "@bsv/sdk";
6368
+ function popMessage({ purpose = "agent-pop", owner, name, domain, keyHex, nonce }) {
6369
+ return `indelible/${purpose}/v1|${owner}|${name}|${domain}|${keyHex}|${nonce}`;
6370
+ }
6371
+ function canonicalAgentName2(name) {
6372
+ return String(name || "").normalize("NFC").toLowerCase().trim();
6373
+ }
6374
+ function signMessage(wif, message) {
6375
+ const priv = PrivateKey15.fromWif(wif);
6376
+ return priv.sign(Array.from(Buffer.from(message, "utf8"))).toDER("hex");
6377
+ }
6378
+ function listLocalAgents(scope = "") {
6379
+ const dir = agentsDir(scope);
6380
+ if (!existsSync27(dir)) return [];
6381
+ const out = [];
6382
+ for (const name of readdirSync3(dir)) {
6383
+ const p = agentIdentityPath(scope, name);
6384
+ if (!existsSync27(p)) continue;
6385
+ try {
6386
+ const id = JSON.parse(readFileSync20(p, "utf8"));
6387
+ if (!id.identity_key_hex || !id.wif) continue;
6388
+ let derived = null;
6389
+ try {
6390
+ derived = PublicKey4.fromString(id.identity_key_hex).toAddress();
6391
+ } catch {
6392
+ }
6393
+ out.push({
6394
+ name: canonicalAgentName2(id.agent_name || name),
6395
+ domain: id.domain || null,
6396
+ identity_key_hex: id.identity_key_hex,
6397
+ address: derived,
6398
+ self_consistent: Boolean(derived && (!id.address || derived === id.address)),
6399
+ path: p
6400
+ });
6401
+ } catch {
6402
+ }
6403
+ }
6404
+ return out.sort((a, b) => a.name.localeCompare(b.name));
6405
+ }
6406
+ function buildRegisterBody({ agent, nonce, owner, ownerWif, scope = "" }) {
6407
+ const id = JSON.parse(readFileSync20(agentIdentityPath(scope, agent.name), "utf8"));
6408
+ const name = canonicalAgentName2(id.agent_name || agent.name);
6409
+ const domain = agent.domain || id.domain;
6410
+ const keyHex = id.identity_key_hex;
6411
+ const msg = popMessage({ owner, name, domain, keyHex, nonce });
6412
+ const ownerPriv = PrivateKey15.fromWif(ownerWif);
6413
+ if (ownerPriv.toPublicKey().toAddress() !== owner) {
6414
+ return { ok: false, reason: "owner_key_mismatch", detail: `this box's wallet is not ${owner}` };
6415
+ }
6416
+ return {
6417
+ ok: true,
6418
+ body: {
6419
+ nonce,
6420
+ owner_pubkey: ownerPriv.toPublicKey().toString(),
6421
+ owner_signature: signMessage(ownerWif, msg),
6422
+ agent_signature: signMessage(id.wif, msg),
6423
+ agent: {
6424
+ name,
6425
+ domain,
6426
+ identity_key_hex: keyHex,
6427
+ // address is a CROSS-CHECK only — the server derives pay_to from the key itself and
6428
+ // refuses a mismatch. We send it so a mismatch surfaces loudly instead of silently.
6429
+ address: id.address || PublicKey4.fromString(keyHex).toAddress(),
6430
+ display_name: id.display_name || void 0,
6431
+ role: id.role || void 0
6432
+ }
6433
+ }
6434
+ };
6435
+ }
6436
+ function buildRetireBody({ name, nonce, owner, ownerWif }) {
6437
+ const canonical = canonicalAgentName2(name);
6438
+ const msg = popMessage({ purpose: "agent-retire", owner, name: canonical, domain: "", keyHex: "", nonce });
6439
+ const ownerPriv = PrivateKey15.fromWif(ownerWif);
6440
+ if (ownerPriv.toPublicKey().toAddress() !== owner) {
6441
+ return { ok: false, reason: "owner_key_mismatch", detail: `this box's wallet is not ${owner}` };
6442
+ }
6443
+ return { ok: true, body: { nonce, owner_pubkey: ownerPriv.toPublicKey().toString(), owner_signature: signMessage(ownerWif, msg) } };
6444
+ }
6445
+ var init_agent_possession = __esm({
6446
+ "mcp-server/lib/agent-possession.js"() {
6447
+ init_tenant_paths();
6448
+ }
6449
+ });
6450
+
6356
6451
  // mcp-server/lib/storefront-workshop.js
6357
6452
  var storefront_workshop_exports = {};
6358
6453
  __export(storefront_workshop_exports, {
@@ -6360,14 +6455,14 @@ __export(storefront_workshop_exports, {
6360
6455
  reportDeliverable: () => reportDeliverable,
6361
6456
  workOrders: () => workOrders
6362
6457
  });
6363
- import { readFileSync as readFileSync20 } from "fs";
6364
- import { readdir as readdir4 } from "fs/promises";
6458
+ import { readFileSync as readFileSync21 } from "fs";
6459
+ import { readdir as readdir4, readFile as readFile11 } from "fs/promises";
6365
6460
  import { join as join28 } from "path";
6366
6461
  import { homedir as homedir24 } from "os";
6367
6462
  function cfg() {
6368
6463
  let c = {};
6369
6464
  try {
6370
- c = JSON.parse(readFileSync20(join28(homedir24(), ".indelible", "config.json"), "utf8"));
6465
+ c = JSON.parse(readFileSync21(join28(homedir24(), ".indelible", "config.json"), "utf8"));
6371
6466
  } catch {
6372
6467
  }
6373
6468
  return c;
@@ -6396,13 +6491,21 @@ async function reportDeliverable({ scope = "", apiUrl, apiKey } = {}) {
6396
6491
  if (!url || !key) return { ok: false, error: "no api_url/api_key configured" };
6397
6492
  try {
6398
6493
  const agents = await deliverableAgents(scope);
6494
+ const keyed = {};
6495
+ for (const name of agents) {
6496
+ try {
6497
+ const id = JSON.parse(await readFile11(agentIdentityPath(scope, name), "utf8"));
6498
+ if (id && typeof id.identity_key_hex === "string") keyed[name] = id.identity_key_hex;
6499
+ } catch {
6500
+ }
6501
+ }
6399
6502
  const r = await fetch(`${url}/api/storefront/deliverable`, {
6400
6503
  method: "POST",
6401
6504
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
6402
- body: JSON.stringify({ agents }),
6505
+ body: JSON.stringify({ agents, keyed }),
6403
6506
  signal: AbortSignal.timeout(1e4)
6404
6507
  });
6405
- return { ok: r.ok, reported: agents.length, agents };
6508
+ return { ok: r.ok, reported: agents.length, agents, keyed: Object.keys(keyed).length };
6406
6509
  } catch (e) {
6407
6510
  return { ok: false, error: e.message.slice(0, 160) };
6408
6511
  }
@@ -6485,14 +6588,14 @@ __export(owner_control_exports, {
6485
6588
  stop: () => stop,
6486
6589
  stopAndListen: () => stopAndListen
6487
6590
  });
6488
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync10, existsSync as existsSync27, mkdirSync as mkdirSync11, renameSync as renameSync6 } from "node:fs";
6591
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync10, existsSync as existsSync28, mkdirSync as mkdirSync11, renameSync as renameSync6 } from "node:fs";
6489
6592
  import { join as join29, dirname as dirname11 } from "node:path";
6490
6593
  import { homedir as homedir25 } from "node:os";
6491
6594
  function readControl(path = CONTROL_PATH) {
6492
- if (!existsSync27(path)) return { state: "running", reason: "no control file \u2014 normal operation", source: "default" };
6595
+ if (!existsSync28(path)) return { state: "running", reason: "no control file \u2014 normal operation", source: "default" };
6493
6596
  let raw;
6494
6597
  try {
6495
- raw = readFileSync21(path, "utf8");
6598
+ raw = readFileSync22(path, "utf8");
6496
6599
  } catch (e) {
6497
6600
  return { state: "paused", reason: `control unreadable: ${e.message}`, source: "fail-safe" };
6498
6601
  }
@@ -6754,7 +6857,7 @@ var store_exports = {};
6754
6857
  __export(store_exports, {
6755
6858
  DriftStore: () => DriftStore
6756
6859
  });
6757
- import { readFileSync as readFileSync22, existsSync as existsSync28, mkdirSync as mkdirSync12, appendFileSync as appendFileSync4 } from "node:fs";
6860
+ import { readFileSync as readFileSync23, existsSync as existsSync29, mkdirSync as mkdirSync12, appendFileSync as appendFileSync4 } from "node:fs";
6758
6861
  import { join as join30, dirname as dirname12 } from "node:path";
6759
6862
  import { homedir as homedir26 } from "node:os";
6760
6863
  var DriftStore;
@@ -6774,8 +6877,8 @@ var init_store = __esm({
6774
6877
  this._load();
6775
6878
  }
6776
6879
  _load() {
6777
- if (!existsSync28(this.path)) return;
6778
- for (const line of readFileSync22(this.path, "utf8").split("\n")) {
6880
+ if (!existsSync29(this.path)) return;
6881
+ for (const line of readFileSync23(this.path, "utf8").split("\n")) {
6779
6882
  if (!line.trim()) continue;
6780
6883
  let ev;
6781
6884
  try {
@@ -6853,7 +6956,7 @@ __export(discovery_exports, {
6853
6956
  healthyPredicate: () => healthyPredicate,
6854
6957
  publishEvent: () => publishEvent
6855
6958
  });
6856
- import { writeFileSync as writeFileSync11, readdirSync as readdirSync3, readFileSync as readFileSync23, mkdirSync as mkdirSync13, renameSync as renameSync7, existsSync as existsSync29 } from "node:fs";
6959
+ import { writeFileSync as writeFileSync11, readdirSync as readdirSync4, readFileSync as readFileSync24, mkdirSync as mkdirSync13, renameSync as renameSync7, existsSync as existsSync30 } from "node:fs";
6857
6960
  import { join as join31 } from "node:path";
6858
6961
  import { createHash as createHash7 } from "node:crypto";
6859
6962
  function publishEvent(mailboxDir, ev, { hint = null } = {}) {
@@ -6919,8 +7022,8 @@ var init_discovery = __esm({
6919
7022
  async poll() {
6920
7023
  const hints = this._pendingHints.splice(0);
6921
7024
  for (const h of hints) await this._resolveFile(`${h.locator}.json`);
6922
- if (this.catchUpEnabled && existsSync29(this.dir)) {
6923
- for (const f of readdirSync3(this.dir)) {
7025
+ if (this.catchUpEnabled && existsSync30(this.dir)) {
7026
+ for (const f of readdirSync4(this.dir)) {
6924
7027
  if (!f.endsWith(".json")) continue;
6925
7028
  await this._resolveFile(f);
6926
7029
  }
@@ -6929,10 +7032,10 @@ var init_discovery = __esm({
6929
7032
  async _resolveFile(fname) {
6930
7033
  if (this._seenFiles.has(fname)) return;
6931
7034
  const p = join31(this.dir, fname);
6932
- if (!existsSync29(p)) return;
7035
+ if (!existsSync30(p)) return;
6933
7036
  let ev;
6934
7037
  try {
6935
- ev = JSON.parse(readFileSync23(p, "utf8"));
7038
+ ev = JSON.parse(readFileSync24(p, "utf8"));
6936
7039
  } catch {
6937
7040
  return;
6938
7041
  }
@@ -6949,7 +7052,7 @@ var init_discovery = __esm({
6949
7052
  }
6950
7053
  /** The FD-B honesty check: everything durable vs everything discovered. */
6951
7054
  audit() {
6952
- const durable = existsSync29(this.dir) ? readdirSync3(this.dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5)) : [];
7055
+ const durable = existsSync30(this.dir) ? readdirSync4(this.dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5)) : [];
6953
7056
  const have = new Set(this.store.all().map((e) => e.event_id));
6954
7057
  const missing = durable.filter((id) => !have.has(id));
6955
7058
  return { durableCount: durable.length, discoveredCount: have.size, missing };
@@ -6994,7 +7097,7 @@ var init_refcrypt = __esm({
6994
7097
  });
6995
7098
 
6996
7099
  // mcp-server/lib/drift/outbox.js
6997
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync12, existsSync as existsSync30, mkdirSync as mkdirSync14, renameSync as renameSync8, rmSync as rmSync2, readdirSync as readdirSync4 } from "node:fs";
7100
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync12, existsSync as existsSync31, mkdirSync as mkdirSync14, renameSync as renameSync8, rmSync as rmSync2, readdirSync as readdirSync5 } from "node:fs";
6998
7101
  import { join as join32, dirname as dirname13 } from "node:path";
6999
7102
  var Outbox;
7000
7103
  var init_outbox = __esm({
@@ -7028,8 +7131,8 @@ var init_outbox = __esm({
7028
7131
  /** Publish one pending event and mark it sent. Idempotent at every seam. */
7029
7132
  publishOne(id, { faults = {} } = {}) {
7030
7133
  const p = this._pendingPath(id);
7031
- if (!existsSync30(p)) return { done: existsSync30(this._sentPath(id)), reason: "no-pending" };
7032
- const ev = JSON.parse(readFileSync24(p, "utf8"));
7134
+ if (!existsSync31(p)) return { done: existsSync31(this._sentPath(id)), reason: "no-pending" };
7135
+ const ev = JSON.parse(readFileSync25(p, "utf8"));
7033
7136
  if (faults.crashBeforePublish) throw new Error("FAULT: crash after pending, before publish");
7034
7137
  publishEvent(this.mailbox, ev);
7035
7138
  if (faults.crashBeforeSentMark) throw new Error("FAULT: crash after publish, before sent-mark");
@@ -7040,7 +7143,7 @@ var init_outbox = __esm({
7040
7143
  /** Restart/next-beat flush: complete every owed publication. Never throws past one entry. */
7041
7144
  flush({ faults = {} } = {}) {
7042
7145
  const results = [];
7043
- for (const f of readdirSync4(this.dir)) {
7146
+ for (const f of readdirSync5(this.dir)) {
7044
7147
  if (!f.endsWith(".pending.json")) continue;
7045
7148
  const id = f.slice(0, -".pending.json".length);
7046
7149
  try {
@@ -7052,7 +7155,7 @@ var init_outbox = __esm({
7052
7155
  return results;
7053
7156
  }
7054
7157
  pendingIds() {
7055
- return readdirSync4(this.dir).filter((f) => f.endsWith(".pending.json")).map((f) => f.slice(0, -".pending.json".length));
7158
+ return readdirSync5(this.dir).filter((f) => f.endsWith(".pending.json")).map((f) => f.slice(0, -".pending.json".length));
7056
7159
  }
7057
7160
  /**
7058
7161
  * Codex round-2 DEBT #1 (wire c0c12fae): store.append and addPending are two
@@ -7067,9 +7170,9 @@ var init_outbox = __esm({
7067
7170
  const recovered = [];
7068
7171
  for (const ev of storeEvents) {
7069
7172
  if (ev.actor?.host !== host) continue;
7070
- if (existsSync30(join32(this.mailbox, `${ev.event_id}.json`))) continue;
7071
- if (existsSync30(this._sentPath(ev.event_id))) continue;
7072
- if (existsSync30(this._pendingPath(ev.event_id))) continue;
7173
+ if (existsSync31(join32(this.mailbox, `${ev.event_id}.json`))) continue;
7174
+ if (existsSync31(this._sentPath(ev.event_id))) continue;
7175
+ if (existsSync31(this._pendingPath(ev.event_id))) continue;
7073
7176
  this.addPending(ev);
7074
7177
  recovered.push(ev.event_id);
7075
7178
  }
@@ -7205,11 +7308,11 @@ __export(owner_brake_signed_exports, {
7205
7308
  verifyDelegation: () => verifyDelegation,
7206
7309
  verifyDirective: () => verifyDirective
7207
7310
  });
7208
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync13, existsSync as existsSync31, mkdirSync as mkdirSync15, renameSync as renameSync9 } from "node:fs";
7311
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync13, existsSync as existsSync32, mkdirSync as mkdirSync15, renameSync as renameSync9 } from "node:fs";
7209
7312
  import { join as join34, dirname as dirname14 } from "node:path";
7210
7313
  import { homedir as homedir28 } from "node:os";
7211
7314
  import { createHash as createHash9 } from "node:crypto";
7212
- import { PrivateKey as PrivateKey15, PublicKey as PublicKey4, Signature as Signature3, KeyDeriver } from "@bsv/sdk";
7315
+ import { PrivateKey as PrivateKey16, PublicKey as PublicKey5, Signature as Signature3, KeyDeriver } from "@bsv/sdk";
7213
7316
  function makeDelegation(rootPriv, delegatePubHex) {
7214
7317
  const body = { scope: BRAKE_DELEGATION_SCOPE, delegate_pubkey: delegatePubHex };
7215
7318
  return { ...body, signature: sign(rootPriv, canonicalize2(body)) };
@@ -7256,9 +7359,9 @@ function makeSignedGate({ rootPubHex, dir = join34(homedir28(), ".indelible", "d
7256
7359
  const directivePath = join34(dir, "signed-directive.json");
7257
7360
  const witnessPath = join34(dir, `brake-witness-${seat}.json`);
7258
7361
  const readWitness = () => {
7259
- if (!existsSync31(witnessPath)) return null;
7362
+ if (!existsSync32(witnessPath)) return null;
7260
7363
  try {
7261
- const w = JSON.parse(readFileSync25(witnessPath, "utf8"));
7364
+ const w = JSON.parse(readFileSync26(witnessPath, "utf8"));
7262
7365
  if (STATES.includes(w.state) && Number.isInteger(w.epoch)) return w;
7263
7366
  } catch {
7264
7367
  }
@@ -7273,10 +7376,10 @@ function makeSignedGate({ rootPubHex, dir = join34(homedir28(), ".indelible", "d
7273
7376
  return function gate() {
7274
7377
  const witness = readWitness();
7275
7378
  let effective = witness ? { state: witness.state, epoch: witness.epoch, message: witness.message ?? null, source: "witness" } : null;
7276
- if (existsSync31(directivePath)) {
7379
+ if (existsSync32(directivePath)) {
7277
7380
  let envelope = null;
7278
7381
  try {
7279
- envelope = JSON.parse(readFileSync25(directivePath, "utf8"));
7382
+ envelope = JSON.parse(readFileSync26(directivePath, "utf8"));
7280
7383
  } catch {
7281
7384
  }
7282
7385
  if (!envelope) {
@@ -7301,7 +7404,7 @@ function makeSignedGate({ rootPubHex, dir = join34(homedir28(), ".indelible", "d
7301
7404
  };
7302
7405
  }
7303
7406
  function makeBrakeKey() {
7304
- const priv = PrivateKey15.fromRandom();
7407
+ const priv = PrivateKey16.fromRandom();
7305
7408
  return { priv, pubHex: priv.toPublicKey().toString() };
7306
7409
  }
7307
7410
  var BRAKE_DELEGATION_SCOPE, BRAKE_METANET_PROTOCOL, BRAKE_METANET_KEY_ID, STATES, sha256Bytes, sign, verify;
@@ -7317,7 +7420,7 @@ var init_owner_brake_signed = __esm({
7317
7420
  sign = (priv, str) => priv.sign(sha256Bytes(str)).toDER("hex");
7318
7421
  verify = (pubHex, str, sigHex) => {
7319
7422
  try {
7320
- return PublicKey4.fromString(pubHex).verify(sha256Bytes(str), Signature3.fromDER(sigHex, "hex"));
7423
+ return PublicKey5.fromString(pubHex).verify(sha256Bytes(str), Signature3.fromDER(sigHex, "hex"));
7321
7424
  } catch {
7322
7425
  return false;
7323
7426
  }
@@ -7396,7 +7499,7 @@ var init_hint_pipe = __esm({
7396
7499
  });
7397
7500
 
7398
7501
  // mcp-server/lib/drift/summon-ledger-lock.js
7399
- import { openSync as openSync3, writeFileSync as writeFileSync14, closeSync as closeSync3, readFileSync as readFileSync26, unlinkSync as unlinkSync3, mkdirSync as mkdirSync16 } from "node:fs";
7502
+ import { openSync as openSync3, writeFileSync as writeFileSync14, closeSync as closeSync3, readFileSync as readFileSync27, unlinkSync as unlinkSync3, mkdirSync as mkdirSync16 } from "node:fs";
7400
7503
  import { dirname as dirname15 } from "node:path";
7401
7504
  import { randomBytes as randomBytes5 } from "node:crypto";
7402
7505
  function pidAlive4(pid) {
@@ -7421,7 +7524,7 @@ async function acquireLedgerLock(lockPath2, { pollMs = 50, maxTries = 40 } = {})
7421
7524
  if (e.code !== "EEXIST") throw e;
7422
7525
  let info = null;
7423
7526
  try {
7424
- info = JSON.parse(readFileSync26(lockPath2, "utf8"));
7527
+ info = JSON.parse(readFileSync27(lockPath2, "utf8"));
7425
7528
  } catch {
7426
7529
  }
7427
7530
  if (info && !pidAlive4(info.pid)) {
@@ -7447,7 +7550,7 @@ function makeHandle(lockPath2, token) {
7447
7550
  if (released) return { released: false, reason: "already-released" };
7448
7551
  let info = null;
7449
7552
  try {
7450
- info = JSON.parse(readFileSync26(lockPath2, "utf8"));
7553
+ info = JSON.parse(readFileSync27(lockPath2, "utf8"));
7451
7554
  } catch {
7452
7555
  released = true;
7453
7556
  return { released: false, reason: "gone-or-unreadable" };
@@ -7482,7 +7585,7 @@ var init_summon_ledger_lock = __esm({
7482
7585
  });
7483
7586
 
7484
7587
  // mcp-server/lib/drift/summon-ledger.js
7485
- import { readFileSync as readFileSync27, existsSync as existsSync32, appendFileSync as appendFileSync5, writeFileSync as writeFileSync15, renameSync as renameSync10, mkdirSync as mkdirSync17 } from "node:fs";
7588
+ import { readFileSync as readFileSync28, existsSync as existsSync33, appendFileSync as appendFileSync5, writeFileSync as writeFileSync15, renameSync as renameSync10, mkdirSync as mkdirSync17 } from "node:fs";
7486
7589
  import { join as join35, dirname as dirname16 } from "node:path";
7487
7590
  import { homedir as homedir29, hostname as hostname3 } from "node:os";
7488
7591
  import { randomBytes as randomBytes6 } from "node:crypto";
@@ -7504,17 +7607,17 @@ function appendRec(rec) {
7504
7607
  appendFileSync5(ledgerPath(), JSON.stringify(rec) + "\n");
7505
7608
  }
7506
7609
  function readHead() {
7507
- if (!existsSync32(headPath())) return { folded_started: 0, folded_through_at: 0 };
7610
+ if (!existsSync33(headPath())) return { folded_started: 0, folded_through_at: 0 };
7508
7611
  try {
7509
- return JSON.parse(readFileSync27(headPath(), "utf8"));
7612
+ return JSON.parse(readFileSync28(headPath(), "utf8"));
7510
7613
  } catch {
7511
7614
  return { folded_started: 0, folded_through_at: 0 };
7512
7615
  }
7513
7616
  }
7514
7617
  function readTail() {
7515
- if (!existsSync32(ledgerPath())) return [];
7618
+ if (!existsSync33(ledgerPath())) return [];
7516
7619
  const out = [];
7517
- for (const l of readFileSync27(ledgerPath(), "utf8").split("\n")) {
7620
+ for (const l of readFileSync28(ledgerPath(), "utf8").split("\n")) {
7518
7621
  if (l.trim()) {
7519
7622
  try {
7520
7623
  out.push(JSON.parse(l));
@@ -7661,12 +7764,12 @@ __export(summon_watcher_exports, {
7661
7764
  function verifyReplyLanded(seat, target, events) {
7662
7765
  return events.find((e) => e.actor?.host === seat && (e.causality?.parents || []).includes(target)) || null;
7663
7766
  }
7664
- function computeGrandfathered({ events, seat, peers = null, backlog = 1 }) {
7767
+ function computeGrandfathered({ events, seat, peers = null, backlog = 1, addressedOnly = false }) {
7665
7768
  const excluded = /* @__PURE__ */ new Set();
7666
7769
  const g = /* @__PURE__ */ new Set();
7667
7770
  let kept = 0;
7668
7771
  for (; ; ) {
7669
- const id = pickUnansweredTarget({ events, seat, peers, exclude: /* @__PURE__ */ new Set([...excluded, ...g]) });
7772
+ const id = pickUnansweredTarget({ events, seat, peers, addressedOnly, exclude: /* @__PURE__ */ new Set([...excluded, ...g]) });
7670
7773
  if (!id) break;
7671
7774
  if (kept < backlog) {
7672
7775
  kept++;
@@ -7677,7 +7780,7 @@ function computeGrandfathered({ events, seat, peers = null, backlog = 1 }) {
7677
7780
  }
7678
7781
  return g;
7679
7782
  }
7680
- function pickUnansweredTarget({ events, seat, peers = null, exclude = null }) {
7783
+ function pickUnansweredTarget({ events, seat, peers = null, exclude = null, addressedOnly = false }) {
7681
7784
  const all = [...events].sort((a, b) => a.created_at < b.created_at ? -1 : 1);
7682
7785
  const candidates = all.filter((e) => {
7683
7786
  const h = e.actor?.host;
@@ -7685,6 +7788,7 @@ function pickUnansweredTarget({ events, seat, peers = null, exclude = null }) {
7685
7788
  if (peers && !peers.includes(h)) return false;
7686
7789
  const to = e.payload?.["drift.to_seat"];
7687
7790
  if (to && to !== seat) return false;
7791
+ if (addressedOnly && !to) return false;
7688
7792
  return true;
7689
7793
  });
7690
7794
  for (let i = candidates.length - 1; i >= 0; i--) {
@@ -7935,8 +8039,8 @@ async function mintSession(apiUrl, address) {
7935
8039
  const wif = await getWif();
7936
8040
  if (!wif) return { ok: false, reason: "wallet_locked", detail: "no key available to prove ownership" };
7937
8041
  try {
7938
- const { PrivateKey: PrivateKey16 } = await import("@bsv/sdk");
7939
- const priv = PrivateKey16.fromWif(wif);
8042
+ const { PrivateKey: PrivateKey17 } = await import("@bsv/sdk");
8043
+ const priv = PrivateKey17.fromWif(wif);
7940
8044
  const own = priv.toPublicKey().toAddress();
7941
8045
  if (address && address !== own) {
7942
8046
  return { ok: false, reason: "address_key_mismatch", detail: `this box's key proves ${own}, not ${address}` };
@@ -8023,7 +8127,7 @@ import { execSync as execSync2 } from "node:child_process";
8023
8127
  import { homedir as homedir30 } from "node:os";
8024
8128
  import { join as join36, dirname as dirname17, resolve as resolve3 } from "node:path";
8025
8129
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8026
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync16, existsSync as existsSync33, mkdirSync as mkdirSync18, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
8130
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync16, existsSync as existsSync34, mkdirSync as mkdirSync18, readdirSync as readdirSync6, statSync as statSync7 } from "node:fs";
8027
8131
 
8028
8132
  // mcp-server/lib/guardrails-run.js
8029
8133
  function contextFromInput(input) {
@@ -12875,11 +12979,11 @@ function installHooks() {
12875
12979
  const claudeDir = join36(homedir30(), ".claude");
12876
12980
  const settingsPath = join36(claudeDir, "settings.json");
12877
12981
  const legacyPath = join36(claudeDir, "settings.local.json");
12878
- if (!existsSync33(claudeDir)) mkdirSync18(claudeDir, { recursive: true });
12982
+ if (!existsSync34(claudeDir)) mkdirSync18(claudeDir, { recursive: true });
12879
12983
  let settings = {};
12880
- if (existsSync33(settingsPath)) {
12984
+ if (existsSync34(settingsPath)) {
12881
12985
  try {
12882
- settings = JSON.parse(readFileSync28(settingsPath, "utf8"));
12986
+ settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
12883
12987
  } catch {
12884
12988
  settings = {};
12885
12989
  }
@@ -12888,9 +12992,9 @@ function installHooks() {
12888
12992
  let migratedFromLegacy = false;
12889
12993
  let normalizedMatcher = false;
12890
12994
  const GUARD_MATCHER = "Bash|PowerShell";
12891
- if (existsSync33(legacyPath)) {
12995
+ if (existsSync34(legacyPath)) {
12892
12996
  try {
12893
- const legacy = JSON.parse(readFileSync28(legacyPath, "utf8"));
12997
+ const legacy = JSON.parse(readFileSync29(legacyPath, "utf8"));
12894
12998
  if (legacy?.hooks && typeof legacy.hooks === "object") {
12895
12999
  const isIndelible = (entry) => entry?.hooks?.some((hh) => hh.command?.includes("indelible-mcp hook")) || entry?.command?.includes("indelible-mcp hook");
12896
13000
  const entryKey = (entry) => JSON.stringify([
@@ -12989,7 +13093,7 @@ function installHooks() {
12989
13093
  function runPreToolUseGuard() {
12990
13094
  let code = 0;
12991
13095
  try {
12992
- const raw = readFileSync28(0, "utf8").trim();
13096
+ const raw = readFileSync29(0, "utf8").trim();
12993
13097
  if (!raw) process.exit(0);
12994
13098
  const hit = evaluate(JSON.parse(raw));
12995
13099
  if (hit) {
@@ -13262,6 +13366,153 @@ Commands:
13262
13366
  }
13263
13367
  break;
13264
13368
  }
13369
+ case "agents": {
13370
+ const A_KNOWN = ["name", "all", "list", "retire", "api-url"];
13371
+ const aUnknown = process.argv.slice(3).filter((a) => a.startsWith("--")).map((a) => a.slice(2).split("=")[0]).filter((f) => !A_KNOWN.includes(f));
13372
+ if (aUnknown.length) {
13373
+ console.error(`\u2717 unknown flag${aUnknown.length > 1 ? "s" : ""} --${aUnknown.join(", --")} \u2014 this command proves identity, so it refuses what it doesn't understand.
13374
+ Known: ${A_KNOWN.map((f) => "--" + f).join(" ")}`);
13375
+ process.exitCode = 1;
13376
+ break;
13377
+ }
13378
+ const aflag = (n, d) => {
13379
+ const a = process.argv.find((x) => x.startsWith(`--${n}=`));
13380
+ return a ? a.split("=")[1] : d;
13381
+ };
13382
+ const { listLocalAgents: listLocalAgents2, buildRegisterBody: buildRegisterBody2, buildRetireBody: buildRetireBody2 } = await Promise.resolve().then(() => (init_agent_possession(), agent_possession_exports));
13383
+ const scope = await resolveTenantContext();
13384
+ const local = listLocalAgents2(scope);
13385
+ if (process.argv.includes("--list") || process.argv.length === 3) {
13386
+ if (!local.length) {
13387
+ console.log("No agents on this box yet. Birth one with `birth_custom_agent`, or run setup if this is a fresh install.");
13388
+ break;
13389
+ }
13390
+ console.log(`${local.length} agent${local.length === 1 ? "" : "s"} on this box:
13391
+ `);
13392
+ for (const a of local) {
13393
+ console.log(` ${a.name.padEnd(16)} ${String(a.domain || "\u2014").padEnd(14)} ${a.address || "(unreadable key)"}${a.self_consistent ? "" : " \u26A0 key/address mismatch"}`);
13394
+ }
13395
+ console.log("\nRegister them so they can stand at the Counter: indelible-mcp agents --all");
13396
+ break;
13397
+ }
13398
+ const cfgAll = await loadConfig();
13399
+ const apiUrl = (aflag("api-url", null) || process.env.INDELIBLE_API_URL || cfgAll.api_url || "https://indelible.one").replace(/\/$/, "");
13400
+ const apiKey = cfgAll.api_key;
13401
+ const ownerWif = cfgAll.wif || cfgAll.private_key;
13402
+ if (!apiKey) {
13403
+ console.error("\u2717 no api_key in your config \u2014 run `indelible-mcp setup` first.");
13404
+ process.exitCode = 1;
13405
+ break;
13406
+ }
13407
+ if (!ownerWif) {
13408
+ console.error("\u2717 no wallet key in your config \u2014 this command needs your wallet to co-sign.");
13409
+ process.exitCode = 1;
13410
+ break;
13411
+ }
13412
+ const { PrivateKey: PrivateKey17 } = await import("@bsv/sdk");
13413
+ const owner = PrivateKey17.fromWif(ownerWif).toPublicKey().toAddress();
13414
+ const mint = async (name) => {
13415
+ const r = await fetch(`${apiUrl}/api/sanctuary/agent/challenge`, {
13416
+ method: "POST",
13417
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
13418
+ body: JSON.stringify({ name }),
13419
+ signal: AbortSignal.timeout(15e3)
13420
+ });
13421
+ if (!r.ok) return { ok: false, status: r.status, body: await r.text() };
13422
+ return { ok: true, ...await r.json() };
13423
+ };
13424
+ const retireName = aflag("retire", null);
13425
+ if (retireName) {
13426
+ const ch = await mint(retireName);
13427
+ if (!ch.ok) {
13428
+ console.error(`\u2717 challenge refused (${ch.status}): ${String(ch.body).slice(0, 160)}`);
13429
+ process.exitCode = 1;
13430
+ break;
13431
+ }
13432
+ const built = buildRetireBody2({ name: retireName, nonce: ch.nonce, owner, ownerWif });
13433
+ if (!built.ok) {
13434
+ console.error(`\u2717 ${built.reason}: ${built.detail}`);
13435
+ process.exitCode = 1;
13436
+ break;
13437
+ }
13438
+ const r = await fetch(`${apiUrl}/api/sanctuary/agent/retire`, {
13439
+ method: "POST",
13440
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
13441
+ body: JSON.stringify(built.body),
13442
+ signal: AbortSignal.timeout(15e3)
13443
+ });
13444
+ const b = await r.json().catch(() => ({}));
13445
+ if (r.ok) console.log(`\u2713 retired "${b.name}" \u2014 any open listing is closed. Re-register any time; the record keeps its history.`);
13446
+ else {
13447
+ console.error(`\u2717 retire refused (${r.status}): ${b.error || ""} ${b.message || ""}`.trim());
13448
+ process.exitCode = 1;
13449
+ }
13450
+ break;
13451
+ }
13452
+ const only = aflag("name", null);
13453
+ const targets = only ? local.filter((a) => a.name === String(only).toLowerCase()) : local;
13454
+ if (!targets.length) {
13455
+ console.error(only ? `\u2717 no agent named "${only}" on this box. Run \`indelible-mcp agents --list\`.` : "\u2717 no agents on this box.");
13456
+ process.exitCode = 1;
13457
+ break;
13458
+ }
13459
+ if (!only && !process.argv.includes("--all")) {
13460
+ console.log(`This will prove ${targets.length} agents to ${apiUrl} as ${owner}.
13461
+ Run with --all to go ahead, or --name=<agent> for one.`);
13462
+ break;
13463
+ }
13464
+ let ok = 0, refused = 0;
13465
+ for (const a of targets) {
13466
+ if (!a.self_consistent) {
13467
+ console.log(` \u26A0 ${a.name.padEnd(16)} skipped \u2014 its key does not match its own address`);
13468
+ refused++;
13469
+ continue;
13470
+ }
13471
+ const ch = await mint(a.name);
13472
+ if (!ch.ok) {
13473
+ console.log(` \u2717 ${a.name.padEnd(16)} challenge refused (${ch.status})`);
13474
+ refused++;
13475
+ continue;
13476
+ }
13477
+ const built = buildRegisterBody2({ agent: a, nonce: ch.nonce, owner, ownerWif, scope });
13478
+ if (!built.ok) {
13479
+ console.log(` \u2717 ${a.name.padEnd(16)} ${built.reason} \u2014 ${built.detail}`);
13480
+ refused++;
13481
+ continue;
13482
+ }
13483
+ const send = async (body) => fetch(`${apiUrl}/api/sanctuary/agent/register`, {
13484
+ method: "POST",
13485
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
13486
+ // 45s: registering checks the key against the on-chain registry across the bridge
13487
+ // fleet, so this is slower than a normal call. 20s aborted a real run mid-way.
13488
+ body: JSON.stringify(body),
13489
+ signal: AbortSignal.timeout(45e3)
13490
+ }).catch((e) => ({ ok: false, status: 0, json: async () => ({ error: e.name === "TimeoutError" ? "timed out \u2014 the registry check was slow; run it again" : e.message }) }));
13491
+ let r = await send(built.body);
13492
+ let b = await r.json().catch(() => ({}));
13493
+ if (r.status === 503 && b.error === "REGISTRY_UNAVAILABLE") {
13494
+ const ch2 = await mint(a.name);
13495
+ if (ch2.ok) {
13496
+ const retry = buildRegisterBody2({ agent: a, nonce: ch2.nonce, owner, ownerWif, scope });
13497
+ if (retry.ok) {
13498
+ r = await send(retry.body);
13499
+ b = await r.json().catch(() => ({}));
13500
+ }
13501
+ }
13502
+ }
13503
+ if (r.ok) {
13504
+ ok++;
13505
+ console.log(` \u2713 ${a.name.padEnd(16)} ${b.status === "unchanged" ? "already proven" : "proven"} \xB7 paid at ${b.agent?.address || "\u2014"}`);
13506
+ } else {
13507
+ refused++;
13508
+ console.log(` \u2717 ${a.name.padEnd(16)} ${r.status || "\u2014"} ${b.error || ""}${b.message ? " \u2014 " + b.message : ""}`);
13509
+ }
13510
+ }
13511
+ console.log(`
13512
+ ${ok} proven, ${refused} refused.`);
13513
+ if (ok) console.log("Commerce-domain agents can now be opened at your Counter (they still need your box online to deliver).");
13514
+ break;
13515
+ }
13265
13516
  case "workshop": {
13266
13517
  const wflag = (n, d) => {
13267
13518
  const a = process.argv.find((x) => x.startsWith(`--${n}=`));
@@ -13329,7 +13580,7 @@ Commands:
13329
13580
  }
13330
13581
  const { DriftStore: DriftStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
13331
13582
  const { Poller: Poller2, publishEvent: publishEvent2 } = await Promise.resolve().then(() => (init_discovery(), discovery_exports));
13332
- const { makeEvent: makeEvent2, HOST_PATTERN: HOST_PATTERN2 } = await Promise.resolve().then(() => (init_events(), events_exports));
13583
+ const { makeEvent: makeEvent2, HOST_PATTERN: HOST_PATTERN2, hostVendor: hostVendor2 } = await Promise.resolve().then(() => (init_events(), events_exports));
13333
13584
  const { encryptRef: encryptRef2 } = await Promise.resolve().then(() => (init_refcrypt(), refcrypt_exports));
13334
13585
  const flag = (n, d) => {
13335
13586
  const a = process.argv.find((x) => x.startsWith(`--${n}=`));
@@ -13546,12 +13797,13 @@ Commands:
13546
13797
  const SAVE = !process.argv.includes("--no-save");
13547
13798
  const streamName = flag("stream", "live");
13548
13799
  const FOR = flag("for", "both");
13549
- if (!["both", "claude", "codex"].includes(FOR)) {
13550
- console.error("\u2717 --for must be both, claude, or codex");
13800
+ if (FOR !== "both" && !HOST_PATTERN2.test(FOR)) {
13801
+ console.error(`\u2717 --for must be both, or a seat name like codex / codex-parity (got "${FOR}")`);
13551
13802
  process.exitCode = 1;
13552
13803
  break;
13553
13804
  }
13554
13805
  const SEATS = FOR === "both" ? ["claude", "codex"] : [FOR];
13806
+ const LANE = flag("lane", null) === "off" ? false : flag("lane", null) !== null || SEATS.some((s2) => s2.includes("-"));
13555
13807
  const BACKLOG = flag("backlog", "1") === "all" ? Infinity : Math.max(0, parseInt(flag("backlog", "1"), 10) || 0);
13556
13808
  const view = new DriftStore2({ path: pj(DRIFT_DIR, "summoner-view.jsonl") });
13557
13809
  const vpoller = new Poller2(MAILBOX, view, {});
@@ -13572,11 +13824,11 @@ Commands:
13572
13824
  let gfDone = false;
13573
13825
  const target = (seat2) => {
13574
13826
  const events = view.all().filter(inStream);
13575
- const id = pickUnansweredTarget2({ events, seat: seat2, exclude: gf[seat2] });
13827
+ const id = pickUnansweredTarget2({ events, seat: seat2, exclude: gf[seat2], addressedOnly: LANE });
13576
13828
  return id ? events.find((e) => e.event_id === id) : null;
13577
13829
  };
13578
13830
  const doSummon = (seat2, t) => {
13579
- const bin = seat2 === "claude" ? claudeBin : codexBin;
13831
+ const bin = hostVendor2(seat2) === "claude" ? claudeBin : codexBin;
13580
13832
  if (!bin) return;
13581
13833
  const g = oc.beatGate();
13582
13834
  if (!g.proceed) {
@@ -13591,9 +13843,16 @@ Commands:
13591
13843
  inflight[seat2] = true;
13592
13844
  last[seat2] = Date.now();
13593
13845
  total++;
13594
- const peer = seat2 === "claude" ? "codex" : "claude";
13846
+ const peer = t.actor?.host || (hostVendor2(seat2) === "claude" ? "codex" : "claude");
13595
13847
  const saveLine = SAVE ? ` Finally, commit THIS session to the permanent memory: call the indelible save_session tool WITH the argument summoned_by="${peer}" \u2014 that marks yours as a Summoned Session in the record (the env marker does not survive on every vendor); your session becomes part of the shared on-chain memory \u2014 that is the point.` : "";
13596
- const inst = `You are the summoned ${seat2} pilot on the Indelible drift wire. Your partner ${peer} posted event ${t.event_id} and it is unanswered. Read context: indelible-mcp drift read --n=8 \u2014 then post ONE substantive reply to that exact event in your own words: indelible-mcp drift post --as=${seat2} --reply-to=${t.event_id} "your reply". Close the loop, keep it brief.${saveLine} Then exit.`;
13848
+ const letter = String(t.payload?.message || "").slice(0, 6e3);
13849
+ const inst = `You are the summoned ${seat2} pilot on the Indelible drift wire. ${peer} posted event ${t.event_id} and it is unanswered. THIS IS THE MESSAGE YOU MUST ANSWER, in full:
13850
+
13851
+ """
13852
+ ${letter}
13853
+ """
13854
+
13855
+ Answer THAT message and nothing else \u2014 the wire also carries unrelated conversations, so ignore any traffic that is not about this topic (indelible-mcp drift read --n=8 is available for background, but the quoted message above is your assignment). Then post ONE substantive reply to that exact event in your own words: indelible-mcp drift post --as=${seat2} --reply-to=${t.event_id} "your reply". Close the loop, keep it brief.${saveLine} Then exit.`;
13597
13856
  const args3 = seat2 === "claude" ? ["-p", inst, "--allowedTools", "Bash,mcp__indelible__save_session"] : ["exec", "--sandbox", "danger-full-access", "--skip-git-repo-check", inst];
13598
13857
  console.log(`\u26A1 summoning a fresh ${seat2} (answering ${t.event_id.slice(0, 12)}\u2026) \u2014 real usage on your ${seat2} account${SAVE ? " \xB7 will self-save to chain" : ""}`);
13599
13858
  try {
@@ -13618,7 +13877,7 @@ Commands:
13618
13877
  await vpoller.poll();
13619
13878
  if (!gfDone) {
13620
13879
  const events = view.all().filter(inStream);
13621
- for (const s2 of SEATS) gf[s2] = computeGrandfathered2({ events, seat: s2, backlog: BACKLOG });
13880
+ for (const s2 of SEATS) gf[s2] = computeGrandfathered2({ events, seat: s2, backlog: BACKLOG, addressedOnly: LANE });
13622
13881
  gfDone = true;
13623
13882
  const n = SEATS.reduce((a, s2) => a + gf[s2].size, 0);
13624
13883
  if (n) console.log(`${n} pre-shift letter(s) grandfathered silent \u2014 answering the head + everything new (--backlog=all to drain history)`);
@@ -13670,7 +13929,7 @@ Commands:
13670
13929
  }
13671
13930
  function printHelp() {
13672
13931
  console.log(`
13673
- Indelible MCP \u2014 Blockchain memory for Claude Code (v5.5.1)
13932
+ Indelible MCP \u2014 Blockchain memory for Claude Code (v5.6.0)
13674
13933
 
13675
13934
  Setup:
13676
13935
  indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
@@ -13687,6 +13946,9 @@ The Drift Wire (your two AIs, talking directly \u2014 you hold the brake):
13687
13946
  indelible-mcp drift listen "stop, listen" Freeze both AND hand them your message
13688
13947
 
13689
13948
  The Workshop (your agents earning at the Counter \u2014 paid to THEIR addresses, never ours):
13949
+ indelible-mcp agents --list Your agents on this box (name, domain, the address each is paid at)
13950
+ indelible-mcp agents --all Prove you hold their keys so they can stand at your Counter
13951
+ indelible-mcp agents --retire=<name> Take one off the counter (your wallet co-signs; the record keeps its history)
13690
13952
  indelible-mcp workshop One serving pass: pick up paid orders, run your agent locally, deliver signed work
13691
13953
  indelible-mcp workshop --loop=300 Keep serving (checks in as deliverable so the Counter opens your agents)
13692
13954
  indelible-mcp workshop --status Which of your agents this box can serve right now
@@ -13733,15 +13995,15 @@ Learn more: https://indelible.one
13733
13995
  }
13734
13996
  function findNewestTranscript() {
13735
13997
  const projectsDir = join36(homedir30(), ".claude", "projects");
13736
- if (!existsSync33(projectsDir)) return null;
13998
+ if (!existsSync34(projectsDir)) return null;
13737
13999
  let newestTime = 0;
13738
14000
  let newest = null;
13739
14001
  try {
13740
- for (const project of readdirSync5(projectsDir)) {
14002
+ for (const project of readdirSync6(projectsDir)) {
13741
14003
  const projectPath = join36(projectsDir, project);
13742
14004
  try {
13743
14005
  if (!statSync7(projectPath).isDirectory()) continue;
13744
- for (const file of readdirSync5(projectPath)) {
14006
+ for (const file of readdirSync6(projectPath)) {
13745
14007
  if (!file.endsWith(".jsonl")) continue;
13746
14008
  const p = join36(projectPath, file);
13747
14009
  const t = statSync7(p).mtimeMs;
@@ -13772,8 +14034,8 @@ async function printTimeCard() {
13772
14034
  }
13773
14035
  try {
13774
14036
  const gPath = join36(homedir30(), ".indelible", "goals.json");
13775
- if (existsSync33(gPath)) {
13776
- const g = JSON.parse(readFileSync28(gPath, "utf8"));
14037
+ if (existsSync34(gPath)) {
14038
+ const g = JSON.parse(readFileSync29(gPath, "utf8"));
13777
14039
  const active = (g.goals || []).filter((x) => x.status === "active");
13778
14040
  if (active.length) {
13779
14041
  const oldest = active.reduce((a, b) => new Date(a.created_at) < new Date(b.created_at) ? a : b);
@@ -13809,7 +14071,7 @@ async function runPreCompactSave() {
13809
14071
  process.stderr.write("Indelible: MCP disabled, skipping save\n");
13810
14072
  process.exit(0);
13811
14073
  }
13812
- const target = transcriptPath && existsSync33(transcriptPath) ? transcriptPath : findNewestTranscript() || CONTEXT_FILE2;
14074
+ const target = transcriptPath && existsSync34(transcriptPath) ? transcriptPath : findNewestTranscript() || CONTEXT_FILE2;
13813
14075
  const result = await saveSession(target, `Auto-save before ${trigger} compaction`);
13814
14076
  if (result.success) {
13815
14077
  process.stderr.write(`Indelible: Saved ${result.newMessages} messages (${result.saveType}) tx:${result.txId?.slice(0, 12)}...
@@ -13846,16 +14108,16 @@ async function runPreCompactSave() {
13846
14108
  }
13847
14109
  function findMemoryDir() {
13848
14110
  const projectsDir = join36(homedir30(), ".claude", "projects");
13849
- if (!existsSync33(projectsDir)) return null;
14111
+ if (!existsSync34(projectsDir)) return null;
13850
14112
  let newestTime = 0;
13851
14113
  let newestProject = null;
13852
- const projects = readdirSync5(projectsDir);
14114
+ const projects = readdirSync6(projectsDir);
13853
14115
  for (const project of projects) {
13854
14116
  const projectPath = join36(projectsDir, project);
13855
14117
  try {
13856
14118
  const pStat = statSync7(projectPath);
13857
14119
  if (!pStat.isDirectory()) continue;
13858
- const files = readdirSync5(projectPath);
14120
+ const files = readdirSync6(projectPath);
13859
14121
  for (const file of files) {
13860
14122
  if (!file.endsWith(".jsonl")) continue;
13861
14123
  const fStat = statSync7(join36(projectPath, file));
@@ -13917,10 +14179,10 @@ async function runPostCompactRestore() {
13917
14179
  try {
13918
14180
  const memoryDir = findMemoryDir();
13919
14181
  if (memoryDir && (config2.memory_file_txid || config2.session_history_txid)) {
13920
- if (!existsSync33(memoryDir)) mkdirSync18(memoryDir, { recursive: true });
14182
+ if (!existsSync34(memoryDir)) mkdirSync18(memoryDir, { recursive: true });
13921
14183
  if (config2.memory_file_txid) {
13922
14184
  const memPath = join36(memoryDir, "MEMORY.md");
13923
- if (!existsSync33(memPath)) {
14185
+ if (!existsSync34(memPath)) {
13924
14186
  const memResult = await loadFile(config2.memory_file_txid, { outputPath: memPath });
13925
14187
  if (memResult.success) {
13926
14188
  process.stderr.write(`Indelible: MEMORY.md restored from chain (file was missing)
@@ -13930,7 +14192,7 @@ async function runPostCompactRestore() {
13930
14192
  }
13931
14193
  if (config2.session_history_txid) {
13932
14194
  const histPath = join36(memoryDir, "session-history.md");
13933
- if (!existsSync33(histPath)) {
14195
+ if (!existsSync34(histPath)) {
13934
14196
  const histResult = await loadFile(config2.session_history_txid, { outputPath: histPath });
13935
14197
  if (histResult.success) {
13936
14198
  process.stderr.write(`Indelible: session-history.md restored from chain (file was missing)
@@ -13947,8 +14209,8 @@ async function runPostCompactRestore() {
13947
14209
  const histDir = findMemoryDir();
13948
14210
  if (histDir) {
13949
14211
  const histPath = join36(histDir, "session-history.md");
13950
- if (existsSync33(histPath)) {
13951
- const histContent = readFileSync28(histPath, "utf-8");
14212
+ if (existsSync34(histPath)) {
14213
+ const histContent = readFileSync29(histPath, "utf-8");
13952
14214
  const lines = histContent.split("\n");
13953
14215
  const entryStarts = [];
13954
14216
  for (let i = 0; i < lines.length; i++) {
@@ -13990,7 +14252,7 @@ function readStdin() {
13990
14252
  }
13991
14253
  var SERVER_INFO = {
13992
14254
  name: "indelible",
13993
- version: "5.5.1",
14255
+ version: "5.6.0",
13994
14256
  description: "Blockchain-backed memory and code storage for Claude Code"
13995
14257
  };
13996
14258
  var TOOLS = [
@@ -14738,7 +15000,7 @@ async function runWizard() {
14738
15000
  join36(homedir30(), ".claude", "settings.json")
14739
15001
  ]) {
14740
15002
  try {
14741
- const s = JSON.parse(readFileSync28(cfgPath, "utf8"));
15003
+ const s = JSON.parse(readFileSync29(cfgPath, "utf8"));
14742
15004
  if (s?.mcpServers?.indelible) {
14743
15005
  mcpOk = true;
14744
15006
  break;
@@ -14751,7 +15013,7 @@ async function runWizard() {
14751
15013
  const hooksPath = join36(homedir30(), ".claude", "settings.json");
14752
15014
  let hooksOk = false;
14753
15015
  try {
14754
- const s = JSON.parse(readFileSync28(hooksPath, "utf8"));
15016
+ const s = JSON.parse(readFileSync29(hooksPath, "utf8"));
14755
15017
  hooksOk = s?.hooks?.PreCompact?.some(
14756
15018
  (h) => h.hooks?.some((hh) => hh.command?.includes("indelible-mcp")) || h.command?.includes("indelible-mcp")
14757
15019
  ) && s?.hooks?.SessionStart?.some(