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 +17 -1
- package/CUSTOMER_AGENT_HANDBOOK.md +2 -0
- package/package.json +1 -1
- package/src/index.js +344 -82
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
|
|
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
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:
|
|
4283
|
+
const { readdirSync: readdirSync7, statSync: statSync8 } = await import("fs");
|
|
4284
4284
|
let newestProject = null;
|
|
4285
4285
|
let newestTime = 0;
|
|
4286
|
-
for (const project of
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 (!
|
|
6778
|
-
for (const line of
|
|
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
|
|
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 &&
|
|
6923
|
-
for (const f of
|
|
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 (!
|
|
7035
|
+
if (!existsSync30(p)) return;
|
|
6933
7036
|
let ev;
|
|
6934
7037
|
try {
|
|
6935
|
-
ev = JSON.parse(
|
|
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 =
|
|
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
|
|
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 (!
|
|
7032
|
-
const ev = JSON.parse(
|
|
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
|
|
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
|
|
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 (
|
|
7071
|
-
if (
|
|
7072
|
-
if (
|
|
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
|
|
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
|
|
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 (!
|
|
7362
|
+
if (!existsSync32(witnessPath)) return null;
|
|
7260
7363
|
try {
|
|
7261
|
-
const w = JSON.parse(
|
|
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 (
|
|
7379
|
+
if (existsSync32(directivePath)) {
|
|
7277
7380
|
let envelope = null;
|
|
7278
7381
|
try {
|
|
7279
|
-
envelope = JSON.parse(
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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 (!
|
|
7610
|
+
if (!existsSync33(headPath())) return { folded_started: 0, folded_through_at: 0 };
|
|
7508
7611
|
try {
|
|
7509
|
-
return JSON.parse(
|
|
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 (!
|
|
7618
|
+
if (!existsSync33(ledgerPath())) return [];
|
|
7516
7619
|
const out = [];
|
|
7517
|
-
for (const l of
|
|
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:
|
|
7939
|
-
const priv =
|
|
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
|
|
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 (!
|
|
12982
|
+
if (!existsSync34(claudeDir)) mkdirSync18(claudeDir, { recursive: true });
|
|
12879
12983
|
let settings = {};
|
|
12880
|
-
if (
|
|
12984
|
+
if (existsSync34(settingsPath)) {
|
|
12881
12985
|
try {
|
|
12882
|
-
settings = JSON.parse(
|
|
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 (
|
|
12995
|
+
if (existsSync34(legacyPath)) {
|
|
12892
12996
|
try {
|
|
12893
|
-
const legacy = JSON.parse(
|
|
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 =
|
|
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 (
|
|
13550
|
-
console.error(
|
|
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
|
|
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.
|
|
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 (!
|
|
13998
|
+
if (!existsSync34(projectsDir)) return null;
|
|
13737
13999
|
let newestTime = 0;
|
|
13738
14000
|
let newest = null;
|
|
13739
14001
|
try {
|
|
13740
|
-
for (const project of
|
|
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
|
|
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 (
|
|
13776
|
-
const g = JSON.parse(
|
|
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 &&
|
|
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 (!
|
|
14111
|
+
if (!existsSync34(projectsDir)) return null;
|
|
13850
14112
|
let newestTime = 0;
|
|
13851
14113
|
let newestProject = null;
|
|
13852
|
-
const projects =
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
13951
|
-
const histContent =
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|