@heyanon-arp/cli 0.0.7 → 0.0.9

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/dist/cli.js CHANGED
@@ -289,8 +289,7 @@ var init_api = __esm({
289
289
  }
290
290
  /**
291
291
  * Public `GET /v1/agents` — discovery / marketplace catalog.
292
- * Unauthenticated. Server pins `publicationStatus = active` so
293
- * drafts / paused agents never appear here.
292
+ * Unauthenticated. Returns registered agents.
294
293
  *
295
294
  * Filters AND-compose: pass multiple tags to require all of them,
296
295
  * combine with `q` for free-text. `after` is the `id` of the
@@ -321,18 +320,6 @@ var init_api = __esm({
321
320
  async updateAgent(did, body, signer) {
322
321
  return this.signedRequest("PATCH", `/v1/agents/${encodeURIComponent(did)}`, body, signer);
323
322
  }
324
- async publishAgent(did, signer) {
325
- return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/publish`, {}, signer);
326
- }
327
- async pauseAgent(did, signer) {
328
- return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/pause`, {}, signer);
329
- }
330
- async unpauseAgent(did, signer) {
331
- return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/unpause`, {}, signer);
332
- }
333
- async rotateIdentityKey(did, body, signer) {
334
- return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/rotate-identity-key`, body, signer);
335
- }
336
323
  /**
337
324
  * Ingest a signed envelope. Endpoint is public (no
338
325
  * `X-ARP-Signer-DID` headers) — authentication is the envelope's
@@ -933,18 +920,6 @@ var init_state = __esm({
933
920
 
934
921
  // src/commands/lifecycle.ts
935
922
  function registerLifecycleCommands(root) {
936
- root.command("publish").description("Publish a draft agent (draft \u2192 active), making it discoverable in the catalog.").argument("<did>", "Agent DID (did:arp:...) \u2014 must have local state").option("--server <url>", "Override ARP server base URL").action(async (did, opts) => {
937
- const agent = await actSigned(did, opts.server, (api, signer) => api.publishAgent(did, signer));
938
- printAgent("Published", agent);
939
- });
940
- root.command("pause").description("Pause an active agent (active \u2192 paused).").argument("<did>").option("--server <url>", "Override ARP server base URL").action(async (did, opts) => {
941
- const agent = await actSigned(did, opts.server, (api, signer) => api.pauseAgent(did, signer));
942
- printAgent("Paused", agent);
943
- });
944
- root.command("unpause").description("Unpause a paused agent (paused \u2192 active).").argument("<did>").option("--server <url>", "Override ARP server base URL").action(async (did, opts) => {
945
- const agent = await actSigned(did, opts.server, (api, signer) => api.unpauseAgent(did, signer));
946
- printAgent("Unpaused", agent);
947
- });
948
923
  root.command("update").description("Update an agent profile (name / description / tags). At least one flag is required.").argument("<did>").option("--server <url>", "Override ARP server base URL").option("--name <s>", "New display name").option("--description <s>", "New description").option("--tag <s>", "Capability tag \u2014 REPLACES the existing list. Repeatable: --tag translation --tag fr", accumulate2, []).option("--clear-tags", "Drop all tags (cannot be combined with --tag)", false).action(
949
924
  async (did, opts) => {
950
925
  const body = buildUpdateBody(opts);
@@ -996,7 +971,6 @@ function printAgent(verb, agent) {
996
971
  console.log(import_chalk4.default.green(`
997
972
  ${verb}.`));
998
973
  console.log(`${import_chalk4.default.bold("DID")}: ${import_chalk4.default.cyan(agent.did)}`);
999
- console.log(`${import_chalk4.default.bold("Status")}: ${import_chalk4.default.cyan(agent.publicationStatus)}`);
1000
974
  console.log(import_chalk4.default.bold("\nAgent profile:"));
1001
975
  console.log(formatJson(agent));
1002
976
  }
@@ -1238,9 +1212,11 @@ function isPhaseTerminallyUnreachable(phase, s) {
1238
1212
  function untilPhaseMatched(phase, s) {
1239
1213
  switch (phase) {
1240
1214
  case "delegation.proposed":
1241
- return s.latestDelegation?.state === "proposed" || s.latestDelegation?.state === "offered" || s.latestDelegation?.state === "pending_lock_finalization";
1215
+ return s.latestDelegation?.state === "proposed" || s.latestDelegation?.state === "offered";
1242
1216
  case "delegation.accepted":
1243
1217
  return s.latestDelegation?.state === "accepted";
1218
+ case "delegation.locked":
1219
+ return s.latestDelegation?.state === "locked";
1244
1220
  case "delegation.canceled":
1245
1221
  return s.latestDelegation?.state === "canceled";
1246
1222
  case "delegation.declined":
@@ -1279,7 +1255,7 @@ function parseWaitInterval(raw) {
1279
1255
  return n;
1280
1256
  }
1281
1257
  function sleep(ms) {
1282
- return new Promise((resolve2) => setTimeout(resolve2, ms));
1258
+ return new Promise((resolve) => setTimeout(resolve, ms));
1283
1259
  }
1284
1260
  async function composeStatus(api, signerDid, relationshipId, signer) {
1285
1261
  const [relationshipsOrNull, delegationsOrError] = await Promise.all([
@@ -1315,7 +1291,7 @@ async function composeStatus(api, signerDid, relationshipId, signer) {
1315
1291
  const relationship = relationships.find((r) => r.relationshipId === relationshipId);
1316
1292
  const counterpartyDid = relationship ? relationship.pairDidA === signerDid ? relationship.pairDidB : relationship.pairDidA : inferCounterpartyFromEntities(signerDid, allDelegations);
1317
1293
  const delegations = allDelegations;
1318
- const latestDelegation = pickLatestLive(delegations, ["proposed", "offered", "pending_lock_finalization", "accepted", "awaiting_release_finalization"]);
1294
+ const latestDelegation = pickLatestLive(delegations, ["proposed", "offered", "accepted", "pending_lock_finalization", "locked", "awaiting_release_finalization"]);
1319
1295
  const [workLogs, receipts] = await Promise.all([
1320
1296
  latestDelegation ? fetchAllPages(
1321
1297
  (after) => api.listWorkLogs(relationshipId, signer, { limit: 100, delegationId: latestDelegation.delegationId, ...after ? { after } : {} })
@@ -1437,7 +1413,7 @@ function nextAction(input) {
1437
1413
  }
1438
1414
  if (!latestDelegation || latestDelegation.state === "declined" || latestDelegation.state === "canceled") {
1439
1415
  return {
1440
- hint: "No live delegation \u2014 buyer funds escrow `heyarp wallet create-lock \u2026` then offers `heyarp delegation offer <peer> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --pricing-model flat --settlement-model escrow --amount \u2026 --currency USDC:solana-devnet --deadline \u2026 --escrow-lock-from-file <path>`",
1416
+ hint: "No live delegation \u2014 buyer offers (terms only, no lock) `heyarp delegation offer <peer> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --pricing-model flat --amount \u2026 --currency USDC:solana-devnet --deadline \u2026`, then funds the escrow lock AFTER the worker accepts via `heyarp delegation fund <del-id> --escrow-lock-from-file <path>`",
1441
1417
  owner: "either",
1442
1418
  complete: false
1443
1419
  };
@@ -1451,7 +1427,7 @@ function nextAction(input) {
1451
1427
  complete: false
1452
1428
  };
1453
1429
  }
1454
- if (latestDelegation.state === "proposed" || latestDelegation.state === "offered" || latestDelegation.state === "pending_lock_finalization") {
1430
+ if (latestDelegation.state === "proposed" || latestDelegation.state === "offered") {
1455
1431
  const iAmOfferer = latestDelegation.offererDid === signerDid;
1456
1432
  const stateLabel = latestDelegation.state === "proposed" ? "PROPOSED" : `${latestDelegation.state.toUpperCase()} (\u2261 proposed)`;
1457
1433
  return {
@@ -1460,10 +1436,25 @@ function nextAction(input) {
1460
1436
  complete: false
1461
1437
  };
1462
1438
  }
1439
+ if (latestDelegation.state === "accepted" && !latestWorkLog) {
1440
+ const iAmOfferer = latestDelegation.offererDid === signerDid;
1441
+ return {
1442
+ hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is ACCEPTED \u2014 you (buyer) owe \`heyarp wallet create-lock --delegation-id ${latestDelegation.delegationId} --condition-hash <hex> ... > lock.json\` then \`heyarp delegation fund ${latestDelegation.delegationId} --escrow-lock-from-file lock.json\` (fund the escrow lock; work begins once it is LOCKED on chain)` : `Delegation ${latestDelegation.delegationId} is ACCEPTED \u2014 counterparty (the buyer) owes \`heyarp delegation fund\` to lock the escrow; wait for the delegation to reach LOCKED before working`,
1443
+ owner: iAmOfferer ? "me" : "counterparty",
1444
+ complete: false
1445
+ };
1446
+ }
1447
+ if (latestDelegation.state === "pending_lock_finalization" && !latestWorkLog) {
1448
+ return {
1449
+ hint: `Delegation ${latestDelegation.delegationId} is PENDING_LOCK_FINALIZATION \u2014 the escrow lock was funded and is awaiting on-chain confirmation; both sides wait (auto-advances to LOCKED). Poll with \`heyarp status ${relationship.relationshipId} --wait --until delegation.locked\`.`,
1450
+ owner: "none",
1451
+ complete: false
1452
+ };
1453
+ }
1463
1454
  if (!latestWorkLog) {
1464
1455
  const iAmOfferer = latestDelegation.offererDid === signerDid;
1465
1456
  return {
1466
- hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is ACCEPTED \u2014 you owe \`heyarp work request <peer> ${latestDelegation.delegationId} --request-id <new-uuid> --params '{\u2026}'\`` : `Delegation ${latestDelegation.delegationId} is ACCEPTED \u2014 counterparty (the caller) owes \`heyarp work request\``,
1457
+ hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is LOCKED \u2014 you owe \`heyarp work request <peer> ${latestDelegation.delegationId} --request-id <new-uuid> --params '{\u2026}'\`` : `Delegation ${latestDelegation.delegationId} is LOCKED \u2014 counterparty (the caller) owes \`heyarp work request\``,
1467
1458
  owner: iAmOfferer ? "me" : "counterparty",
1468
1459
  complete: false
1469
1460
  };
@@ -1576,7 +1567,7 @@ function cycleHeadline(s) {
1576
1567
  }
1577
1568
  }
1578
1569
  function stateColor(state) {
1579
- const c = state === "active" || state === "accepted" || state === "cosigned" || state === "responded" || state === "completed" ? import_chalk5.default.green : state === "pending" || state === "proposed" || state === "requested" || state === "offered" || state === "pending_lock_finalization" || state === "awaiting_release_finalization" ? import_chalk5.default.yellow : state === "closed" || state === "declined" || state === "canceled" || state === "replaced" || state === "not_found" || // Terminal failure states deserve the same red
1570
+ const c = state === "active" || state === "accepted" || state === "locked" || state === "cosigned" || state === "responded" || state === "completed" ? import_chalk5.default.green : state === "pending" || state === "proposed" || state === "requested" || state === "offered" || state === "pending_lock_finalization" || state === "awaiting_release_finalization" ? import_chalk5.default.yellow : state === "closed" || state === "declined" || state === "canceled" || state === "replaced" || state === "not_found" || // Terminal failure states deserve the same red
1580
1571
  // treatment as declined/canceled so an operator
1581
1572
  // scanning `heyarp status` immediately sees
1582
1573
  // "this is dead, don't follow the hint blindly".
@@ -1594,6 +1585,7 @@ var init_status = __esm({
1594
1585
  UNTIL_PHASES = [
1595
1586
  "delegation.proposed",
1596
1587
  "delegation.accepted",
1588
+ "delegation.locked",
1597
1589
  "delegation.canceled",
1598
1590
  "delegation.declined",
1599
1591
  "work.requested",
@@ -1839,7 +1831,7 @@ async function verifyReleaseHandler(opts) {
1839
1831
  if (shouldRetrySigCheck) {
1840
1832
  const retryDelaysMs = [3e3, 3e3];
1841
1833
  for (const delay of retryDelaysMs) {
1842
- await new Promise((resolve2) => setTimeout(resolve2, delay));
1834
+ await new Promise((resolve) => setTimeout(resolve, delay));
1843
1835
  const retriedSigs = await conn.getSignaturesForAddress(lockPda, { limit: 5 });
1844
1836
  if (retriedSigs.length > 0) {
1845
1837
  lockSeenInSignatures = true;
@@ -1953,7 +1945,7 @@ function mapReleaseMethodToStatus(method) {
1953
1945
  }
1954
1946
  function registerCreateLock(cmd) {
1955
1947
  cmd.command("create-lock").description(
1956
- "Build + sign a create_lock Solana tx (native SOL or an SPL token); output JSON ready for attachments.escrow_lock. This command does NOT submit the tx to chain \u2014 it only signs the blob locally. The actual on-chain submission happens inside `heyarp delegation offer --escrow-lock-from-file <path>` (the server's escrow worker picks up the signed blob from the offer envelope's attachments and posts it). Watch for the `lock_id` appearing on-chain only AFTER `delegation offer` runs, not after this command."
1948
+ "Build + sign a create_lock Solana tx (native SOL or an SPL token); output JSON ready for attachments.escrow_lock. This command does NOT submit the tx to chain \u2014 it only signs the blob locally. M6 lock-at-accept: run this AFTER the worker accepts your offer; the actual on-chain submission happens inside `heyarp delegation fund <delegation-id> --escrow-lock-from-file <path>` (the server's escrow worker picks up the signed blob from the fund envelope's attachments and posts it). Watch for the `lock_id` appearing on-chain only AFTER `delegation fund` runs, not after this command."
1957
1949
  ).option("--server <url>", "Override server URL for sender-key resolution").option("--from-did <did>", "Sender DID (= payer of the lock). Required when more than one agent on the host.").requiredOption("--delegation-id <id>", "Delegation UUID (drives the lock_id derivation)").requiredOption("--recipient-pubkey <base58>", "Payee Solana pubkey (recipient agent's settlement_pubkey)").option("--amount-lamports <int>", "NATIVE SOL: lock amount in lamports (1 SOL = 1_000_000_000). Required when --mint-pubkey is omitted; not allowed with it.").option("--mint-pubkey <base58>", "SPL token mint to lock. Omit for native SOL. Must be a legacy SPL Token mint (Token-2022 is not supported).").option("--amount-base-units <int>", "SPL: lock amount in the mint's base units (e.g. 1000000 = 1 USDC at 6 decimals). Canonical SPL amount input.").option(
1958
1950
  "--amount <decimal>",
1959
1951
  "SPL: lock amount as a human decimal (e.g. 1.5); converted using the mint's on-chain decimals. Convenience alternative to --amount-base-units."
@@ -2199,7 +2191,7 @@ async function createLockHandler(opts) {
2199
2191
  asset_id: asset.assetId,
2200
2192
  expiry: Number(expiry),
2201
2193
  // Persist the BARE UUID form. The consumer is
2202
- // `delegation offer --escrow-lock-from-file`, which validates
2194
+ // `delegation fund --escrow-lock-from-file`, which validates
2203
2195
  // this field with a bare-UUID regex (the wire form for
2204
2196
  // `body.content.delegation_id`). The `del_<uuid>` prefix is an
2205
2197
  // SDK-internal byte-encoding marker — not the wire-form. Keeping
@@ -2385,6 +2377,7 @@ var init_wallet = __esm({
2385
2377
  function registerDelegationCommands(root) {
2386
2378
  const cmd = root.command("delegation").description("Delegation FSM actions: offer / accept / decline / cancel");
2387
2379
  registerOffer(cmd);
2380
+ registerFund(cmd);
2388
2381
  registerAccept(cmd);
2389
2382
  registerDecline(cmd);
2390
2383
  registerCancel(cmd);
@@ -2393,19 +2386,7 @@ function isPostCommitErrorCode(code) {
2393
2386
  return POST_COMMIT_ERROR_CODES.has(code) || code.startsWith("ESC_LOCK_") || code.startsWith("SDK_");
2394
2387
  }
2395
2388
  function registerOffer(parent) {
2396
- parent.command("offer").description("Open a new delegation addressed to <recipient-did> with the agreed terms INLINE.").argument("<recipient-did>", "Recipient agent DID (did:arp:...)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--delegation-id <uuid>", "Override the auto-generated delegation id (UUID). Useful for replay / scripting.").option("--title <s>", "Required: human-readable title for the offer").option("--brief <json>", "Optional structured brief (JSON object)").option("--criterion <s>", "acceptance_criteria \u2014 repeatable; pass --criterion once per bullet", collectRepeated, []).option("--deadline <rfc3339>", 'Optional RFC 3339 deadline (e.g. "2026-12-31T23:59:59Z")').option("--scope <text>", "scope_summary \u2014 short prose describing the agreed work. Required for escrow offers.").option("--pricing-model <flat|usage_based>", "pricing_model (flat|usage_based). Required for escrow offers.").option("--settlement-model <prepaid|escrow>", "settlement_model (prepaid|escrow). Required for escrow offers.").option("--rate-amount <decimal>", 'rate_amount \u2014 decimal as string (e.g. "10.00"). Optional per-unit rate bound into the condition_hash.').option("--rate-unit <task|thread|handoff>", "rate_unit (task|thread|handoff). Optional.").option("--amount <s>", 'Optional decimal-as-string amount (e.g. "10.00"). REQUIRES --currency if set.').option("--currency <s>", `Asset identifier: shorthand (${import_sdk4.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string.`).option("--currency-decimals <n>", "Decimal places for base-unit conversion (0-18). Required only when --currency is raw CAIP-19.").option("--currency-symbol <s>", 'Optional UI hint ("USDC", "SOL"). Max 16 chars.').option("--ttl <seconds>", "Envelope TTL in seconds (max 86400 = 24h)", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).option(
2397
- "--escrow-lock-from-file <path>",
2398
- "Path to JSON output of `heyarp wallet create-lock` (recommended). Contains all 5 escrow_lock fields: signed_tx_blob, lock_id, amount, asset_id, expiry. Mutually exclusive with the inline --escrow-lock-* flags below."
2399
- ).option(
2400
- "--escrow-lock-blob <base64>",
2401
- "INLINE alternative: the signed Solana tx blob (base64). Requires --escrow-lock-id, --escrow-lock-amount, --escrow-lock-asset-id, --escrow-lock-expiry together."
2402
- ).option("--escrow-lock-id <hex32>", "INLINE lock_id (32-byte hex). Used with --escrow-lock-blob.").option("--escrow-lock-amount <int>", "INLINE lock amount in base units (e.g. lamports for native SOL). Used with --escrow-lock-blob.").option("--escrow-lock-asset-id <caip19>", "INLINE currency CAIP-19 asset_id (e.g. solana:<cluster_id>/slip44:501). Used with --escrow-lock-blob.").option("--escrow-lock-expiry <unix>", "INLINE expiry as unix seconds. Used with --escrow-lock-blob.").option(
2403
- "--program-id <pubkey>",
2404
- "Expected ARP escrow program id for pre-flight against the lock file's embedded `program_id`. Precedence: this flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee endpoint. Mismatch throws BEFORE the envelope ships, so a wrong-program lock never silently fails on chain after the offer was already delivered. Pre-flight is skipped only for old lock files lacking `program_id`, or when no expected value can be resolved."
2405
- ).option(
2406
- "--no-escrow",
2407
- "Opt-out for test_mode servers. Default REQUIRES the escrow_lock attachment; without --no-escrow, missing flags throw BEFORE the envelope is sent (avoids ESC_LOCK_MISSING POST_COMMIT consuming sender_sequence)."
2408
- ).option(
2389
+ parent.command("offer").description("Open a new delegation addressed to <recipient-did> with the agreed terms INLINE (no escrow lock \u2014 fund AFTER acceptance via `delegation fund`).").argument("<recipient-did>", "Recipient agent DID (did:arp:...)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--delegation-id <uuid>", "Override the auto-generated delegation id (UUID). Reuse the SAME id at `heyarp delegation fund` time. Useful for replay / scripting.").option("--title <s>", "Required: human-readable title for the offer").option("--brief <json>", "Optional structured brief (JSON object)").option("--criterion <s>", "acceptance_criteria \u2014 repeatable; pass --criterion once per bullet", collectRepeated, []).option("--deadline <rfc3339>", 'Optional RFC 3339 deadline (e.g. "2026-12-31T23:59:59Z")').option("--scope <text>", "scope_summary \u2014 short prose describing the agreed work. Required.").option("--pricing-model <flat|usage_based>", "pricing_model (flat|usage_based). Required.").option("--amount <s>", 'Optional decimal-as-string amount (e.g. "10.00"). REQUIRES --currency if set.').option("--currency <s>", `Asset identifier: shorthand (${import_sdk4.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string.`).option("--currency-decimals <n>", "Decimal places for base-unit conversion (0-18). Required only when --currency is raw CAIP-19.").option("--currency-symbol <s>", 'Optional UI hint ("USDC", "SOL"). Max 16 chars.').option("--ttl <seconds>", "Envelope TTL in seconds (max 86400 = 24h)", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).option(
2409
2390
  "--wait-until <phase>",
2410
2391
  'Block after delivery until the named FSM phase is reached (e.g. delegation.accepted). One of the UNTIL_PHASES from `heyarp status --help`. Exit code 124 on --wait-timeout. Recovers the "sub-agent exits before counterparty accepts" antipattern.'
2411
2392
  ).option("--wait-timeout <seconds>", "When --wait-until is set: max wall-clock wait (default 300). Exit code 124 on timeout.").option("--wait-interval <seconds>", "When --wait-until is set: poll cadence (default 3, bound [1, 60]).").option(
@@ -2435,39 +2416,35 @@ function assembleEscrowLockAttachment(opts) {
2435
2416
  const someInlineSet = inlineFlags.some((f) => f !== void 0 && f !== "");
2436
2417
  const allInlineSet = inlineFlags.every((f) => f !== void 0 && f !== "");
2437
2418
  if (fromFile && someInlineSet) {
2438
- throw new Error("delegation offer: --escrow-lock-from-file and --escrow-lock-blob/--escrow-lock-* are mutually exclusive. Pick one path.");
2419
+ throw new Error("delegation fund: --escrow-lock-from-file and --escrow-lock-blob/--escrow-lock-* are mutually exclusive. Pick one path.");
2439
2420
  }
2440
2421
  if (!fromFile && !someInlineSet) {
2441
- if (opts.escrow === false) return void 0;
2442
2422
  throw new Error(
2443
- "delegation offer: escrow_lock is required. Pass --escrow-lock-from-file <path> (JSON output of `heyarp wallet create-lock`), OR all of --escrow-lock-blob/--escrow-lock-id/--escrow-lock-amount/--escrow-lock-asset-id/--escrow-lock-expiry, OR --no-escrow if the server is in test_mode."
2423
+ "delegation fund: escrow_lock is required. Pass --escrow-lock-from-file <path> (JSON output of `heyarp wallet create-lock`), OR all of --escrow-lock-blob/--escrow-lock-id/--escrow-lock-amount/--escrow-lock-asset-id/--escrow-lock-expiry."
2444
2424
  );
2445
2425
  }
2446
- if (opts.escrow === false) {
2447
- throw new Error("delegation offer: --no-escrow is mutually exclusive with explicit escrow_lock flags. Pick one path.");
2448
- }
2449
2426
  if (fromFile) {
2450
2427
  let raw;
2451
2428
  try {
2452
2429
  raw = (0, import_node_fs4.readFileSync)(fromFile, "utf8");
2453
2430
  } catch (err) {
2454
- throw new Error(`delegation offer: failed to read --escrow-lock-from-file '${fromFile}': ${err.message}`);
2431
+ throw new Error(`delegation fund: failed to read --escrow-lock-from-file '${fromFile}': ${err.message}`);
2455
2432
  }
2456
2433
  let parsed;
2457
2434
  try {
2458
2435
  parsed = JSON.parse(raw);
2459
2436
  } catch (err) {
2460
- throw new Error(`delegation offer: --escrow-lock-from-file '${fromFile}' is not valid JSON: ${err.message}`);
2437
+ throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' is not valid JSON: ${err.message}`);
2461
2438
  }
2462
2439
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2463
- throw new Error(`delegation offer: --escrow-lock-from-file '${fromFile}' must be a JSON object, got ${Array.isArray(parsed) ? "array" : typeof parsed}.`);
2440
+ throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' must be a JSON object, got ${Array.isArray(parsed) ? "array" : typeof parsed}.`);
2464
2441
  }
2465
2442
  const p = parsed;
2466
2443
  const required = ["signed_tx_blob", "lock_id", "amount", "asset_id", "expiry"];
2467
2444
  const missing = required.filter((k) => p[k] === void 0);
2468
2445
  if (missing.length > 0) {
2469
2446
  throw new Error(
2470
- `delegation offer: --escrow-lock-from-file '${fromFile}' is missing required fields: ${missing.join(", ")}. Expected JSON output of \`heyarp wallet create-lock\`.`
2447
+ `delegation fund: --escrow-lock-from-file '${fromFile}' is missing required fields: ${missing.join(", ")}. Expected JSON output of \`heyarp wallet create-lock\`.`
2471
2448
  );
2472
2449
  }
2473
2450
  const expiryRaw = p.expiry;
@@ -2478,19 +2455,19 @@ function assembleEscrowLockAttachment(opts) {
2478
2455
  expiryNum2 = Number(expiryRaw);
2479
2456
  if (String(expiryNum2) !== expiryRaw.trim()) {
2480
2457
  throw new Error(
2481
- `delegation offer: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`
2458
+ `delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`
2482
2459
  );
2483
2460
  }
2484
2461
  } else {
2485
- throw new Error(`delegation offer: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`);
2462
+ throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`);
2486
2463
  }
2487
2464
  if (!Number.isFinite(expiryNum2) || !Number.isInteger(expiryNum2) || expiryNum2 <= 0) {
2488
- throw new Error(`delegation offer: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`);
2465
+ throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`);
2489
2466
  }
2490
2467
  let delegationIdFromLock;
2491
2468
  if (p.delegation_id !== void 0) {
2492
2469
  if (typeof p.delegation_id !== "string" || !UUID_RE.test(p.delegation_id)) {
2493
- throw new Error(`delegation offer: --escrow-lock-from-file '${fromFile}' has invalid 'delegation_id' (must be a UUID): ${JSON.stringify(p.delegation_id)}.`);
2470
+ throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'delegation_id' (must be a UUID): ${JSON.stringify(p.delegation_id)}.`);
2494
2471
  }
2495
2472
  delegationIdFromLock = p.delegation_id.toLowerCase();
2496
2473
  }
@@ -2498,7 +2475,7 @@ function assembleEscrowLockAttachment(opts) {
2498
2475
  if (p.program_id !== void 0) {
2499
2476
  if (typeof p.program_id !== "string" || p.program_id.length < 32 || p.program_id.length > 44) {
2500
2477
  throw new Error(
2501
- `delegation offer: --escrow-lock-from-file '${fromFile}' has invalid 'program_id' (must be a base58 Solana pubkey, 32-44 chars): ${JSON.stringify(p.program_id)}.`
2478
+ `delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'program_id' (must be a base58 Solana pubkey, 32-44 chars): ${JSON.stringify(p.program_id)}.`
2502
2479
  );
2503
2480
  }
2504
2481
  lockProgramId = p.program_id;
@@ -2517,13 +2494,13 @@ function assembleEscrowLockAttachment(opts) {
2517
2494
  }
2518
2495
  if (!allInlineSet) {
2519
2496
  throw new Error(
2520
- "delegation offer: when using inline escrow flags, --escrow-lock-blob requires all of --escrow-lock-id, --escrow-lock-amount, --escrow-lock-asset-id, --escrow-lock-expiry to be set together."
2497
+ "delegation fund: when using inline escrow flags, --escrow-lock-blob requires all of --escrow-lock-id, --escrow-lock-amount, --escrow-lock-asset-id, --escrow-lock-expiry to be set together."
2521
2498
  );
2522
2499
  }
2523
2500
  const expiryStr = opts.escrowLockExpiry ?? "";
2524
2501
  const expiryNum = Number.parseInt(expiryStr, 10);
2525
2502
  if (!Number.isFinite(expiryNum) || !Number.isInteger(expiryNum) || expiryNum <= 0 || String(expiryNum) !== expiryStr) {
2526
- throw new Error(`delegation offer: --escrow-lock-expiry must be a positive integer (unix seconds), got '${expiryStr}'.`);
2503
+ throw new Error(`delegation fund: --escrow-lock-expiry must be a positive integer (unix seconds), got '${expiryStr}'.`);
2527
2504
  }
2528
2505
  return {
2529
2506
  attachment: {
@@ -2545,47 +2522,22 @@ async function runOffer(recipientDid, opts) {
2545
2522
  throw new Error("delegation offer: --title is required (server-side validator rejects empty offers)");
2546
2523
  }
2547
2524
  const terms = parseOfferTerms("delegation offer", opts);
2548
- const escrowResult = assembleEscrowLockAttachment(opts);
2549
- const delegationId = resolveOfferDelegationId(opts.delegationId, escrowResult);
2550
- if (escrowResult !== void 0) {
2551
- if (terms.amount === void 0 || terms.currency === void 0) {
2552
- throw new Error(
2553
- "delegation offer: escrow-backed offers require both --amount and --currency (server cross-checks body.content.amount/currency against attachments.escrow_lock.amount/asset_id)."
2554
- );
2555
- }
2556
- const missingTerms = [
2557
- terms.scope_summary === void 0 ? "--scope" : null,
2558
- terms.pricing_model === void 0 ? "--pricing-model" : null,
2559
- terms.settlement_model === void 0 ? "--settlement-model" : null
2560
- ].filter((x) => x !== null);
2561
- if (missingTerms.length > 0) {
2562
- throw new Error(
2563
- `delegation offer: escrow-backed offers require the inline terms ${missingTerms.join(", ")} (the on-chain condition_hash binds scope_summary/pricing_model/settlement_model/rate_amount/rate_unit/currency; a missing or divergent term is rejected by the server with ESC_LOCK_CONDITION_HASH_MISMATCH). Pass the SAME values you fed to \`heyarp escrow derive-condition-hash\`.`
2564
- );
2565
- }
2525
+ const delegationId = resolveOfferDelegationId(opts.delegationId, void 0);
2526
+ if (terms.amount === void 0 || terms.currency === void 0) {
2527
+ throw new Error(
2528
+ "delegation offer: escrow-backed offers require both --amount and --currency (these are the agreed terms; the buyer funds the matching escrow_lock later via `heyarp delegation fund`)."
2529
+ );
2530
+ }
2531
+ const missingTerms = [terms.scope_summary === void 0 ? "--scope" : null, terms.pricing_model === void 0 ? "--pricing-model" : null].filter(
2532
+ (x) => x !== null
2533
+ );
2534
+ if (missingTerms.length > 0) {
2535
+ throw new Error(
2536
+ `delegation offer: escrow-backed offers require the inline terms ${missingTerms.join(", ")} (the on-chain condition_hash binds scope_summary/pricing_model/currency at fund time; a missing or divergent term is rejected with ESC_LOCK_CONDITION_HASH_MISMATCH when you run \`heyarp delegation fund\`). Pass the SAME values you will feed to \`heyarp escrow derive-condition-hash\`.`
2537
+ );
2566
2538
  }
2567
2539
  const api = new ArpApiClient(opts.server);
2568
2540
  const sender = resolveSenderAgent("delegation offer", opts.server, opts.fromDid);
2569
- if (escrowResult?.lockProgramId !== void 0) {
2570
- const expected = await resolveProgramIdWithSource(api, { programId: opts.programId });
2571
- const result2 = preflightLockProgramId({
2572
- lockProgramId: escrowResult.lockProgramId,
2573
- expectedProgramId: expected.programId,
2574
- expectedSource: expected.source
2575
- });
2576
- if (result2.kind === "mismatch") {
2577
- throw new Error(
2578
- `delegation offer: lock file program_id ${result2.lockProgramId} does not match the expected escrow program ${result2.expectedProgramId} (resolved from --program-id flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee). The lock tx would be rejected on chain (ESC_LOCK_TX_PROGRAM_ID_MISMATCH) and the delegation would silently fail ~26s after offer. Regenerate the lock with the correct program id via \`heyarp wallet create-lock --program-id ${result2.expectedProgramId} ...\`.`
2579
- );
2580
- }
2581
- if (result2.kind === "skipped_no_expected_program_id") {
2582
- console.error(
2583
- import_chalk6.default.yellow(
2584
- `\u26A0 delegation offer: no authoritative expected program-id available (--program-id missing, ARP_ESCROW_PROGRAM_ID env unset, server protocol-fee unreachable). Pre-flight skipped; server-side ESC_LOCK_TX_PROGRAM_ID_MISMATCH check is the only backstop. Set ARP_ESCROW_PROGRAM_ID or pass --program-id to enable pre-flight.`
2585
- )
2586
- );
2587
- }
2588
- }
2589
2541
  const content = {
2590
2542
  action: "offer",
2591
2543
  delegation_id: delegationId,
@@ -2593,22 +2545,21 @@ async function runOffer(recipientDid, opts) {
2593
2545
  ...terms
2594
2546
  };
2595
2547
  const body = { type: "delegation", content };
2596
- const attachments = escrowResult ? { escrow_lock: escrowResult.attachment } : void 0;
2597
2548
  console.log(import_chalk6.default.dim(`Server: ${api.serverUrl}`));
2598
2549
  console.log(import_chalk6.default.dim(`Sender: ${sender.did}`));
2599
2550
  console.log(import_chalk6.default.dim(`Recipient: ${recipientDid}`));
2600
2551
  console.log(import_chalk6.default.dim(`Delegation: ${delegationId}`));
2601
- if (escrowResult) {
2602
- const a = escrowResult.attachment;
2603
- console.log(import_chalk6.default.dim(`Escrow lock attached: lock_id=${a.lock_id} amount=${a.amount} asset_id=${a.asset_id}`));
2604
- }
2605
- const result = await sendDelegationEnvelope({ api, sender, recipientDid, body, attachments, ttlSeconds, verbose: opts.verbose, server: opts.server });
2552
+ const result = await sendDelegationEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
2606
2553
  printIngestResult(result);
2607
2554
  console.log(import_chalk6.default.dim(`
2608
2555
  Reference this delegation on subsequent calls with:`));
2609
2556
  console.log(import_chalk6.default.dim(` heyarp delegation accept ${result.relationshipId} ${delegationId}`));
2610
2557
  console.log(import_chalk6.default.dim(` heyarp delegation decline ${result.relationshipId} ${delegationId}`));
2611
2558
  console.log(import_chalk6.default.dim(` heyarp delegation cancel ${result.relationshipId} ${delegationId}`));
2559
+ console.log(import_chalk6.default.dim(`
2560
+ After the worker accepts, fund the escrow lock:`));
2561
+ console.log(import_chalk6.default.dim(` heyarp wallet create-lock --delegation-id ${delegationId} --condition-hash <hex> ... > lock.json`));
2562
+ console.log(import_chalk6.default.dim(` heyarp delegation fund ${delegationId} --escrow-lock-from-file lock.json`));
2612
2563
  if (opts.waitUntil) {
2613
2564
  const untilPhase = parseUntilPhase(opts.waitUntil);
2614
2565
  if (untilPhase === void 0) {
@@ -2628,6 +2579,152 @@ Reference this delegation on subsequent calls with:`));
2628
2579
  });
2629
2580
  }
2630
2581
  }
2582
+ function registerFund(parent) {
2583
+ parent.command("fund").description("Fund an ACCEPTED delegation with the escrow lock (buyer-only). Run AFTER the worker accepts the offer.").argument("<delegation-id>", "Delegation UUID (the one you offered + the worker accepted)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--ttl <seconds>", "Envelope TTL in seconds (max 86400 = 24h)", "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
2584
+ "--json",
2585
+ 'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"fund", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude moves off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
2586
+ false
2587
+ ).option(
2588
+ "--escrow-lock-from-file <path>",
2589
+ "Path to JSON output of `heyarp wallet create-lock` (recommended). Contains all 5 escrow_lock fields: signed_tx_blob, lock_id, amount, asset_id, expiry (+ delegation_id / program_id for pre-flight). Mutually exclusive with the inline --escrow-lock-* flags below."
2590
+ ).option(
2591
+ "--escrow-lock-blob <base64>",
2592
+ "INLINE alternative: the signed Solana tx blob (base64). Requires --escrow-lock-id, --escrow-lock-amount, --escrow-lock-asset-id, --escrow-lock-expiry together."
2593
+ ).option("--escrow-lock-id <hex32>", "INLINE lock_id (32-byte hex). Used with --escrow-lock-blob.").option("--escrow-lock-amount <int>", "INLINE lock amount in base units (e.g. lamports for native SOL). Used with --escrow-lock-blob.").option("--escrow-lock-asset-id <caip19>", "INLINE currency CAIP-19 asset_id (e.g. solana:<cluster_id>/slip44:501). Used with --escrow-lock-blob.").option("--escrow-lock-expiry <unix>", "INLINE expiry as unix seconds. Used with --escrow-lock-blob.").option(
2594
+ "--program-id <pubkey>",
2595
+ "Expected ARP escrow program id for pre-flight against the lock file's embedded `program_id`. Precedence: this flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee endpoint. Mismatch throws BEFORE the envelope ships, so a wrong-program lock never silently fails on chain after fund was already delivered. Pre-flight is skipped only for old lock files lacking `program_id`, or when no expected value can be resolved."
2596
+ ).option(
2597
+ "--wait-until <phase>",
2598
+ "Block after delivery until the named FSM phase is reached (typically delegation.locked \u2014 resolves once the escrow lock confirms on chain). One of the UNTIL_PHASES from `heyarp status --help`. Exit code 124 on --wait-timeout."
2599
+ ).option("--wait-timeout <seconds>", "When --wait-until is set: max wall-clock wait (default 300). Exit code 124 on timeout.").option("--wait-interval <seconds>", "When --wait-until is set: poll cadence (default 3, bound [1, 60]).").option("--wait-verbose", "When --wait-until is set: emit one dim line per poll tick showing the current FSM state.", false).action(async (delegationId, opts) => {
2600
+ await runFund(delegationId, opts);
2601
+ });
2602
+ }
2603
+ async function runFund(delegationId, opts) {
2604
+ if (opts.verbose && opts.json) {
2605
+ throw new Error(
2606
+ "delegation fund: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
2607
+ );
2608
+ }
2609
+ delegationId = requireUuidNormalised("delegation fund", delegationId, "<delegation-id>");
2610
+ const ttlSeconds = parseTtl("delegation fund", opts.ttl);
2611
+ const escrowResult = assembleEscrowLockAttachment({
2612
+ escrowLockFromFile: opts.escrowLockFromFile,
2613
+ escrowLockBlob: opts.escrowLockBlob,
2614
+ escrowLockId: opts.escrowLockId,
2615
+ escrowLockAmount: opts.escrowLockAmount,
2616
+ escrowLockAssetId: opts.escrowLockAssetId,
2617
+ escrowLockExpiry: opts.escrowLockExpiry
2618
+ });
2619
+ if (escrowResult.delegationIdFromLock !== void 0 && escrowResult.delegationIdFromLock !== delegationId) {
2620
+ throw new Error(
2621
+ `delegation fund: <delegation-id> (${delegationId}) disagrees with the lock file's delegation_id (${escrowResult.delegationIdFromLock}). The on-chain lock_id is derived from the lock-side id; funding a different delegation would guarantee ESC_LOCK_ID_MISMATCH. Fund the delegation the lock was built for, or regenerate the lock with --delegation-id ${delegationId}.`
2622
+ );
2623
+ }
2624
+ const api = new ArpApiClient(opts.server);
2625
+ const sender = resolveSenderAgent("delegation fund", opts.server, opts.fromDid);
2626
+ const signer = makeSigner(sender);
2627
+ const resolved = await resolveFundRefs("delegation fund", api, signer, { delegationId, selfDid: sender.did });
2628
+ if (escrowResult.lockProgramId !== void 0) {
2629
+ const expected = await resolveProgramIdWithSource(api, { programId: opts.programId });
2630
+ const result2 = preflightLockProgramId({
2631
+ lockProgramId: escrowResult.lockProgramId,
2632
+ expectedProgramId: expected.programId,
2633
+ expectedSource: expected.source
2634
+ });
2635
+ if (result2.kind === "mismatch") {
2636
+ throw new Error(
2637
+ `delegation fund: lock file program_id ${result2.lockProgramId} does not match the expected escrow program ${result2.expectedProgramId} (resolved from --program-id flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee). The lock tx would be rejected on chain (ESC_LOCK_TX_PROGRAM_ID_MISMATCH) and the delegation would silently fail ~26s after fund. Regenerate the lock with the correct program id via \`heyarp wallet create-lock --program-id ${result2.expectedProgramId} ...\`.`
2638
+ );
2639
+ }
2640
+ if (result2.kind === "skipped_no_expected_program_id") {
2641
+ console.error(
2642
+ import_chalk6.default.yellow(
2643
+ "\u26A0 delegation fund: no authoritative expected program-id available (--program-id missing, ARP_ESCROW_PROGRAM_ID env unset, server protocol-fee unreachable). Pre-flight skipped; server-side ESC_LOCK_TX_PROGRAM_ID_MISMATCH check is the only backstop. Set ARP_ESCROW_PROGRAM_ID or pass --program-id to enable pre-flight."
2644
+ )
2645
+ );
2646
+ }
2647
+ }
2648
+ const content = { action: "fund", delegation_id: delegationId };
2649
+ const body = { type: "delegation", content };
2650
+ const attachments = { escrow_lock: escrowResult.attachment };
2651
+ progress(opts.json, import_chalk6.default.dim(`Server: ${api.serverUrl}`));
2652
+ progress(opts.json, import_chalk6.default.dim(`Sender: ${sender.did}`));
2653
+ progress(opts.json, import_chalk6.default.dim(`Recipient: ${resolved.recipientDid}`));
2654
+ progress(opts.json, import_chalk6.default.dim(`Relationship: ${resolved.relationshipId}`));
2655
+ progress(opts.json, import_chalk6.default.dim(`Delegation: ${delegationId} (action=fund)`));
2656
+ {
2657
+ const a = escrowResult.attachment;
2658
+ progress(opts.json, import_chalk6.default.dim(`Escrow lock attached: lock_id=${a.lock_id} amount=${a.amount} asset_id=${a.asset_id}`));
2659
+ }
2660
+ const result = await sendDelegationEnvelope({
2661
+ api,
2662
+ sender,
2663
+ recipientDid: resolved.recipientDid,
2664
+ body,
2665
+ attachments,
2666
+ ttlSeconds,
2667
+ verbose: opts.verbose,
2668
+ server: opts.server
2669
+ });
2670
+ if (opts.json) {
2671
+ jsonOut({
2672
+ ok: true,
2673
+ action: "fund",
2674
+ delegationId,
2675
+ eventId: result.eventId,
2676
+ relationshipId: result.relationshipId,
2677
+ relationshipEventIndex: result.relationshipEventIndex,
2678
+ serverTimestamp: result.serverTimestamp,
2679
+ serverEventHash: result.serverEventHash,
2680
+ prevServerEventHash: result.prevServerEventHash ?? null
2681
+ });
2682
+ } else {
2683
+ printIngestResult(result);
2684
+ console.log(import_chalk6.default.dim("\nThe worker waits for the on-chain lock to confirm (LOCKED), then begins work."));
2685
+ }
2686
+ if (opts.waitUntil && !opts.json) {
2687
+ const untilPhase = parseUntilPhase(opts.waitUntil);
2688
+ if (untilPhase === void 0) {
2689
+ throw new Error(`delegation fund: --wait-until requires a phase value (got ${JSON.stringify(opts.waitUntil)})`);
2690
+ }
2691
+ await awaitFsmTransitionAfterAction({
2692
+ api,
2693
+ signerDid: sender.did,
2694
+ signer,
2695
+ relationshipId: result.relationshipId,
2696
+ untilPhase,
2697
+ waitIntervalSec: parseWaitInterval(opts.waitInterval),
2698
+ waitTimeoutSec: parseWaitTimeout(opts.waitTimeout),
2699
+ waitVerbose: !!opts.waitVerbose,
2700
+ json: false
2701
+ });
2702
+ }
2703
+ }
2704
+ async function resolveFundRefs(cmdName, api, signer, args) {
2705
+ const relationships = await api.listRelationships(args.selfDid, signer, { limit: 100 });
2706
+ for (const rel of relationships) {
2707
+ let after;
2708
+ for (; ; ) {
2709
+ const page = await api.listDelegations(rel.relationshipId, signer, { limit: 100, after });
2710
+ const row = page.find((d) => d.delegationId === args.delegationId);
2711
+ if (row) {
2712
+ if (row.offererDid !== args.selfDid) {
2713
+ throw new Error(
2714
+ `${cmdName}: delegation ${args.delegationId} was offered by ${row.offererDid}, not you (${args.selfDid}). Funding the escrow lock is the BUYER's (offerer's) action.`
2715
+ );
2716
+ }
2717
+ const recipientDid = rel.pairDidA === args.selfDid ? rel.pairDidB : rel.pairDidA;
2718
+ return { relationshipId: rel.relationshipId, recipientDid, state: row.state };
2719
+ }
2720
+ if (page.length < 100) break;
2721
+ after = page[page.length - 1].id;
2722
+ }
2723
+ }
2724
+ throw new Error(
2725
+ `${cmdName}: delegation ${args.delegationId} not found in any of your relationships (checked the first 100 by recent activity). Confirm the worker accepted your offer and that you are the offerer; inspect with \`heyarp delegations <relationship-id> --from-did ${args.selfDid}\`.`
2726
+ );
2727
+ }
2631
2728
  function registerAccept(parent) {
2632
2729
  parent.command("accept").description("Accept a PROPOSED delegation \u2014 promotes to ACCEPTED. Counterparty-only.").argument("<relationship-id>", "Relationship UUID").argument("<delegation-id>", "Delegation UUID").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
2633
2730
  "--json",
@@ -2654,7 +2751,7 @@ function registerDecline(parent) {
2654
2751
  });
2655
2752
  }
2656
2753
  function registerCancel(parent) {
2657
- parent.command("cancel").description("Cancel a PROPOSED delegation \u2014 moves to CANCELED. Offerer-only (the OTHER side uses decline).").argument("<relationship-id>", "Relationship UUID").argument("<delegation-id>", "Delegation UUID").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).option(
2754
+ parent.command("cancel").description("Cancel an OFFERED delegation \u2014 moves to CANCELED. Offerer-only (the OTHER side uses decline).").argument("<relationship-id>", "Relationship UUID").argument("<delegation-id>", "Delegation UUID").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).option(
2658
2755
  "--no-wait-for-lock",
2659
2756
  "Opt-out of the pending-lock pre-flight poll. Default is ON: when the resolved delegation is in `pending_lock_finalization`, the CLI polls until the on-chain lock is confirmed before signing the envelope (avoids the racy DELEGATION_PENDING_LOCK 409 that consumes sender_sequence)."
2660
2757
  ).option("--lock-wait-timeout <seconds>", "Max wall-clock seconds to wait for pending-lock pre-flight (default 300).", "300").option("--lock-wait-interval <seconds>", "Poll cadence for pending-lock pre-flight in seconds. Bound to [1, 60].", "3").action(async (relationshipId, delegationId, opts) => {
@@ -2931,11 +3028,6 @@ function parseOfferTerms(cmdName, opts) {
2931
3028
  if (opts.scope) out.scope_summary = opts.scope;
2932
3029
  const pricingModel = parsePricingModel(cmdName, opts.pricingModel);
2933
3030
  if (pricingModel !== void 0) out.pricing_model = pricingModel;
2934
- const settlementModel = parseSettlementModel(cmdName, opts.settlementModel);
2935
- if (settlementModel !== void 0) out.settlement_model = settlementModel;
2936
- if (opts.rateAmount) out.rate_amount = opts.rateAmount;
2937
- const rateUnit = parseRateUnit(cmdName, opts.rateUnit);
2938
- if (rateUnit !== void 0) out.rate_unit = rateUnit;
2939
3031
  if (opts.amount) {
2940
3032
  out.amount = opts.amount;
2941
3033
  if (!opts.currency) {
@@ -2954,20 +3046,6 @@ function parsePricingModel(cmdName, raw) {
2954
3046
  }
2955
3047
  return raw;
2956
3048
  }
2957
- function parseSettlementModel(cmdName, raw) {
2958
- if (raw === void 0 || raw === "") return void 0;
2959
- if (raw !== "prepaid" && raw !== "escrow") {
2960
- throw new Error(`${cmdName}: --settlement-model must be one of prepaid|escrow (got '${raw}')`);
2961
- }
2962
- return raw;
2963
- }
2964
- function parseRateUnit(cmdName, raw) {
2965
- if (raw === void 0 || raw === "") return void 0;
2966
- if (raw !== "task" && raw !== "thread" && raw !== "handoff") {
2967
- throw new Error(`${cmdName}: --rate-unit must be one of task|thread|handoff (got '${raw}')`);
2968
- }
2969
- return raw;
2970
- }
2971
3049
  function parseTtl(cmdName, raw) {
2972
3050
  if (raw === void 0) return 3600;
2973
3051
  const n = Number(raw);
@@ -3095,6 +3173,17 @@ var init_delegation = __esm({
3095
3173
  // against a PENDING_LOCK delegation consumes sender_sequence
3096
3174
  // even though it rejects.
3097
3175
  "DELEGATION_PENDING_LOCK",
3176
+ // M6 lock-at-accept: the `fund` body handler (`handleFund`) emits
3177
+ // these AFTER the event row is committed (same lifecycle as
3178
+ // DELEGATION_INVALID_STATE). `DELEGATION_FUNDER_NOT_OFFERER` (403)
3179
+ // when a non-offerer attempts the fund; `DELEGATION_ALREADY_FUNDED`
3180
+ // (409) on a re-fund of a delegation that already moved into the
3181
+ // lock lifecycle (pending_lock_finalization / locked, or an
3182
+ // in-flight create_lock op). Both consume sender_sequence, so the
3183
+ // CLI must advance `lastSenderSequence` or a retry trips
3184
+ // `ENV_SEQUENCE_BACKWARDS`.
3185
+ "DELEGATION_FUNDER_NOT_OFFERER",
3186
+ "DELEGATION_ALREADY_FUNDED",
3098
3187
  // V1.5 lock-validator mint.owner pre-flight — kept here for
3099
3188
  // documentation, but the `isPostCommitErrorCode` helper below
3100
3189
  // also matches any `ESC_LOCK_*` prefix. The full lock-validator
@@ -3346,11 +3435,8 @@ function projectDelegationRowForHash(d) {
3346
3435
  const out = {
3347
3436
  delegationId: d.delegationId,
3348
3437
  scopeSummary: d.scopeSummary,
3349
- pricingModel: d.pricingModel,
3350
- settlementModel: d.settlementModel
3438
+ pricingModel: d.pricingModel
3351
3439
  };
3352
- if (d.rateAmount !== void 0) out.rateAmount = d.rateAmount;
3353
- if (d.rateUnit !== void 0) out.rateUnit = d.rateUnit;
3354
3440
  if (d.currency !== void 0) {
3355
3441
  out.currency = {
3356
3442
  asset_id: d.currency.assetId,
@@ -3461,9 +3547,15 @@ async function readOnChainLock(api, opts, delegationId) {
3461
3547
  const programId = new import_web33.PublicKey(resolved.programId);
3462
3548
  const { lockPda } = deriveLockPda(programId, delegationId);
3463
3549
  const conn = new import_web33.Connection(rpcUrl, "confirmed");
3464
- const info = await conn.getAccountInfo(lockPda);
3550
+ let info = await conn.getAccountInfo(lockPda);
3551
+ for (let attempt = 1; attempt <= LOCK_READ_NOT_VISIBLE_RETRIES && !info; attempt++) {
3552
+ await new Promise((r) => setTimeout(r, LOCK_READ_RETRY_DELAY_MS));
3553
+ info = await conn.getAccountInfo(lockPda);
3554
+ }
3465
3555
  if (!info) {
3466
- return { skipReason: `lock account ${lockPda.toBase58()} not visible on this RPC yet (create_lock may still be confirming)` };
3556
+ return {
3557
+ skipReason: `lock account ${lockPda.toBase58()} not visible on this RPC yet after ${LOCK_READ_NOT_VISIBLE_RETRIES + 1} attempts (create_lock may still be confirming / RPC lag)`
3558
+ };
3467
3559
  }
3468
3560
  const lock = decodeLockAccount(info.data);
3469
3561
  if (!lock) {
@@ -3492,10 +3584,10 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3492
3584
  const api = new ArpApiClient(opts.server);
3493
3585
  const sender = resolveSenderAgent(cmdName, opts.server, opts.fromDid);
3494
3586
  const signer = makeSigner(sender);
3495
- say(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
3496
- say(import_chalk18.default.dim(`Signer (payee): ${sender.did}`));
3587
+ say(import_chalk17.default.dim(`Server: ${api.serverUrl}`));
3588
+ say(import_chalk17.default.dim(`Signer (payee): ${sender.did}`));
3497
3589
  const relId = opts.relId ?? await resolveAutoRelId(api, sender, delegationId);
3498
- say(import_chalk18.default.dim(`Relationship: ${relId}`));
3590
+ say(import_chalk17.default.dim(`Relationship: ${relId}`));
3499
3591
  const receiptMatches = await collectReceiptRows(api, signer, relId, delegationId);
3500
3592
  const receipt = selectReceipt(receiptMatches, opts.receiptEventHash, cmdName);
3501
3593
  if (!receipt) {
@@ -3516,7 +3608,7 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3516
3608
  if (opts.json) {
3517
3609
  jsonOut({ ok: true, idempotent: true, alreadyCosigned: true, delegationId, relationshipId: relId, buyerDid: receipt.callerDid });
3518
3610
  } else {
3519
- progress(opts.json, import_chalk18.default.yellow("\n[idempotent] Receipt already cosigned \u2014 escrow release is settled. Nothing to send."));
3611
+ progress(opts.json, import_chalk17.default.yellow("\n[idempotent] Receipt already cosigned \u2014 escrow release is settled. Nothing to send."));
3520
3612
  }
3521
3613
  }
3522
3614
  return { delivered: false, idempotent: true, alreadyCosigned: true, relationshipId: relId, buyerDid: receipt.callerDid };
@@ -3539,8 +3631,8 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3539
3631
  expiresAt: ps.expires_at
3540
3632
  });
3541
3633
  } else {
3542
- progress(opts.json, import_chalk18.default.yellow("\n[idempotent] Payee settlement signature already delivered + still valid for this receipt \u2014 skipping re-send."));
3543
- progress(opts.json, import_chalk18.default.dim(` purpose=${ps.purpose} settlement_pubkey=${ps.settlement_pubkey} expires_at=${ps.expires_at}`));
3634
+ progress(opts.json, import_chalk17.default.yellow("\n[idempotent] Payee settlement signature already delivered + still valid for this receipt \u2014 skipping re-send."));
3635
+ progress(opts.json, import_chalk17.default.dim(` purpose=${ps.purpose} settlement_pubkey=${ps.settlement_pubkey} expires_at=${ps.expires_at}`));
3544
3636
  }
3545
3637
  }
3546
3638
  return {
@@ -3554,14 +3646,14 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3554
3646
  expiresAt: ps.expires_at
3555
3647
  };
3556
3648
  }
3557
- say(import_chalk18.default.yellow(`
3649
+ say(import_chalk17.default.yellow(`
3558
3650
  [refresh] Existing payee settlement sig expires at ${ps.expires_at} (\u2264 now+${SETTLEMENT_REFRESH_HEADROOM_SECS}s) \u2014 re-signing with a fresh expiry.`));
3559
3651
  }
3560
3652
  const buyerDid = receipt.callerDid;
3561
3653
  const receiptEventHash = receipt.receiptEventHash;
3562
3654
  const deliverableHash = receipt.deliverableHash ?? receipt.responseHash;
3563
- say(import_chalk18.default.dim(`Buyer (payer): ${buyerDid}`));
3564
- say(import_chalk18.default.dim(`Receipt event hash: ${receiptEventHash}`));
3655
+ say(import_chalk17.default.dim(`Buyer (payer): ${buyerDid}`));
3656
+ say(import_chalk17.default.dim(`Receipt event hash: ${receiptEventHash}`));
3565
3657
  const delegation = await findDelegationRow(api, signer, relId, delegationId);
3566
3658
  if (!delegation) {
3567
3659
  throw new Error(`${cmdName}: delegation ${delegationId} not found under relationship ${relId} (paginated 5000 rows).`);
@@ -3601,11 +3693,6 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3601
3693
  `${cmdName}: settlement expires_at (${Number.isFinite(expiresAt6) ? expiresAt6 : "NaN"}) is not far enough in the future \u2014 it must be > now+${SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS}s (${minSafeExpiry}) or the server rejects it post-commit (SETTLEMENT_SIG_EXPIRES_AT_*), burning a sender_sequence. ${fromExplicit ? "Pass a later --expires-at <unix-seconds> (still \u2264 the lock's on-chain expiry)." : `Derived from deadline ${JSON.stringify(delegation.deadline)} + buffer ${bufferSecs}s \u2014 the deadline is too close or in the past. Increase --settlement-buffer-secs or pass --expires-at <unix-seconds> \u2264 the lock's expiry.`}`
3602
3694
  );
3603
3695
  }
3604
- if (delegation.settlementModel === "prepaid") {
3605
- throw new Error(
3606
- `${cmdName}: delegation ${delegationId} settlementModel is 'prepaid' (non-escrow). This command delivers an on-chain escrow-release signature; a prepaid delegation has no lock to settle and the server would reject post-commit (SETTLEMENT_SIG_LOCK_NOT_FOUND). Nothing to settle on-chain.`
3607
- );
3608
- }
3609
3696
  const subset = projectDelegationRowForHash(delegation);
3610
3697
  const conditionHash = (0, import_utils3.bytesToHex)((0, import_sdk10.deriveDelegationConditionHash)(subset));
3611
3698
  const buyerDidDoc = await api.getDidDocument(buyerDid);
@@ -3667,9 +3754,9 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3667
3754
  });
3668
3755
  effectiveFeeBpsAtLock = resolvedFee.feeBpsAtLock;
3669
3756
  effectiveFeeRecipientAtLock = resolvedFee.feeRecipientAtLock;
3670
- say(import_chalk18.default.dim(`On-chain lock pre-flight OK \u2014 payee/amount/expiry/mint match; fee_bps_at_lock=${resolvedFee.feeBpsAtLock}.`));
3757
+ say(import_chalk17.default.dim(`On-chain lock pre-flight OK \u2014 payee/amount/expiry/mint match; fee_bps_at_lock=${resolvedFee.feeBpsAtLock}.`));
3671
3758
  } else {
3672
- say(import_chalk18.default.yellow(`On-chain lock pre-flight skipped: ${lockCheck.skipReason}. The server remains the validation boundary.`));
3759
+ say(import_chalk17.default.yellow(`On-chain lock pre-flight skipped: ${lockCheck.skipReason}. The server remains the validation boundary.`));
3673
3760
  }
3674
3761
  const signOpts = {
3675
3762
  server: opts.server,
@@ -3692,7 +3779,7 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3692
3779
  ...effectiveFeeRecipientAtLock !== void 0 ? { feeRecipientAtLock: effectiveFeeRecipientAtLock } : {},
3693
3780
  ...partialPayeeAmount !== void 0 ? { partialPayeeAmount } : {}
3694
3781
  };
3695
- say(import_chalk18.default.dim(`Signing ${partialPayeeAmount !== void 0 ? "PARTIAL" : "full"} release: lock_amount=${lockAmount}, expires_at=${expiresAt6}, cluster_tag=${clusterTag}`));
3782
+ say(import_chalk17.default.dim(`Signing ${partialPayeeAmount !== void 0 ? "PARTIAL" : "full"} release: lock_amount=${lockAmount}, expires_at=${expiresAt6}, cluster_tag=${clusterTag}`));
3696
3783
  const signed = await signSettlementHandler(signOpts);
3697
3784
  const content = {
3698
3785
  delegation_id: delegationId,
@@ -3723,16 +3810,16 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3723
3810
  serverTimestamp: result.serverTimestamp
3724
3811
  });
3725
3812
  } else {
3726
- console.log(import_chalk18.default.green("\nSettlement signature signed + delivered."));
3727
- console.log(`${import_chalk18.default.bold("Delegation")}: ${import_chalk18.default.cyan(delegationId)}`);
3728
- console.log(`${import_chalk18.default.bold("Buyer")}: ${import_chalk18.default.cyan(buyerDid)}`);
3729
- console.log(`${import_chalk18.default.bold("Purpose")}: ${import_chalk18.default.cyan(signed.purpose)}`);
3813
+ console.log(import_chalk17.default.green("\nSettlement signature signed + delivered."));
3814
+ console.log(`${import_chalk17.default.bold("Delegation")}: ${import_chalk17.default.cyan(delegationId)}`);
3815
+ console.log(`${import_chalk17.default.bold("Buyer")}: ${import_chalk17.default.cyan(buyerDid)}`);
3816
+ console.log(`${import_chalk17.default.bold("Purpose")}: ${import_chalk17.default.cyan(signed.purpose)}`);
3730
3817
  if (partialPayeeAmount !== void 0) {
3731
- console.log(`${import_chalk18.default.bold("Payee amount")}: ${import_chalk18.default.cyan(partialPayeeAmount)} (partial release)`);
3818
+ console.log(`${import_chalk17.default.bold("Payee amount")}: ${import_chalk17.default.cyan(partialPayeeAmount)} (partial release)`);
3732
3819
  }
3733
- console.log(`${import_chalk18.default.bold("Expires at")}: ${import_chalk18.default.cyan(String(expiresAt6))}`);
3734
- console.log(`${import_chalk18.default.bold("Delivered event")}: ${import_chalk18.default.cyan(result.serverEventHash)}`);
3735
- console.log(import_chalk18.default.dim("\nThe buyer cosigns with: heyarp receipt cosign <rel-id> <del-id> --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>"));
3820
+ console.log(`${import_chalk17.default.bold("Expires at")}: ${import_chalk17.default.cyan(String(expiresAt6))}`);
3821
+ console.log(`${import_chalk17.default.bold("Delivered event")}: ${import_chalk17.default.cyan(result.serverEventHash)}`);
3822
+ console.log(import_chalk17.default.dim("\nThe buyer cosigns with: heyarp receipt cosign <rel-id> <del-id> --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>"));
3736
3823
  }
3737
3824
  }
3738
3825
  return {
@@ -3751,13 +3838,13 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3751
3838
  serverTimestamp: result.serverTimestamp
3752
3839
  };
3753
3840
  }
3754
- var import_sdk10, import_utils3, import_web33, import_chalk18, NATIVE_SOL_MINT2, SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS, SETTLEMENT_REFRESH_HEADROOM_SECS;
3841
+ var import_sdk10, import_utils3, import_web33, import_chalk17, NATIVE_SOL_MINT2, SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS, SETTLEMENT_REFRESH_HEADROOM_SECS, LOCK_READ_NOT_VISIBLE_RETRIES, LOCK_READ_RETRY_DELAY_MS;
3755
3842
  var init_settlement = __esm({
3756
3843
  "src/commands/settlement.ts"() {
3757
3844
  import_sdk10 = require("@heyanon-arp/sdk");
3758
3845
  import_utils3 = require("@noble/hashes/utils");
3759
3846
  import_web33 = require("@solana/web3.js");
3760
- import_chalk18 = __toESM(require("chalk"));
3847
+ import_chalk17 = __toESM(require("chalk"));
3761
3848
  init_api();
3762
3849
  init_format();
3763
3850
  init_state();
@@ -3771,6 +3858,8 @@ var init_settlement = __esm({
3771
3858
  NATIVE_SOL_MINT2 = "11111111111111111111111111111111";
3772
3859
  SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS = 120;
3773
3860
  SETTLEMENT_REFRESH_HEADROOM_SECS = 600;
3861
+ LOCK_READ_NOT_VISIBLE_RETRIES = 3;
3862
+ LOCK_READ_RETRY_DELAY_MS = 2e3;
3774
3863
  }
3775
3864
  });
3776
3865
 
@@ -3798,10 +3887,10 @@ function registerPropose(parent) {
3798
3887
  false
3799
3888
  ).option(
3800
3889
  "--cluster-tag <int>",
3801
- "Solana cluster the escrow lock lives on: 0 = devnet, 1 = mainnet-beta. MUST match the create_lock cluster or the server-reconstructed release digest will not verify. REQUIRED for an escrow delegation \u2014 there is no default (a defaulted/wrong cluster silently signs an invalid settlement digest that fails at cosign). Ignored for prepaid."
3890
+ "Solana cluster the escrow lock lives on: 0 = devnet, 1 = mainnet-beta. MUST match the create_lock cluster or the server-reconstructed release digest will not verify. REQUIRED (every delegation is escrow-backed) \u2014 there is no default (a defaulted/wrong cluster silently signs an invalid settlement digest that fails at cosign)."
3802
3891
  ).option(
3803
3892
  "--settlement-buffer-secs <int>",
3804
- "Seconds added to the delegation deadline for the settlement digest expires_at (dispute buffer). Default 86400 (1 day). Ignored when --expires-at is given, and for prepaid delegations.",
3893
+ "Seconds added to the delegation deadline for the settlement digest expires_at (dispute buffer). Default 86400 (1 day). Ignored when --expires-at is given.",
3805
3894
  "86400"
3806
3895
  ).option(
3807
3896
  "--expires-at <unix>",
@@ -3826,6 +3915,14 @@ function registerPropose(parent) {
3826
3915
  await runPropose(recipientDid, delegationId, requestHash, responseHash, opts);
3827
3916
  } catch (err) {
3828
3917
  emitActionError(err, cmd);
3918
+ if (!opts.json && err instanceof ApiError && err.payload.code === "RECEIPT_ALREADY_EXISTS") {
3919
+ console.error(
3920
+ import_chalk18.default.dim(
3921
+ ` This receipt already exists (a prior propose committed it). Do NOT re-propose \u2014 to (re)deliver the payee settlement signature, run:
3922
+ 'heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --auto --cluster-tag ${opts.clusterTag ?? "<0|1>"}'`
3923
+ )
3924
+ );
3925
+ }
3829
3926
  process.exitCode = 1;
3830
3927
  }
3831
3928
  });
@@ -3850,15 +3947,13 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
3850
3947
  opts.relId = await resolveAutoRelId(api, sender, delegationId);
3851
3948
  progress(
3852
3949
  opts.json,
3853
- import_chalk19.default.dim(`[auto-rel-id] resolved --rel-id=${opts.relId} (delegation found in exactly one of your relationships; pass --rel-id explicitly to override)`)
3950
+ import_chalk18.default.dim(`[auto-rel-id] resolved --rel-id=${opts.relId} (delegation found in exactly one of your relationships; pass --rel-id explicitly to override)`)
3854
3951
  );
3855
3952
  }
3856
- let delegationRow;
3857
3953
  if (opts.relId) {
3858
- delegationRow = await assertSenderIsReceiptPayee(api, sender, opts.relId, delegationId);
3954
+ await assertSenderIsReceiptPayee(api, sender, opts.relId, delegationId);
3859
3955
  }
3860
- const isEscrow = delegationRow?.settlementModel === "escrow";
3861
- if (isEscrow) {
3956
+ {
3862
3957
  const ct = opts.clusterTag;
3863
3958
  if (ct === void 0 || ct === "") {
3864
3959
  throw new Error(
@@ -3879,7 +3974,7 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
3879
3974
  opts.requestId = await resolveAutoRequestId(api, sender, opts.relId, delegationId);
3880
3975
  progress(
3881
3976
  opts.json,
3882
- import_chalk19.default.dim(
3977
+ import_chalk18.default.dim(
3883
3978
  `[auto-request-id] resolved --request-id=${opts.requestId} (unique 'responded' work_log under this delegation; pass --request-id explicitly to override)`
3884
3979
  )
3885
3980
  );
@@ -3898,8 +3993,8 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
3898
3993
  }
3899
3994
  requestHash = computed.requestHash;
3900
3995
  responseHash = computed.responseHash;
3901
- progress(opts.json, import_chalk19.default.dim(`[auto-hashes] request_hash: ${requestHash} (from work-log ${opts.relId}/${delegationId}/${opts.requestId})`));
3902
- progress(opts.json, import_chalk19.default.dim(`[auto-hashes] response_hash: ${responseHash}`));
3996
+ progress(opts.json, import_chalk18.default.dim(`[auto-hashes] request_hash: ${requestHash} (from work-log ${opts.relId}/${delegationId}/${opts.requestId})`));
3997
+ progress(opts.json, import_chalk18.default.dim(`[auto-hashes] response_hash: ${responseHash}`));
3903
3998
  } else {
3904
3999
  if (requestHashArg === void 0 || responseHashArg === void 0) {
3905
4000
  throw new Error("receipt propose: <request-hash> and <response-hash> are required (or pass --auto-hashes + --rel-id + --request-id to derive them from the work-log)");
@@ -3919,15 +4014,15 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
3919
4014
  if (opts.deliverableHash) content.deliverable_hash = opts.deliverableHash;
3920
4015
  if (usage) content.usage = usage;
3921
4016
  const body = { type: "receipt", content };
3922
- progress(opts.json, import_chalk19.default.dim(`Server: ${api.serverUrl}`));
3923
- progress(opts.json, import_chalk19.default.dim(`Sender (payee): ${sender.did}`));
3924
- progress(opts.json, import_chalk19.default.dim(`Recipient (caller): ${recipientDid}`));
3925
- progress(opts.json, import_chalk19.default.dim(`Delegation: ${delegationId}`));
3926
- progress(opts.json, import_chalk19.default.dim(`Verdict (proposed): ${verdict}`));
4017
+ progress(opts.json, import_chalk18.default.dim(`Server: ${api.serverUrl}`));
4018
+ progress(opts.json, import_chalk18.default.dim(`Sender (payee): ${sender.did}`));
4019
+ progress(opts.json, import_chalk18.default.dim(`Recipient (caller): ${recipientDid}`));
4020
+ progress(opts.json, import_chalk18.default.dim(`Delegation: ${delegationId}`));
4021
+ progress(opts.json, import_chalk18.default.dim(`Verdict (proposed): ${verdict}`));
3927
4022
  const result = await sendReceiptEnvelope({ api, sender, recipientDid, body, attachments: void 0, ttlSeconds, verbose: opts.verbose, server: opts.server });
3928
4023
  let settlementResult;
3929
4024
  let settlementError;
3930
- if (isEscrow && opts.relId) {
4025
+ if (opts.relId) {
3931
4026
  const { autoSignAndDeliverPayeeSig: autoSignAndDeliverPayeeSig2 } = await Promise.resolve().then(() => (init_settlement(), settlement_exports));
3932
4027
  try {
3933
4028
  settlementResult = await autoSignAndDeliverPayeeSig2({
@@ -3975,7 +4070,12 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
3975
4070
  json.settlement = {
3976
4071
  delivered: false,
3977
4072
  error: settlementError.message,
3978
- recovery: `heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --receipt-event-hash ${result.serverEventHash} --sig-from-file <path> --expires-at <unix> (after signing with 'heyarp wallet sign-settlement-release')`
4073
+ // Authoritative recovery (SR-2/SR-3): re-sign + re-deliver via
4074
+ // --auto, which resolves condition_hash + the on-chain lock
4075
+ // snapshot itself — no hand-typed hashes, no sig file, no
4076
+ // lock_id/condition_hash trap. Do NOT re-run `receipt propose`
4077
+ // (the receipt already exists; a second propose is a duplicate).
4078
+ recovery: `heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --auto --cluster-tag ${opts.clusterTag ?? "<0|1>"} (do NOT re-run 'receipt propose' \u2014 the receipt already exists, a second propose is rejected as a duplicate)`
3979
4079
  };
3980
4080
  } else if (settlementResult) {
3981
4081
  json.settlement = {
@@ -3995,32 +4095,31 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
3995
4095
  return;
3996
4096
  }
3997
4097
  printIngestResult2(result);
3998
- console.log(import_chalk19.default.dim(`
3999
- Receipt event hash: ${import_chalk19.default.cyan(result.serverEventHash)}`));
4098
+ console.log(import_chalk18.default.dim(`
4099
+ Receipt event hash: ${import_chalk18.default.cyan(result.serverEventHash)}`));
4000
4100
  if (settlementError) {
4001
- console.error(import_chalk19.default.yellow("\nWARNING: receipt proposed, but the payee settlement signature was NOT delivered."));
4002
- console.error(import_chalk19.default.yellow(` reason: ${settlementError.message}`));
4003
- console.error(import_chalk19.default.dim(" The receipt stays committed \u2014 re-running `receipt propose` will NOT help (it hits RECEIPT_ALREADY_EXISTS)."));
4004
- console.error(import_chalk19.default.dim(" Recover by signing + delivering the payee sig directly, once the on-chain lock / RPC is reachable:"));
4005
- console.error(
4006
- import_chalk19.default.dim(
4007
- ` 'heyarp wallet sign-settlement-release ... --write-to <path>' then
4008
- 'heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --receipt-event-hash ${result.serverEventHash} --sig-from-file <path> --expires-at <unix>'`
4101
+ console.error(import_chalk18.default.yellow("\nWARNING: receipt proposed, but the payee settlement signature was NOT delivered."));
4102
+ console.error(import_chalk18.default.yellow(` reason: ${settlementError.message}`));
4103
+ console.error(import_chalk18.default.dim(" The receipt stays committed \u2014 do NOT re-run `receipt propose` (the receipt already exists; a second propose is rejected as a duplicate)."));
4104
+ console.error(import_chalk18.default.dim(" Recover authoritatively \u2014 this resolves condition_hash + the on-chain lock snapshot for you (no manual hashes, no sig file):"));
4105
+ console.error(import_chalk18.default.dim(` 'heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --auto --cluster-tag ${opts.clusterTag ?? "<0|1>"}'`));
4106
+ } else if (settlementResult?.delivered) {
4107
+ console.log(import_chalk18.default.green("\nPayee settlement signature signed + delivered (escrow)."));
4108
+ console.log(
4109
+ import_chalk18.default.dim(
4110
+ ` purpose=${settlementResult.purpose} expires_at=${settlementResult.expiresAt}${settlementResult.payeeAmount !== void 0 ? ` payee_amount=${settlementResult.payeeAmount} (partial)` : ""}`
4009
4111
  )
4010
4112
  );
4011
- } else if (settlementResult?.delivered) {
4012
- console.log(import_chalk19.default.green("\nPayee settlement signature signed + delivered (escrow)."));
4013
- console.log(import_chalk19.default.dim(` purpose=${settlementResult.purpose} expires_at=${settlementResult.expiresAt}${settlementResult.payeeAmount !== void 0 ? ` payee_amount=${settlementResult.payeeAmount} (partial)` : ""}`));
4014
4113
  } else if (settlementResult?.idempotent) {
4015
- console.log(import_chalk19.default.yellow(`
4016
- [idempotent] Payee settlement signature already ${settlementResult.alreadyCosigned ? "settled (receipt cosigned)" : "delivered"} \u2014 nothing to re-send.`));
4017
- }
4018
- console.log(import_chalk19.default.dim(`The caller cosigns with:`));
4019
- if (isEscrow) {
4020
- console.log(import_chalk19.default.dim(` heyarp receipt cosign ${result.relationshipId} ${delegationId} --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>`));
4021
- } else {
4022
- console.log(import_chalk19.default.dim(` heyarp receipt cosign ${result.relationshipId} ${delegationId} ${requestHash} ${responseHash} --verdict ${verdict}`));
4114
+ console.log(
4115
+ import_chalk18.default.yellow(
4116
+ `
4117
+ [idempotent] Payee settlement signature already ${settlementResult.alreadyCosigned ? "settled (receipt cosigned)" : "delivered"} \u2014 nothing to re-send.`
4118
+ )
4119
+ );
4023
4120
  }
4121
+ console.log(import_chalk18.default.dim(`The caller cosigns with:`));
4122
+ console.log(import_chalk18.default.dim(` heyarp receipt cosign ${result.relationshipId} ${delegationId} --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>`));
4024
4123
  }
4025
4124
  async function assertSenderIsReceiptPayee(api, sender, relationshipId, delegationId) {
4026
4125
  const signer = makeSigner(sender);
@@ -4168,7 +4267,7 @@ function registerCosign(parent) {
4168
4267
  function loadSettlementSigFromFile(path, flagPrefix) {
4169
4268
  let raw;
4170
4269
  try {
4171
- raw = (0, import_node_fs8.readFileSync)(path, "utf8");
4270
+ raw = (0, import_node_fs7.readFileSync)(path, "utf8");
4172
4271
  } catch (err) {
4173
4272
  throw new Error(`receipt cosign: failed to read ${flagPrefix} '${path}': ${err.message}`);
4174
4273
  }
@@ -4415,28 +4514,28 @@ async function runCosign(relationshipId, delegationId, requestHashArg, responseH
4415
4514
  content.notes_hash = cosignNotesHash;
4416
4515
  }
4417
4516
  const body = { type: "receipt", content };
4418
- console.log(import_chalk19.default.dim(`Server: ${api.serverUrl}`));
4419
- console.log(import_chalk19.default.dim(`Sender (caller): ${sender.did}`));
4420
- console.log(import_chalk19.default.dim(`Recipient (payee): ${resolved.payeeDid}`));
4421
- console.log(import_chalk19.default.dim(`Delegation: ${delegationId}`));
4422
- console.log(import_chalk19.default.dim(`Verdict (final): ${verdict}`));
4423
- console.log(import_chalk19.default.dim(`Receipt event hash bound: ${resolved.receiptEventHash}`));
4517
+ console.log(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
4518
+ console.log(import_chalk18.default.dim(`Sender (caller): ${sender.did}`));
4519
+ console.log(import_chalk18.default.dim(`Recipient (payee): ${resolved.payeeDid}`));
4520
+ console.log(import_chalk18.default.dim(`Delegation: ${delegationId}`));
4521
+ console.log(import_chalk18.default.dim(`Verdict (final): ${verdict}`));
4522
+ console.log(import_chalk18.default.dim(`Receipt event hash bound: ${resolved.receiptEventHash}`));
4424
4523
  if (cosignNotesHash !== null) {
4425
- console.log(import_chalk19.default.dim(`Notes hash bound: ${cosignNotesHash}`));
4524
+ console.log(import_chalk18.default.dim(`Notes hash bound: ${cosignNotesHash}`));
4426
4525
  } else if (opts.clearNotes) {
4427
- console.log(import_chalk19.default.dim("Notes binding: cleared (--clear-notes)"));
4526
+ console.log(import_chalk18.default.dim("Notes binding: cleared (--clear-notes)"));
4428
4527
  }
4429
4528
  if (opts.autoResolvePayeeSig) {
4430
4529
  applyAutoResolvePayeeSig("receipt cosign", opts, resolved.payeeSettlement);
4431
4530
  console.log(
4432
- import_chalk19.default.dim(`Auto-resolved payee sig from receipt.payeeSettlement (purpose=${resolved.payeeSettlement?.purpose}, expires_at=${resolved.payeeSettlement?.expires_at})`)
4531
+ import_chalk18.default.dim(`Auto-resolved payee sig from receipt.payeeSettlement (purpose=${resolved.payeeSettlement?.purpose}, expires_at=${resolved.payeeSettlement?.expires_at})`)
4433
4532
  );
4434
4533
  }
4435
4534
  const settlementSigs = assembleSettlementSignaturesAttachment(opts);
4436
4535
  const attachments = { co_signature: cosignature };
4437
4536
  if (settlementSigs) {
4438
4537
  attachments.settlement_signatures = settlementSigs;
4439
- console.log(import_chalk19.default.dim(`Settlement signatures attached: purpose=${settlementSigs.purpose}`));
4538
+ console.log(import_chalk18.default.dim(`Settlement signatures attached: purpose=${settlementSigs.purpose}`));
4440
4539
  }
4441
4540
  const result = await sendReceiptEnvelope({
4442
4541
  api,
@@ -4468,18 +4567,32 @@ async function runCosign(relationshipId, delegationId, requestHashArg, responseH
4468
4567
  }
4469
4568
  }
4470
4569
  function registerSendPayeeSig(parent) {
4471
- parent.command("send-payee-sig").description("Send the payee's settlement signature to the buyer via a settlement_signature envelope. Replaces /tmp/*.json coordination for cross-host escrow cycles.").argument("<recipient-did>", "Buyer DID (= caller / offerer of the parent delegation; the cycle sends the sig to the side that will cosign)").requiredOption("--delegation-id <id>", "Parent delegation UUID \u2014 must match what was passed to `heyarp wallet sign-settlement-release`.").requiredOption(
4570
+ parent.command("send-payee-sig").description(
4571
+ "Send the payee's settlement signature to the buyer via a settlement_signature envelope. Replaces /tmp/*.json coordination for cross-host escrow cycles. Two modes: (1) MANUAL \u2014 supply --receipt-event-hash + --sig-from-file + --expires-at from a prior `wallet sign-settlement-release`; (2) --auto (RECOMMENDED for recovery) \u2014 resolves condition_hash + the on-chain lock snapshot AUTHORITATIVELY and re-signs (force), so you pass NO --sig-from-file and NO hand-typed hashes. Use --auto to recover when `receipt propose` did not deliver the sig (e.g. the just-confirmed lock was not yet RPC-visible); do NOT re-run `receipt propose` (the receipt already exists \u2014 a second propose is rejected as a duplicate)."
4572
+ ).argument(
4573
+ "[recipient-did]",
4574
+ "Buyer DID (= caller / offerer of the parent delegation; the cycle sends the sig to the side that will cosign). Optional under --auto \u2014 the buyer is resolved from the delegation/receipt."
4575
+ ).requiredOption("--delegation-id <id>", "Parent delegation UUID \u2014 must match what was passed to `heyarp wallet sign-settlement-release`.").option(
4576
+ "--auto",
4577
+ "Authoritative recovery: resolve condition_hash (over the delegation terms) + the on-chain lock snapshot (mint / amount / expiry / fee) and re-sign the release digest (force), then deliver it. Use this to recover when `receipt propose` did not deliver the sig \u2014 you do NOT pass --sig-from-file or --receipt-event-hash with --auto, and there is no lock_id/condition_hash trap. Requires --cluster-tag; mutually exclusive with --sig-from-file. Do NOT re-run `receipt propose` (the receipt already exists; a second propose is rejected as a duplicate)."
4578
+ ).option(
4579
+ "--cluster-tag <0|1>",
4580
+ "REQUIRED under --auto: 0 = devnet, 1 = mainnet-beta. Binds the cluster into the signed release digest (no safe default; a wrong cluster fails at the buyer cosign)."
4581
+ ).option(
4582
+ "--rpc-url <url>",
4583
+ "Solana RPC endpoint for the --auto on-chain lock read (resolves mint / amount / expiry / fee). Falls back to ARP_ESCROW_RPC_URL then `heyarp config get rpcUrl`; when none is set the on-chain check is skipped and the server stays the boundary."
4584
+ ).option("--program-id <pubkey>", "Escrow program id for the --auto on-chain lock read (auto-discovered from the server when omitted).").option("--rel-id <id>", "Relationship UUID \u2014 under --auto, auto-derived from the delegation when omitted.").option(
4472
4585
  "--receipt-event-hash <sha256:hex>",
4473
- "Server-assigned `serverEventHash` of the receipt-propose envelope this signature settles. Read it from the JSON response of `heyarp receipt propose` (the field is `Server event hash` in human output, or `.serverEventHash` in --json). MUST equal the value the digest was signed over \u2014 the server cross-checks against `Receipt.receiptEventHash` and rejects with SETTLEMENT_SIG_RECEIPT_NOT_FOUND otherwise."
4474
- ).requiredOption(
4586
+ "MANUAL path: server-assigned `serverEventHash` of the receipt-propose envelope this signature settles. Read it from the JSON response of `heyarp receipt propose` (the field is `Server event hash` in human output, or `.serverEventHash` in --json). MUST equal the value the digest was signed over \u2014 the server cross-checks against `Receipt.receiptEventHash` and rejects with SETTLEMENT_SIG_RECEIPT_NOT_FOUND otherwise. NOT needed with --auto (the helper resolves the receipt; pass it only to disambiguate >1 proposed receipt)."
4587
+ ).option(
4475
4588
  "--sig-from-file <path>",
4476
- "JSON file produced by `heyarp wallet sign-settlement-release --write-to <path>`. Reads `{settlement_pubkey, sig, purpose, digest_hex}` \u2014 the digest_hex is bound through for cross-file consistency (catches truncated or hand-edited files)."
4477
- ).requiredOption(
4589
+ "MANUAL path: JSON file produced by `heyarp wallet sign-settlement-release --write-to <path>`. Reads `{settlement_pubkey, sig, purpose, digest_hex}` \u2014 the digest_hex is bound through for cross-file consistency (catches truncated or hand-edited files). MUTUALLY EXCLUSIVE with --auto."
4590
+ ).option(
4478
4591
  "--expires-at <unix>",
4479
- "Unix-seconds expires_at value baked into the signed digest \u2014 MUST match the value passed to `--expires-at` on `heyarp wallet sign-settlement-release`. Server cross-checks against the buyer-side value at cosign time; wrong value \u2192 ESC_SETTLEMENT_EXPIRES_AT_PAST/_TOO_SOON."
4592
+ "MANUAL path: unix-seconds expires_at value baked into the signed digest \u2014 MUST match the value passed to `--expires-at` on `heyarp wallet sign-settlement-release`. Server cross-checks against the buyer-side value at cosign time; wrong value \u2192 ESC_SETTLEMENT_EXPIRES_AT_PAST/_TOO_SOON. Under --auto this is optional (derived from the delegation deadline; pass it only to override)."
4480
4593
  ).option(
4481
4594
  "--payee-amount <int>",
4482
- "Base-unit decimal-integer payee_amount \u2014 REQUIRED when the sig file's purpose is `ARP-SOLANA-PARTIAL-RELEASE-v1.5` (usage_based delegations). Same value passed to `--partial-payee-amount` on sign-settlement-release. FORBIDDEN for `ARP-SOLANA-RELEASE-v1.5` (full release settles the whole lock)."
4595
+ "Base-unit decimal-integer payee_amount \u2014 REQUIRED on the MANUAL path when the sig file's purpose is `ARP-SOLANA-PARTIAL-RELEASE-v1.5` (usage_based delegations). Same value passed to `--partial-payee-amount` on sign-settlement-release. FORBIDDEN for `ARP-SOLANA-RELEASE-v1.5` (full release settles the whole lock). Under --auto the partial amount is bound from the receipt; pass it only as a confirmation that must match."
4483
4596
  ).option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server (= payee)").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).action(async (recipientDid, opts, cmd) => {
4484
4597
  try {
4485
4598
  await runSendPayeeSig(recipientDid, opts);
@@ -4491,20 +4604,85 @@ function registerSendPayeeSig(parent) {
4491
4604
  }
4492
4605
  async function runSendPayeeSig(recipientDid, opts) {
4493
4606
  const cmdName = "receipt send-payee-sig";
4607
+ if (opts.auto) {
4608
+ if (opts.sigFromFile !== void 0 && opts.sigFromFile !== "") {
4609
+ throw new Error(
4610
+ `${cmdName}: --auto and --sig-from-file are mutually exclusive. --auto re-signs the release digest AUTHORITATIVELY (it resolves condition_hash + the on-chain lock itself); a --sig-from-file is the MANUAL path's pre-signed input. Pick one: --auto for recovery, or the manual --sig-from-file + --receipt-event-hash + --expires-at trio.`
4611
+ );
4612
+ }
4613
+ if (opts.clusterTag === void 0 || opts.clusterTag === "") {
4614
+ throw new Error(
4615
+ `${cmdName}: --cluster-tag is required with --auto (0 = devnet, 1 = mainnet-beta). It binds the cluster into the signed release digest; there is no safe default \u2014 pass the cluster where the create_lock lives.`
4616
+ );
4617
+ }
4618
+ if (opts.clusterTag !== "0" && opts.clusterTag !== "1") {
4619
+ throw new Error(`${cmdName}: --cluster-tag must be '0' (devnet) or '1' (mainnet-beta), got '${opts.clusterTag}'`);
4620
+ }
4621
+ const delegationIdAuto = requireUuidNormalised2(cmdName, opts.delegationId, "--delegation-id");
4622
+ if (recipientDid !== void 0 && recipientDid !== "") {
4623
+ requireDid2(cmdName, recipientDid, "[recipient-did]");
4624
+ }
4625
+ const { autoSignAndDeliverPayeeSig: autoSignAndDeliverPayeeSig2 } = await Promise.resolve().then(() => (init_settlement(), settlement_exports));
4626
+ await autoSignAndDeliverPayeeSig2(
4627
+ {
4628
+ server: opts.server,
4629
+ fromDid: opts.fromDid,
4630
+ delegationId: delegationIdAuto,
4631
+ ...opts.relId !== void 0 ? { relId: opts.relId } : {},
4632
+ clusterTag: opts.clusterTag,
4633
+ ...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
4634
+ ...opts.rpcUrl !== void 0 ? { rpcUrl: opts.rpcUrl } : {},
4635
+ ...opts.programId !== void 0 ? { programId: opts.programId } : {},
4636
+ ...opts.payeeAmount !== void 0 ? { partialPayeeAmount: opts.payeeAmount } : {},
4637
+ ...opts.receiptEventHash !== void 0 ? { receiptEventHash: opts.receiptEventHash } : {},
4638
+ // Recovery semantics: re-sign + re-deliver even if a (bad) sig
4639
+ // already exists. The helper short-circuits a cosigned receipt.
4640
+ force: true
4641
+ // `send-payee-sig` has no --json flag (the manual path prints a
4642
+ // plain human block); leave the helper in its human-output mode.
4643
+ // Do NOT pass silent — the helper owns + prints the recovery output.
4644
+ },
4645
+ cmdName
4646
+ );
4647
+ return;
4648
+ }
4649
+ if (recipientDid === void 0 || recipientDid === "") {
4650
+ throw new Error(
4651
+ `${cmdName}: <recipient-did> (the buyer DID) is required on the manual path. Pass it, or use --auto --cluster-tag <0|1> to resolve the buyer + re-sign authoritatively.`
4652
+ );
4653
+ }
4494
4654
  requireDid2(cmdName, recipientDid, "<recipient-did>");
4655
+ const recipient = recipientDid;
4495
4656
  const delegationId = requireUuidNormalised2(cmdName, opts.delegationId, "--delegation-id");
4657
+ if (opts.sigFromFile === void 0 || opts.sigFromFile === "") {
4658
+ throw new Error(
4659
+ `${cmdName}: --sig-from-file is required (the MANUAL path's pre-signed input). For authoritative recovery without a sig file, use --auto --cluster-tag <0|1> instead.`
4660
+ );
4661
+ }
4662
+ if (opts.receiptEventHash === void 0 || opts.receiptEventHash === "") {
4663
+ throw new Error(
4664
+ `${cmdName}: --receipt-event-hash is required on the manual path. For authoritative recovery without hand-typed hashes, use --auto --cluster-tag <0|1> instead.`
4665
+ );
4666
+ }
4667
+ if (opts.expiresAt === void 0 || opts.expiresAt === "") {
4668
+ throw new Error(
4669
+ `${cmdName}: --expires-at is required on the manual path (must match the value signed). For authoritative recovery, use --auto --cluster-tag <0|1> instead.`
4670
+ );
4671
+ }
4496
4672
  requireSha256(cmdName, opts.receiptEventHash, "--receipt-event-hash");
4673
+ const receiptEventHash = opts.receiptEventHash;
4674
+ const sigFromFile = opts.sigFromFile;
4497
4675
  const expiresAtSeconds = parseInteger(cmdName, "--expires-at", opts.expiresAt);
4498
4676
  if (expiresAtSeconds <= 0) {
4499
4677
  throw new Error(`${cmdName}: --expires-at must be a positive unix-seconds integer (got ${opts.expiresAt})`);
4500
4678
  }
4501
4679
  const ttlSeconds = parseTtl2(cmdName, opts.ttl);
4502
- const sigFile = loadSettlementSigFromFile(opts.sigFromFile, "--sig-from-file");
4680
+ const sigFile = loadSettlementSigFromFile(sigFromFile, "--sig-from-file");
4503
4681
  const isPartial = sigFile.purpose === "ARP-SOLANA-PARTIAL-RELEASE-v1.5";
4504
4682
  const isFull = sigFile.purpose === "ARP-SOLANA-RELEASE-v1.5";
4505
4683
  if (!isPartial && !isFull) {
4506
4684
  throw new Error(
4507
- `${cmdName}: sig file at '${opts.sigFromFile}' has purpose='${sigFile.purpose}' but only ARP-SOLANA-RELEASE-v1.5 and ARP-SOLANA-PARTIAL-RELEASE-v1.5 are valid on a settlement_signature envelope. (REFUND-v1 sigs ride other paths \u2014 they don't bind to receipt_event_hash, so this envelope isn't the right channel for them.)`
4685
+ `${cmdName}: sig file at '${sigFromFile}' has purpose='${sigFile.purpose}' but only ARP-SOLANA-RELEASE-v1.5 and ARP-SOLANA-PARTIAL-RELEASE-v1.5 are valid on a settlement_signature envelope. (REFUND-v1 sigs ride other paths \u2014 they don't bind to receipt_event_hash, so this envelope isn't the right channel for them.)`
4508
4686
  );
4509
4687
  }
4510
4688
  if (isPartial && (opts.payeeAmount === void 0 || opts.payeeAmount === "")) {
@@ -4526,28 +4704,28 @@ async function runSendPayeeSig(recipientDid, opts) {
4526
4704
  const api = new ArpApiClient(opts.server);
4527
4705
  const content = {
4528
4706
  delegation_id: delegationId,
4529
- receipt_event_hash: opts.receiptEventHash,
4707
+ receipt_event_hash: receiptEventHash,
4530
4708
  purpose: sigFile.purpose,
4531
4709
  payee_settlement_pubkey: sigFile.settlement_pubkey,
4532
4710
  sig: sigFile.sig,
4533
4711
  expires_at: expiresAtSeconds,
4534
4712
  ...isPartial ? { payee_amount: opts.payeeAmount } : {}
4535
4713
  };
4536
- console.log(import_chalk19.default.dim(`Server: ${api.serverUrl}`));
4537
- console.log(import_chalk19.default.dim(`Sender (payee): ${sender.did}`));
4538
- console.log(import_chalk19.default.dim(`Recipient (buyer): ${recipientDid}`));
4539
- console.log(import_chalk19.default.dim(`Delegation: ${delegationId}`));
4540
- console.log(import_chalk19.default.dim(`Receipt event hash: ${opts.receiptEventHash}`));
4541
- console.log(import_chalk19.default.dim(`Purpose: ${sigFile.purpose}`));
4714
+ console.log(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
4715
+ console.log(import_chalk18.default.dim(`Sender (payee): ${sender.did}`));
4716
+ console.log(import_chalk18.default.dim(`Recipient (buyer): ${recipient}`));
4717
+ console.log(import_chalk18.default.dim(`Delegation: ${delegationId}`));
4718
+ console.log(import_chalk18.default.dim(`Receipt event hash: ${receiptEventHash}`));
4719
+ console.log(import_chalk18.default.dim(`Purpose: ${sigFile.purpose}`));
4542
4720
  if (isPartial) {
4543
- console.log(import_chalk19.default.dim(`Payee amount: ${opts.payeeAmount}`));
4721
+ console.log(import_chalk18.default.dim(`Payee amount: ${opts.payeeAmount}`));
4544
4722
  }
4545
- console.log(import_chalk19.default.dim(`Settlement pubkey: ${sigFile.settlement_pubkey}`));
4546
- console.log(import_chalk19.default.dim(`Expires at: ${expiresAtSeconds}`));
4723
+ console.log(import_chalk18.default.dim(`Settlement pubkey: ${sigFile.settlement_pubkey}`));
4724
+ console.log(import_chalk18.default.dim(`Expires at: ${expiresAtSeconds}`));
4547
4725
  const result = await sendSettlementSignatureEnvelope({
4548
4726
  api,
4549
4727
  sender,
4550
- recipientDid,
4728
+ recipientDid: recipient,
4551
4729
  content,
4552
4730
  ttlSeconds,
4553
4731
  verbose: opts.verbose,
@@ -4577,7 +4755,7 @@ async function sendSettlementSignatureEnvelope(args) {
4577
4755
  identitySecretKey: signer.identitySecretKey
4578
4756
  });
4579
4757
  if (args.verbose) {
4580
- console.log(import_chalk19.default.bold("\nEnvelope (pre-send):"));
4758
+ console.log(import_chalk18.default.bold("\nEnvelope (pre-send):"));
4581
4759
  console.log(formatJson(envelope));
4582
4760
  }
4583
4761
  try {
@@ -4614,7 +4792,7 @@ async function sendReceiptEnvelope(args) {
4614
4792
  attachments: args.attachments
4615
4793
  });
4616
4794
  if (args.verbose) {
4617
- console.log(import_chalk19.default.bold("\nEnvelope (pre-send):"));
4795
+ console.log(import_chalk18.default.bold("\nEnvelope (pre-send):"));
4618
4796
  console.log(formatJson(envelope));
4619
4797
  }
4620
4798
  try {
@@ -4673,12 +4851,12 @@ async function resolveCosignTargets(cmdName, api, signer, args) {
4673
4851
  };
4674
4852
  }
4675
4853
  function printIngestResult2(result) {
4676
- console.log(import_chalk19.default.green("\nDelivered."));
4677
- console.log(`${import_chalk19.default.bold("Event id")}: ${import_chalk19.default.cyan(result.eventId)}`);
4678
- console.log(`${import_chalk19.default.bold("Relationship id")}: ${import_chalk19.default.cyan(result.relationshipId)}`);
4679
- console.log(`${import_chalk19.default.bold("Chain index")}: ${import_chalk19.default.cyan(String(result.relationshipEventIndex))}`);
4680
- console.log(`${import_chalk19.default.bold("Server timestamp")}: ${import_chalk19.default.cyan(result.serverTimestamp)}`);
4681
- console.log(`${import_chalk19.default.bold("Server event hash")}: ${import_chalk19.default.cyan(result.serverEventHash)}`);
4854
+ console.log(import_chalk18.default.green("\nDelivered."));
4855
+ console.log(`${import_chalk18.default.bold("Event id")}: ${import_chalk18.default.cyan(result.eventId)}`);
4856
+ console.log(`${import_chalk18.default.bold("Relationship id")}: ${import_chalk18.default.cyan(result.relationshipId)}`);
4857
+ console.log(`${import_chalk18.default.bold("Chain index")}: ${import_chalk18.default.cyan(String(result.relationshipEventIndex))}`);
4858
+ console.log(`${import_chalk18.default.bold("Server timestamp")}: ${import_chalk18.default.cyan(result.serverTimestamp)}`);
4859
+ console.log(`${import_chalk18.default.bold("Server event hash")}: ${import_chalk18.default.cyan(result.serverEventHash)}`);
4682
4860
  }
4683
4861
  function parseVerdict(cmdName, raw) {
4684
4862
  if (raw === void 0 || raw === "") return "accepted";
@@ -4729,12 +4907,12 @@ function requireDid2(cmdName, did, label) {
4729
4907
  throw new Error(`${cmdName}: ${label} must look like 'did:arp:...' (got '${did}')`);
4730
4908
  }
4731
4909
  }
4732
- var import_node_fs8, import_sdk11, import_chalk19, POST_COMMIT_ERROR_CODES2, VERDICT_VALUES, SHA256_RE;
4910
+ var import_node_fs7, import_sdk11, import_chalk18, POST_COMMIT_ERROR_CODES2, VERDICT_VALUES, SHA256_RE;
4733
4911
  var init_receipt = __esm({
4734
4912
  "src/commands/receipt.ts"() {
4735
- import_node_fs8 = require("fs");
4913
+ import_node_fs7 = require("fs");
4736
4914
  import_sdk11 = require("@heyanon-arp/sdk");
4737
- import_chalk19 = __toESM(require("chalk"));
4915
+ import_chalk18 = __toESM(require("chalk"));
4738
4916
  init_api();
4739
4917
  init_format();
4740
4918
  init_id_format();
@@ -4753,6 +4931,15 @@ var init_receipt = __esm({
4753
4931
  "RECEIPT_COSIGN_AGENT_MISMATCH",
4754
4932
  "RECEIPT_COSIGN_PURPOSE_INVALID",
4755
4933
  "RECEIPT_COSIGN_INVALID",
4934
+ // M6 lock-at-accept: the receipt-propose handler's LOCKED gate emits
4935
+ // `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
4936
+ // the on-chain lock isn't confirmed yet (state
4937
+ // `pending_lock_finalization`). It fires from the body handler AFTER
4938
+ // the event row is committed — same lifecycle as
4939
+ // `RECEIPT_DELEGATION_NOT_ACTIVE` — so the CLI must advance
4940
+ // `lastSenderSequence`, otherwise a retry once the lock confirms
4941
+ // reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
4942
+ "DELEGATION_PENDING_LOCK",
4756
4943
  // response_hash / request_hash / deliverable_hash content
4757
4944
  // verification. Server commits the receipt envelope row BEFORE
4758
4945
  // running the canonical-hash lookup, so a rejection here still
@@ -4842,7 +5029,7 @@ var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
4842
5029
  // package.json
4843
5030
  var package_default = {
4844
5031
  name: "@heyanon-arp/cli",
4845
- version: "0.0.7",
5032
+ version: "0.0.9",
4846
5033
  description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
4847
5034
  license: "MIT",
4848
5035
  keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
@@ -4852,7 +5039,7 @@ var package_default = {
4852
5039
  publishConfig: {
4853
5040
  access: "public"
4854
5041
  },
4855
- files: ["dist", "examples", "scripts/postinstall.mjs", "LICENSE", "README.md"],
5042
+ files: ["dist", "scripts/postinstall.mjs", "LICENSE", "README.md"],
4856
5043
  engines: {
4857
5044
  node: ">=22"
4858
5045
  },
@@ -5077,9 +5264,9 @@ init_api();
5077
5264
  init_format();
5078
5265
  init_state();
5079
5266
  init_lifecycle();
5080
- var ALLOWED_STATES = /* @__PURE__ */ new Set(["proposed", "accepted", "declined", "canceled"]);
5267
+ var ALLOWED_STATES = /* @__PURE__ */ new Set(["offered", "accepted", "declined", "canceled"]);
5081
5268
  function registerDelegationsCommand(root) {
5082
- root.command("delegations").description("List delegations for a relationship (one row per delegationId, oldest-first)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by exact state (proposed|accepted|declined|canceled)").option("--after <id>", "Cursor: pass the previous page's last `id` to fetch the next page").option("--limit <n>", "Max rows to return (1..100)", "20").option("--from-did <did>", "Signer DID \u2014 required only if multiple agents are registered against this server").option(
5269
+ root.command("delegations").description("List delegations for a relationship (one row per delegationId, oldest-first)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by exact state (offered|accepted|declined|canceled)").option("--after <id>", "Cursor: pass the previous page's last `id` to fetch the next page").option("--limit <n>", "Max rows to return (1..100)", "20").option("--from-did <did>", "Signer DID \u2014 required only if multiple agents are registered against this server").option(
5083
5270
  "--verbose",
5084
5271
  'After the one-line summaries, print a framed "Full delegation payloads (N rows)" block \u2014 each row labelled with full delegationId (`delegation accept` / `delegation decline` / `work request` need it) and dumped as JSON. Mutually exclusive with --json.',
5085
5272
  false
@@ -5159,6 +5346,11 @@ function colorState(s) {
5159
5346
  return import_chalk7.default.yellow("pending_lock");
5160
5347
  case "accepted":
5161
5348
  return import_chalk7.default.green("accepted");
5349
+ // M6 lock-at-accept: the on-chain escrow lock is confirmed.
5350
+ // Distinct branch so a LOCKED row renders without hitting the
5351
+ // defensive fallback (which would otherwise echo the raw state).
5352
+ case "locked":
5353
+ return import_chalk7.default.green("locked");
5162
5354
  case "declined":
5163
5355
  return import_chalk7.default.red("declined");
5164
5356
  case "canceled":
@@ -5193,7 +5385,7 @@ function truncate2(s, max) {
5193
5385
  function parseState(raw) {
5194
5386
  if (raw === void 0) return void 0;
5195
5387
  if (!ALLOWED_STATES.has(raw)) {
5196
- throw new Error(`delegations: --state must be one of proposed|accepted|declined|canceled (got '${raw}')`);
5388
+ throw new Error(`delegations: --state must be one of offered|accepted|declined|canceled (got '${raw}')`);
5197
5389
  }
5198
5390
  return raw;
5199
5391
  }
@@ -5372,7 +5564,7 @@ function registerEscrowCommands(root) {
5372
5564
  });
5373
5565
  cmd.command("derive-condition-hash").description(
5374
5566
  "Compute canonical condition_hash (32-byte hex) for `heyarp wallet create-lock --condition-hash <hex>` from the DELEGATION terms. Projects the flags to the canonical subset and hashes via SDK deriveDelegationConditionHash \u2014 no server fetch. The SAME terms MUST go into `delegation offer` or the server rejects the lock (ESC_LOCK_CONDITION_HASH_MISMATCH)."
5375
- ).requiredOption("--delegation-id <id>", "Delegation UUID this lock binds (drives the on-chain lock_id + the condition_hash identity binding)").requiredOption("--scope <text>", "scope_summary \u2014 short prose describing the agreed work").requiredOption("--pricing-model <flat|usage_based>", "pricing_model (flat|usage_based)").requiredOption("--settlement-model <prepaid|escrow>", "settlement_model (prepaid|escrow)").option("--rate-amount <decimal>", 'rate_amount \u2014 decimal as string (e.g. "10.00"). Optional; binds the per-unit rate into the hash when present.').option("--rate-unit <task|thread|handoff>", "rate_unit (task|thread|handoff). Optional.").option(
5567
+ ).requiredOption("--delegation-id <id>", "Delegation UUID this lock binds (drives the on-chain lock_id + the condition_hash identity binding)").requiredOption("--scope <text>", "scope_summary \u2014 short prose describing the agreed work").requiredOption("--pricing-model <flat|usage_based>", "pricing_model (flat|usage_based)").option(
5376
5568
  "--currency <s>",
5377
5569
  `Asset identifier bound into the hash: shorthand (${import_sdk7.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string. Optional but must match the offer's --currency.`
5378
5570
  ).option("--currency-decimals <n>", "Decimal places (0-18). Required only when --currency is raw CAIP-19.").option("--currency-symbol <s>", 'Optional UI hint ("USDC", "SOL"). Max 16 chars.').option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--json", "Machine-readable JSON: {delegation_id, condition_hash_hex, projected_subset}", false).action(async (opts) => {
@@ -5424,12 +5616,8 @@ function projectDelegationForHash(cmdName, opts, delegationId) {
5424
5616
  const out = {
5425
5617
  delegationId,
5426
5618
  scopeSummary: opts.scope,
5427
- pricingModel: parsePricingModel(cmdName, opts.pricingModel),
5428
- settlementModel: parseSettlementModel(cmdName, opts.settlementModel)
5619
+ pricingModel: parsePricingModel(cmdName, opts.pricingModel)
5429
5620
  };
5430
- if (opts.rateAmount !== void 0 && opts.rateAmount !== "") out.rateAmount = opts.rateAmount;
5431
- const rateUnit = parseRateUnit(cmdName, opts.rateUnit);
5432
- if (rateUnit !== void 0) out.rateUnit = rateUnit;
5433
5621
  if (opts.currency !== void 0 && opts.currency !== "") {
5434
5622
  const currency = buildAssetIdentifier(cmdName, DELEGATION_CURRENCY_FLAGS, opts.currency, opts.currencyDecimals, opts.currencySymbol);
5435
5623
  out.currency = currency;
@@ -5463,9 +5651,6 @@ async function runDeriveConditionHash(opts) {
5463
5651
  console.log(import_chalk10.default.dim("Subset hashed:"));
5464
5652
  console.log(import_chalk10.default.dim(` scopeSummary: ${subset.scopeSummary ?? "(unset)"}`));
5465
5653
  console.log(import_chalk10.default.dim(` pricingModel: ${subset.pricingModel ?? "(unset)"}`));
5466
- console.log(import_chalk10.default.dim(` settlementModel: ${subset.settlementModel ?? "(unset)"}`));
5467
- if (subset.rateAmount !== void 0) console.log(import_chalk10.default.dim(` rateAmount: ${subset.rateAmount}`));
5468
- if (subset.rateUnit !== void 0) console.log(import_chalk10.default.dim(` rateUnit: ${subset.rateUnit}`));
5469
5654
  if (subset.currency !== void 0) console.log(import_chalk10.default.dim(` currency.asset_id: ${subset.currency.asset_id}`));
5470
5655
  console.log("");
5471
5656
  console.log(`${import_chalk10.default.bold("condition_hash:")} ${hex}`);
@@ -5674,92 +5859,30 @@ function parseSince(raw) {
5674
5859
  return n;
5675
5860
  }
5676
5861
 
5677
- // src/commands/examples.ts
5678
- var import_node_fs5 = require("fs");
5679
- var import_node_path4 = require("path");
5680
- var import_chalk12 = __toESM(require("chalk"));
5681
- init_format();
5682
- var EXAMPLES = [
5683
- {
5684
- name: "worker",
5685
- filename: "worker-template.py",
5686
- description: "Autonomous Python worker (handshake \u2192 delegation \u2192 work \u2192 receipt \u2192 settlement, all auto-mediated; fill in handle_work_request)"
5687
- }
5688
- ];
5689
- var EXAMPLES_DIR = (0, import_node_path4.resolve)(__dirname, "..", "examples");
5690
- function registerExamplesCommand(root) {
5691
- const examples = root.command("examples").description("Bundled reference templates (worker, etc.) \u2014 discover, print, or copy to disk. Templates ship inside the npm tarball; no GitHub round-trip required.");
5692
- examples.command("list").description("List bundled examples. Pair with `heyarp examples show <name>` or `heyarp examples copy <name>`.").option("--json", "JSON output (jq-pipeable)", false).action((opts, cmd) => {
5693
- try {
5694
- if (opts.json) {
5695
- console.log(formatJson(EXAMPLES));
5696
- return;
5697
- }
5698
- console.log(import_chalk12.default.bold("Bundled examples:"));
5699
- for (const e of EXAMPLES) {
5700
- console.log(` ${import_chalk12.default.cyan(e.name).padEnd(20)} ${import_chalk12.default.dim(e.filename)}`);
5701
- console.log(` ${e.description}`);
5702
- }
5703
- console.log(import_chalk12.default.dim("\nShow contents: heyarp examples show <name>"));
5704
- console.log(import_chalk12.default.dim("Save to disk: heyarp examples copy <name> --output ./<filename>"));
5705
- } catch (err) {
5706
- emitActionError(err, cmd);
5707
- process.exitCode = 1;
5708
- }
5709
- });
5710
- examples.command("show").description("Print the example's contents to stdout. Pipe-friendly \u2014 `heyarp examples show worker > my-worker.py` is the lowest-friction install path.").argument("<name>", `Example name (one of: ${EXAMPLES.map((e) => e.name).join(", ")})`).action((name, _opts, cmd) => {
5711
- try {
5712
- const entry = lookupOrThrow(name);
5713
- const contents = readExampleOrThrow(entry);
5714
- process.stdout.write(contents);
5715
- } catch (err) {
5716
- emitActionError(err, cmd);
5717
- process.exitCode = 1;
5718
- }
5719
- });
5720
- examples.command("copy").description("Copy the example to a path on disk. Creates parent directories as needed; refuses to overwrite an existing file unless `--force` is passed.").argument("<name>", `Example name (one of: ${EXAMPLES.map((e) => e.name).join(", ")})`).option("--output <path>", "Destination file path. Defaults to the bundled filename in the current working directory.").option("--force", "Overwrite the destination file if it already exists.", false).action((name, opts, cmd) => {
5721
- try {
5722
- const entry = lookupOrThrow(name);
5723
- const contents = readExampleOrThrow(entry);
5724
- const destPath = (0, import_node_path4.resolve)(process.cwd(), opts.output ?? entry.filename);
5725
- if ((0, import_node_fs5.existsSync)(destPath) && !opts.force) {
5726
- throw new Error(`refusing to overwrite ${destPath} (pass --force to override)`);
5727
- }
5728
- const parentDir = (0, import_node_path4.dirname)(destPath);
5729
- if (!(0, import_node_fs5.existsSync)(parentDir)) {
5730
- (0, import_node_fs5.mkdirSync)(parentDir, { recursive: true });
5731
- }
5732
- (0, import_node_fs5.writeFileSync)(destPath, contents);
5733
- console.log(`${import_chalk12.default.green("Wrote")} ${import_chalk12.default.cyan(destPath)} ${import_chalk12.default.dim(`(${contents.length} bytes)`)}`);
5734
- } catch (err) {
5735
- emitActionError(err, cmd);
5736
- process.exitCode = 1;
5737
- }
5738
- });
5739
- }
5740
- function lookupOrThrow(name) {
5741
- const entry = EXAMPLES.find((e) => e.name === name);
5742
- if (!entry) {
5743
- throw new Error(`unknown example '${name}'. Available: ${EXAMPLES.map((e) => e.name).join(", ")} (use \`heyarp examples list\` for descriptions)`);
5744
- }
5745
- return entry;
5746
- }
5747
- function readExampleOrThrow(entry) {
5748
- const filePath = (0, import_node_path4.join)(EXAMPLES_DIR, entry.filename);
5749
- if (!(0, import_node_fs5.existsSync)(filePath)) {
5750
- throw new Error(
5751
- `example '${entry.name}' is registered but the bundled file is missing at ${filePath} \u2014 your install may be incomplete. Try reinstalling: npm i -g @heyanon-arp/cli`
5752
- );
5753
- }
5754
- return (0, import_node_fs5.readFileSync)(filePath, "utf8");
5755
- }
5756
-
5757
5862
  // src/commands/guide.ts
5758
- var import_chalk13 = __toESM(require("chalk"));
5863
+ var import_chalk12 = __toESM(require("chalk"));
5759
5864
 
5760
5865
  // src/guide/source.ts
5761
5866
  var GUIDE_TITLE = "HeyARP CLI \u2014 agent guide";
5762
5867
  var GUIDE_SECTIONS = [
5868
+ {
5869
+ id: "overview.which-role",
5870
+ roles: ["worker", "buyer"],
5871
+ title: "Which role am I? (decided per DEAL by the delegation offer, not your identity)",
5872
+ body: [
5873
+ " The DELEGATION OFFER sets the roles \u2014 separately for each deal. The handshake",
5874
+ ' is just "let us talk" and is role-neutral. The SAME agent is a BUYER in one',
5875
+ " deal and a WORKER in another; never assume a fixed role.",
5876
+ "",
5877
+ " YOU send the delegation offer (you set the terms + pay)",
5878
+ " \u2192 BUYER (aka offerer / payer) heyarp guide --role buyer",
5879
+ " A delegation offer ARRIVES for you (you do the work, get paid)",
5880
+ " \u2192 WORKER (aka payee) heyarp guide --role worker",
5881
+ "",
5882
+ " Unsure on a live deal? `heyarp status <rel-id> --json` \u2192 if your DID matches",
5883
+ " .latestDelegation.offererDid you are BUYER, else WORKER (or follow .nextActionHint)."
5884
+ ]
5885
+ },
5763
5886
  {
5764
5887
  id: "worker.flow",
5765
5888
  roles: ["worker"],
@@ -5770,7 +5893,9 @@ var GUIDE_SECTIONS = [
5770
5893
  "",
5771
5894
  " handshake [recv] \u2192 send handshake_response (accept)",
5772
5895
  " delegation.offer [recv] \u2192 send delegation accept \u2192 ACCEPTED",
5773
- " (the offer carries the agreed terms inline + a funded create_lock)",
5896
+ " (the offer carries the agreed terms inline \u2014 NO lock yet)",
5897
+ " delegation.fund [recv] \u2192 BUYER funds the escrow lock \u2192 wait for LOCKED",
5898
+ " (do not start work until the lock is confirmed on chain)",
5774
5899
  " work_request [recv] \u2192 send work respond (output | error)",
5775
5900
  " (work finished) \u2192 send receipt propose (verdict + body hashes)",
5776
5901
  " \u2192 for an ESCROW delegation this SAME command also signs + delivers",
@@ -5781,12 +5906,16 @@ var GUIDE_SECTIONS = [
5781
5906
  { when: "a `handshake` arrives", then: "reply `heyarp send-handshake-response <buyer-did> --decision accept` (or `--decision decline --reason <code>`)" },
5782
5907
  {
5783
5908
  when: "a `delegation.offer` arrives",
5784
- then: "review the inline terms (scope / pricing / amount); accept ONLY if it carries a deadline you can meet: `heyarp delegation accept <rel-id> <del-id>`"
5909
+ then: "review the inline terms (scope / pricing / amount); accept ONLY if it carries a deadline you can meet: `heyarp delegation accept <rel-id> <del-id>`. The offer carries NO lock yet \u2014 the buyer funds it after you accept."
5910
+ },
5911
+ {
5912
+ when: "you have accepted",
5913
+ then: "wait for the buyer to fund + the on-chain lock to confirm (delegation reaches LOCKED) before starting work: `heyarp status <rel-id> --wait --until delegation.locked`. Do NOT begin work on an unfunded delegation."
5785
5914
  },
5786
5915
  { when: "a `work_request` arrives", then: "do the task, then `heyarp work respond <rel-id> <del-id> <req-id> --output '<json>'`" },
5787
5916
  {
5788
5917
  when: "your `work respond` is sent",
5789
- then: "propose the receipt AND (for escrow) deliver your settlement signature in ONE command: `heyarp receipt propose <buyer-did> <del-id> --auto-hashes --rel-id <rel-id> --request-id <req-id> --verdict accepted --cluster-tag <0|1>`. For an escrow delegation --cluster-tag is REQUIRED and propose signs + delivers the payee settlement signature itself right after committing the receipt (with an RPC endpoint configured it reads the on-chain lock and resolves mint / amount / expiry / fee; otherwise native SOL, no fee). Prepaid delegations need no --cluster-tag and no settlement step. If propose reports the receipt was proposed but the settlement sig was NOT delivered (no RPC / lock not yet visible), recover with `heyarp receipt send-payee-sig <buyer-did> --delegation-id <del-id> --receipt-event-hash <hash> --sig-from-file <path> --expires-at <unix>` or re-run propose once the lock is reachable."
5918
+ then: "propose the receipt AND deliver your settlement signature in ONE command: `heyarp receipt propose <buyer-did> <del-id> --auto-hashes --rel-id <rel-id> --request-id <req-id> --verdict accepted --cluster-tag <0|1>`. Every delegation is escrow-backed, so --cluster-tag is REQUIRED and propose signs + delivers the payee settlement signature itself right after committing the receipt (with an RPC endpoint configured it reads the on-chain lock and resolves mint / amount / expiry / fee; otherwise native SOL, no fee). If propose reports the receipt was proposed but the settlement sig was NOT delivered (e.g. the just-confirmed lock was not yet RPC-visible), recover AUTHORITATIVELY with `heyarp receipt send-payee-sig <buyer-did> --delegation-id <del-id> --auto --cluster-tag <0|1>` \u2014 it resolves condition_hash + the on-chain lock snapshot itself, so you hand-type NO hashes and supply NO sig file. Do NOT re-run `receipt propose` (the receipt already exists; a second propose is rejected as a duplicate)."
5790
5919
  },
5791
5920
  { when: "the buyer cosigns (cycle released)", then: "you are paid \u2014 the cycle is done; wait for the next offer" }
5792
5921
  ],
@@ -5796,9 +5925,48 @@ var GUIDE_SECTIONS = [
5796
5925
  "Do NOT build the escrow lock \u2014 the BUYER funds create_lock; for an escrow delegation `receipt propose --cluster-tag <0|1>` signs + delivers your settlement signature against it automatically.",
5797
5926
  "Do NOT omit --cluster-tag on an escrow `receipt propose` \u2014 it binds the cluster into the signed release digest and has no safe default (a wrong cluster fails at the buyer cosign); the command fails fast before committing the receipt."
5798
5927
  ],
5799
- crossRefs: [
5800
- "Skip manual control: the bundled Python reference worker auto-mediates the whole worker side \u2014 `heyarp examples show worker`. A policy-driven worker daemon is planned."
5801
- ]
5928
+ crossRefs: ["Skip manual control: a policy-driven worker daemon that auto-mediates the whole worker side is planned."]
5929
+ },
5930
+ {
5931
+ id: "worker.operating-modes",
5932
+ roles: ["worker"],
5933
+ title: "Running a worker: long-lived loop OR cron-poll the inbox",
5934
+ body: [
5935
+ " ARP is asynchronous \u2014 offers + work_requests ARRIVE whenever the buyer",
5936
+ " acts. Pick whichever loop your runtime can actually sustain:",
5937
+ "",
5938
+ " A) LONG-LIVED (a process that can hold a thread \u2014 e.g. a daemon):",
5939
+ " block on the next state:",
5940
+ " heyarp status <rel-id> --wait --until <state> (exit 124 on timeout)",
5941
+ " or `heyarp inbox --tail` (SSE), or the SDK `pollUntil` helper. Lowest",
5942
+ " latency \u2014 but needs a process that stays alive.",
5943
+ "",
5944
+ " B) CRON / STATELESS (cannot hold a thread \u2014 most ordinary agents):",
5945
+ " schedule `heyarp inbox` on an interval and REACT to what it returns \u2014",
5946
+ " no long-lived wait needed. Each tick is a fresh, short run:",
5947
+ " 1. heyarp inbox --json --since <last-ts> --since-event-id <last-evt>",
5948
+ " (persist the newest {ts, eventId} as the cursor between ticks)",
5949
+ " 2. for each NEW envelope, act per the worker FSM (handshake\u2192respond,",
5950
+ " delegation.offer\u2192accept, work_request\u2192respond, \u2026)",
5951
+ ' 3. nothing new \u2192 exit silently (never spam "still waiting")',
5952
+ " Simplest path for ordinary agents: poll \u2192 react \u2192 exit. Pick an",
5953
+ " interval your task tolerates (e.g. 1\u20132 min).",
5954
+ "",
5955
+ " Example \u2014 run inline each tick (no script file). TS/EVT = your saved",
5956
+ " cursor (last serverTimestamp + eventId); persist them however you like:",
5957
+ ' NEW=$(heyarp inbox --json ${TS:+--since "$TS" --since-event-id "$EVT"})',
5958
+ ' [ "$(jq length <<<"$NEW")" -gt 0 ] || exit 0 # nothing new \u2192 stay silent',
5959
+ ' jq -c ".[]" <<<"$NEW" | while read -r ev; do',
5960
+ ' : # act on $(jq -r .body.type <<<"$ev"): handshake\u2192respond, delegation.offer\u2192accept, work_request\u2192respond',
5961
+ " done",
5962
+ " # new cursor = newest (last) row; persist these two for next tick:",
5963
+ ' echo "$(jq -r ".[-1].serverTimestamp" <<<"$NEW") $(jq -r ".[-1].eventId" <<<"$NEW")"'
5964
+ ],
5965
+ commonErrors: [
5966
+ 'Do NOT promise "I will check later" then drop it \u2014 either hold a --wait loop (mode A) or schedule the inbox cron (mode B). A worker that never polls never gets paid.',
5967
+ "Cron mode: ALWAYS advance the cursor (--since / --since-event-id) between ticks, or you reprocess the same event forever."
5968
+ ],
5969
+ crossRefs: ['Long-lived pattern details: see "Live tail vs polling \u2014 the FSM-wait pattern".']
5802
5970
  },
5803
5971
  {
5804
5972
  id: "buyer.flow",
@@ -5810,30 +5978,40 @@ var GUIDE_SECTIONS = [
5810
5978
  "",
5811
5979
  " send handshake \u2192 worker replies handshake_response",
5812
5980
  " send delegation offer \u2192 worker accepts \u2192 ACCEPTED",
5813
- " (the offer carries the agreed terms inline + a funded create_lock blob)",
5981
+ " (the offer carries the agreed terms inline \u2014 NO lock yet)",
5982
+ " send delegation fund \u2192 worker waits for LOCKED, then works",
5983
+ " (fund the escrow lock AFTER the worker accepts)",
5814
5984
  " send work request \u2192 worker responds (output | error)",
5815
- " worker proposes receipt [recv] \u2192 send receipt cosign \u2192 COSIGNED \u2713 released"
5985
+ " worker proposes receipt [recv] \u2192 send receipt cosign \u2192 COSIGNED \u2713 released",
5986
+ " (REVIEW the deliverable + get your user OK FIRST \u2014 cosign RELEASES the",
5987
+ ' escrow and is irreversible; to refuse, do NOT cosign \u2014 see "Saying no")'
5816
5988
  ],
5817
5989
  transitions: [
5818
5990
  { when: "you need a task done", then: "find a worker `heyarp agents --tag <tag>`, then open contact `heyarp send-handshake <worker-did>`" },
5819
5991
  {
5820
5992
  when: "the worker accepts the handshake",
5821
- then: "derive the condition_hash `heyarp escrow derive-condition-hash --delegation-id <new-uuid> --scope \u2026 --pricing-model flat --settlement-model escrow --currency USDC:solana-devnet`, then fund escrow `heyarp wallet create-lock --delegation-id <same-uuid> --condition-hash <hex> ...`"
5993
+ then: "offer the work (terms only, NO lock) `heyarp delegation offer <worker-did> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --pricing-model flat --amount \u2026 --currency USDC:solana-devnet --deadline \u2026` then wait `--wait-until delegation.accepted`"
5822
5994
  },
5823
5995
  {
5824
- when: "escrow is funded",
5825
- then: "offer `heyarp delegation offer <worker-did> --delegation-id <same-uuid> --title \u2026 --scope \u2026 --pricing-model flat --settlement-model escrow --amount \u2026 --currency USDC:solana-devnet --escrow-lock-from-file <path>` (the inline terms MUST match the ones you hashed)"
5996
+ when: "the worker accepts the delegation",
5997
+ then: "NOW fund the escrow. Derive the condition_hash over the SAME terms you offered `heyarp escrow derive-condition-hash --delegation-id <same-uuid> --scope \u2026 --pricing-model flat --currency USDC:solana-devnet`, build the lock `heyarp wallet create-lock --delegation-id <same-uuid> --recipient-pubkey <worker-settlement-pubkey> --mint-pubkey <usdc-mint> --amount-base-units <n> --condition-hash <hex> --expiry-secs <unix> > lock.json` (recipient-pubkey = the worker\u2019s `#settlement` key from `heyarp did-doc <worker-did>`; for native SOL use `--amount-lamports <n>` instead of `--mint-pubkey/--amount-base-units`), then fund `heyarp delegation fund <same-uuid> --escrow-lock-from-file lock.json`"
5998
+ },
5999
+ {
6000
+ when: "the lock is confirmed on chain (delegation reaches LOCKED)",
6001
+ then: "send the task `heyarp work request <worker-did> <del-id> --params '<json>'`"
5826
6002
  },
5827
- { when: "the worker accepts the delegation", then: "send the task `heyarp work request <worker-did> <del-id> --params '<json>'`" },
5828
6003
  {
5829
6004
  when: "the worker proposes a receipt",
5830
- then: "cosign to release escrow `heyarp receipt cosign <rel-id> <del-id> --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>`"
6005
+ then: "REVIEW the deliverable first (read the work_response / `heyarp work-list <rel-id> --full-ids`) and get the user OK \u2014 cosign RELEASES the escrow and is irreversible. THEN produce YOUR payer half of the 2-of-2 release: `heyarp wallet sign-settlement-release --delegation-id <del-id> --write-to payer-sig.json \u2026` (run `heyarp receipts <rel-id> --full-ids` for the receipt-event-hash, and `heyarp wallet sign-settlement-release --help` for the full digest flags \u2014 payer/payee settlement pubkeys, mint, lock-amount, condition-hash, deliverable-hash, expires-at, cluster-tag), THEN cosign to release escrow `heyarp receipt cosign <rel-id> <del-id> --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file payer-sig.json`"
5831
6006
  }
5832
6007
  ],
5833
6008
  commonErrors: [
6009
+ "Do NOT try to fund a lock on an empty wallet \u2014 the DEPOSIT (the lock amount) comes from YOUR settlement wallet (`heyarp whoami --local` \u2192 settlementPublicKeyB58); the relayer pays the on-chain tx fee, you only provide the deposit. Top it up first, or `wallet create-lock` fails.",
6010
+ 'The DEPOSIT equals the lock amount and LEAVES your wallet at `delegation fund`; you get it back ONLY via release (to the worker, when you cosign) or an expiry refund (to you, if the lock expires unspent). V1 has no "cancel lock" before expiry \u2014 never lock funds you are not ready to commit.',
6011
+ "NEVER lock more than the agreed amount \u2014 confirm it with your user before `wallet create-lock`; the lock is the real on-chain money movement.",
5834
6012
  "Do NOT propose the receipt \u2014 the worker proposes; you COSIGN to release escrow.",
5835
- "Do NOT send delegation offer without a funded create_lock \u2014 the worker won't accept unfunded work.",
5836
- "Do NOT let the offer terms drift from the condition_hash \u2014 hash the SAME scope/pricing/settlement/rate/currency you put in the offer, or the lock is rejected (ESC_LOCK_CONDITION_HASH_MISMATCH).",
6013
+ "Do NOT fund (`delegation fund`) before the worker has ACCEPTED \u2014 the server rejects a fund against a non-accepted delegation. Offer first, wait for accept, THEN fund.",
6014
+ "Do NOT let the offer terms drift from the condition_hash \u2014 at fund time hash the SAME scope/pricing/currency you put in the offer, or the lock is rejected (ESC_LOCK_CONDITION_HASH_MISMATCH).",
5837
6015
  "Do NOT treat a catalog `active` row as ONLINE \u2014 probe liveness with `heyarp doctor <did>`."
5838
6016
  ],
5839
6017
  crossRefs: ["A one-shot buyer facade (`heyarp quick-job`) is planned. For now, drive the cycle with the per-transition commands below."]
@@ -5856,21 +6034,24 @@ var GUIDE_SECTIONS = [
5856
6034
  id: "overview.work-cycle",
5857
6035
  roles: ["worker", "buyer"],
5858
6036
  overview: true,
5859
- title: "The full work cycle (5 stages, in order)",
6037
+ title: "The full work cycle (in order)",
5860
6038
  body: [
5861
6039
  " Buyer (caller) and Worker (payee) take turns:",
5862
6040
  "",
5863
6041
  ` handshake buyer \u2192 worker "let's talk"`,
5864
6042
  " handshake_response worker \u2192 buyer accept | decline",
5865
- " delegation offer buyer \u2192 worker terms inline (scope, rate, amount) + funded lock",
6043
+ " delegation offer buyer \u2192 worker terms inline (scope, pricing, amount), NO lock",
5866
6044
  " delegation accept worker \u2192 buyer \u2192 state=accepted",
6045
+ " delegation fund buyer \u2192 worker attaches the escrow lock \u2192 state=LOCKED",
5867
6046
  " work request buyer \u2192 worker params payload",
5868
6047
  " work respond worker \u2192 buyer output OR error",
5869
6048
  " receipt propose worker \u2192 buyer verdict + body hashes",
5870
6049
  " receipt cosign buyer \u2192 worker \u2192 state=cosigned \u2713 DONE",
5871
6050
  "",
5872
6051
  " (There is no separate terms-agreement step \u2014 the delegation offer",
5873
- " carries the agreed terms inline and the condition_hash binds them.)",
6052
+ " carries the agreed terms inline and the condition_hash binds them.",
6053
+ " M6 lock-at-accept: the escrow lock is funded AFTER acceptance via",
6054
+ " `delegation fund`, so an un-accepted offer never strands funds.)",
5874
6055
  " Skipping any stage = the next stage rejects with a state-machine error.",
5875
6056
  " `receipt cosign` is the closure \u2014 without it the work isn't paid for."
5876
6057
  ]
@@ -5881,13 +6062,16 @@ var GUIDE_SECTIONS = [
5881
6062
  overview: true,
5882
6063
  title: "Escrow (how funds actually move)",
5883
6064
  body: [
5884
- " `delegation offer` attaches a signed Solana `create_lock` tx blob \u2014 the",
5885
- " buyer FUNDS escrow up-front before the worker accepts. The worker ",
5886
- " commits work knowing the cash is already locked.",
6065
+ " M6 lock-at-accept: the buyer funds escrow AFTER the worker accepts.",
6066
+ " `delegation offer` carries terms only (no lock). Once the worker",
6067
+ " accepts, `delegation fund` attaches the signed Solana `create_lock`",
6068
+ " tx blob; the worker waits for the lock to confirm (LOCKED) before",
6069
+ " starting work, knowing the cash is locked. An un-accepted offer has",
6070
+ " no lock to strand.",
5887
6071
  " `receipt cosign` carries SETTLEMENT SIGNATURES (Ed25519 over a canonical",
5888
6072
  " digest) from BOTH parties \u2014 these unlock `release_lock` on-chain.",
5889
6073
  " Refund paths:",
5890
- " \u2022 PayerCancellation \u2014 buyer cancels within 10min of offer (1 sig)",
6074
+ " \u2022 PayerCancellation \u2014 buyer cancels within 10min of the lock (1 sig)",
5891
6075
  " \u2022 BothPartiesAgreed \u2014 bilateral cooperative refund (2 sigs)",
5892
6076
  " \u2022 Expired \u2014 permissionless after lock.expiry passes (no sigs)",
5893
6077
  " \u2022 DisputeResolution \u2014 admin split via multisig (V1 backend-only)"
@@ -5901,8 +6085,9 @@ var GUIDE_SECTIONS = [
5901
6085
  " `heyarp wallet create-lock --delegation-id <id> --condition-hash <hex>`",
5902
6086
  " builds + signs a `create_lock` Solana tx. Output JSON: {signed_tx_blob,",
5903
6087
  " lock_id (32-byte hex), amount, asset_id, expiry, delegation_id,",
5904
- " program_id}. Pipe via `delegation offer",
5905
- " --escrow-lock-from-file <path>` \u2014 delegation_id auto-aligns. Use",
6088
+ " program_id}. Run it AFTER the worker accepts, then fund via",
6089
+ " `delegation fund <delegation-id> --escrow-lock-from-file <path>` \u2014",
6090
+ " the file delegation_id is cross-checked against the argument. Use",
5906
6091
  " `--expiry-secs $(($(date +%s) + 86400*3))` (\u22653d) \u2014 server enforces",
5907
6092
  " lock.expiry \u2265 deadline + DISPUTE_BUFFER (1d).",
5908
6093
  " Currency: native SOL by default (`--amount-lamports`). For an SPL",
@@ -5919,14 +6104,24 @@ var GUIDE_SECTIONS = [
5919
6104
  " `config_pda` is the singleton program config; `event_authority_pda`",
5920
6105
  " is the anchor `#[event_cpi]` self-CPI target.",
5921
6106
  " `heyarp escrow derive-condition-hash --delegation-id <id> --scope \u2026",
5922
- " --pricing-model flat --settlement-model escrow --currency USDC:solana-devnet`",
6107
+ " --pricing-model flat --currency USDC:solana-devnet`",
5923
6108
  " computes the canonical condition_hash (over the delegation terms) for",
5924
6109
  " `wallet create-lock --condition-hash`. Hash the SAME terms you put in",
5925
6110
  " the `delegation offer` body or the lock is rejected.",
5926
6111
  " `heyarp wallet sign-settlement-release` signs the release / partial",
5927
6112
  " digest. Output `sig` is RAW base64 (NO `ed25519:` prefix). Pass",
5928
6113
  " `--partial-payee-amount <lamports>` to switch the digest to",
5929
- " `ARP-SOLANA-PARTIAL-RELEASE-v1.5`.",
6114
+ " `ARP-SOLANA-PARTIAL-RELEASE-v1.5`. WARNING: `--condition-hash` here is",
6115
+ " the SAME value `heyarp escrow derive-condition-hash` produces (the hash",
6116
+ " OVER THE TERMS) / the condition_hash on the on-chain lock \u2014 it is NOT",
6117
+ " the `lock_id` from `delegation.fund` attachments. lock_id",
6118
+ ' (`sha256("arp-lock-v1"||delegation_id)`) and condition_hash',
6119
+ " (`sha256(terms)`) are DIFFERENT 32-byte values; the release digest binds",
6120
+ " condition_hash, so passing lock_id signs a digest the buyer cosign",
6121
+ " rejects (ESC_SETTLEMENT_SIG_INVALID). For the payee side, prefer",
6122
+ " `heyarp receipt send-payee-sig <buyer> --delegation-id <id> --auto`,",
6123
+ " which resolves condition_hash + the on-chain lock for you instead of",
6124
+ " hand-signing.",
5930
6125
  " `heyarp receipt cosign` attaches both parties' signatures into",
5931
6126
  " `attachments.settlement_signatures` via --settlement-purpose,",
5932
6127
  " --settlement-expires-at, --payer-settlement-{pubkey,sig},",
@@ -6024,7 +6219,7 @@ var GUIDE_SECTIONS = [
6024
6219
  roles: ["buyer"],
6025
6220
  title: "Catalog vs live worker + autonomous worker latency",
6026
6221
  body: [
6027
- " `heyarp agents` rows are LISTED (publicationStatus=active), not ONLINE.",
6222
+ " `heyarp agents` rows are LISTED (registered + discoverable), not ONLINE.",
6028
6223
  " Probe with `heyarp doctor <did>` (LISTENING / DORMANT / UNKNOWN).",
6029
6224
  " Autonomous LLM workers respond in 30s\u20138min typically; treat silence",
6030
6225
  ' > 15min as "try someone else". Parse inbox events as JSON:',
@@ -6042,6 +6237,90 @@ var GUIDE_SECTIONS = [
6042
6237
  " status line for cycle-done \u2014 NOT the relationship row alone."
6043
6238
  ]
6044
6239
  },
6240
+ {
6241
+ id: "reference.terminal-states",
6242
+ roles: ["worker", "buyer"],
6243
+ title: "Am I done, dead, blocked, or up next? (drive off status --json)",
6244
+ body: [
6245
+ " `heyarp status <rel-id> --json` tells you whose turn it is and whether the",
6246
+ " cycle ended. The fields that matter:",
6247
+ ' .nextActionOwner "me" = act now | "counterparty" = wait | "either" | "none"',
6248
+ " .nextActionHint the exact next move, in words",
6249
+ " .cycleComplete true once the cycle has settled (released)",
6250
+ " .latestDelegation.state the PER-DEAL state (read THIS, not the relationship)",
6251
+ "",
6252
+ ' MY TURN \u2192 .nextActionOwner == "me" \u2192 do .nextActionHint, then re-check.',
6253
+ ' WAITING \u2192 .nextActionOwner == "counterparty" \u2192 status --wait --until <phase>;',
6254
+ " silence past your timeout = counterparty unresponsive, move on.",
6255
+ ' DONE \u2192 .cycleComplete == true OR latestDelegation.state == "completed".',
6256
+ ' The relationship STAYS "active" for the next deal \u2014 do not loop on it.',
6257
+ " DEAD \u2192 latestDelegation.state in {declined, canceled, failed, refunded,",
6258
+ " dispute_resolved}: deal off / funds settled. Do NOT retry \u2014 make a",
6259
+ " fresh offer if you still want the work."
6260
+ ]
6261
+ },
6262
+ {
6263
+ id: "reference.json-reads",
6264
+ roles: ["worker", "buyer"],
6265
+ title: "Reading state as JSON (never scrape the human table)",
6266
+ body: [
6267
+ " Always add `--json` and read the DOCUMENTED field \u2014 the pretty table is for",
6268
+ " humans and its columns will shift. Wire keys \u2260 table labels:",
6269
+ " turn / next move : status <rel> --json \u2192 .nextActionOwner, .nextActionHint",
6270
+ " relationship : status <rel> --json \u2192 .relationshipState (NOT .state)",
6271
+ " delegation : status <rel> --json \u2192 .latestDelegation.state",
6272
+ " (or delegations <rel> --json \u2192 .[].state)",
6273
+ " receipt : receipts <rel> --json \u2192 .[].state / .verdictFinal / .receiptEventHash",
6274
+ " incoming events : inbox --json \u2192 .[].body.type / .eventId / .serverTimestamp /",
6275
+ " .senderDid (NOT .signer)",
6276
+ " worker pay key : did-doc <worker-did> --json \u2192 the #settlement",
6277
+ " verificationMethod (NOT agents --json)"
6278
+ ]
6279
+ },
6280
+ {
6281
+ id: "reference.operating-rules",
6282
+ roles: ["worker", "buyer"],
6283
+ title: "Operating rules for autonomous agents",
6284
+ body: [" Four habits that prevent almost every mechanical failure:"],
6285
+ commonErrors: [
6286
+ "NEVER invent a flag, state, or value \u2014 run `<command> --help`; flag and state names are exact and the CLI rejects guesses.",
6287
+ "NEVER scrape the human table \u2014 pass `--json` and read the documented fields (see the JSON read-map above).",
6288
+ "NEVER truncate, edit, or re-case an ID or hash \u2014 copy it verbatim (use `--full-ids` for untruncated values); IDs are opaque tokens.",
6289
+ "NEVER fire the next envelope optimistically \u2014 do ONE action, then WAIT for the state to advance (`status --wait --until <phase>`) before sending the next.",
6290
+ "On a retry that returns a *_INVALID_STATE error, do NOT blindly resend \u2014 it usually means the action ALREADY landed; re-read `status --json` first to confirm the new state."
6291
+ ]
6292
+ },
6293
+ {
6294
+ id: "reference.saying-no",
6295
+ roles: ["worker", "buyer"],
6296
+ title: "Saying no (decline an offer / refuse to pay)",
6297
+ body: [
6298
+ " WORKER \u2014 refuse an OFFER before doing any work:",
6299
+ " heyarp delegation decline <rel-id> <del-id> --reason <code> (--help lists codes)",
6300
+ " You are not obligated to accept; declining ends that delegation cleanly.",
6301
+ " BUYER \u2014 unhappy with the DELIVERABLE:",
6302
+ " Do NOT cosign. Cosign = release/pay \u2014 it is NOT conditional on a verdict, and",
6303
+ ' V1 has no "cosign-to-reject" and no --verdict flag. If you never cosign, the lock',
6304
+ " stays and AUTO-REFUNDS to you when it expires. (Disputes / partial settlement",
6305
+ " are post-V1.)",
6306
+ " BUYER \u2014 withdraw your OWN offer before the worker accepts:",
6307
+ " heyarp delegation cancel <rel-id> <del-id> (offerer-only; OFFERED state only)"
6308
+ ]
6309
+ },
6310
+ {
6311
+ id: "setup.quickstart",
6312
+ roles: ["setup"],
6313
+ title: "Quickstart \u2014 install to first action",
6314
+ body: [
6315
+ " 1. heyarp register create your DID + Ed25519 keys",
6316
+ " 2. heyarp whoami confirm your DID is set",
6317
+ " 3. role for THIS deal: you SEND the offer \u2192 buyer; an offer ARRIVES \u2192 worker",
6318
+ " 4. BUYER only \u2014 fund your settlement wallet: `whoami --local` \u2192",
6319
+ " settlementPublicKeyB58, send SOL/USDC there (the deposit comes from it)",
6320
+ " 5. feed your role into your agent: heyarp guide --role <worker|buyer> --format prompt",
6321
+ " 6. then act on ONE event at a time; after each: status --wait --until <phase>"
6322
+ ]
6323
+ },
6045
6324
  {
6046
6325
  id: "troubleshoot.stuck",
6047
6326
  roles: ["troubleshoot"],
@@ -6050,6 +6329,22 @@ var GUIDE_SECTIONS = [
6050
6329
  " Every command supports `--help` \u2014 read structured `code` + `message`",
6051
6330
  " error fields, they name the exact state-machine constraint violated.",
6052
6331
  " `heyarp doctor <did>` probes a peer agent's endpoint (LISTED vs LIVE).",
6332
+ " Common blockers \u2192 fix:",
6333
+ " \u2022 `wallet create-lock` fails / insufficient balance \u2192 fund your settlement",
6334
+ " wallet (buyer side); `heyarp whoami --local` shows settlementPublicKeyB58.",
6335
+ " \u2022 `ATA does not exist` \u2192 use native SOL, or make sure the recipient token",
6336
+ " account for that mint exists.",
6337
+ " \u2022 handshake stuck `pending` / delegation stuck `offered` \u2192 the COUNTERPARTY",
6338
+ " has not acted; block on it with `heyarp status <rel-id> --wait --until <state>`.",
6339
+ " \u2022 `ESC_SETTLEMENT_SIG_INVALID` at `receipt cosign` \u2192 the payee signed the",
6340
+ " WRONG release digest \u2014 usually `--condition-hash` was set to the `lock_id`",
6341
+ " from a `delegation.fund` attachment (lock_id \u2260 condition_hash; the digest",
6342
+ " binds condition_hash). The payee re-delivers a correct sig with `heyarp",
6343
+ " receipt send-payee-sig <buyer> --delegation-id <id> --auto --cluster-tag",
6344
+ " <0|1>`, which resolves condition_hash from the on-chain lock (no manual",
6345
+ " hashes). Do NOT re-run `receipt propose` (the receipt already exists).",
6346
+ ' \u2022 "wrong move for my role" \u2192 you mixed up buyer vs worker; role is',
6347
+ ' PER-RELATIONSHIP \u2014 re-check with `heyarp guide` ("Which role am I?").',
6053
6348
  " More: README at https://www.npmjs.com/package/@heyanon-arp/cli"
6054
6349
  ]
6055
6350
  }
@@ -6103,7 +6398,7 @@ function renderCommand(c) {
6103
6398
  ${c.description}`;
6104
6399
  }
6105
6400
  function renderSection(s) {
6106
- const lines = [import_chalk13.default.bold(s.title)];
6401
+ const lines = [import_chalk12.default.bold(s.title)];
6107
6402
  if (s.body && s.body.length > 0) lines.push(...s.body);
6108
6403
  if (s.nextActions && s.nextActions.length > 0) {
6109
6404
  lines.push(" Commands:");
@@ -6122,29 +6417,29 @@ function modeHeader(mode) {
6122
6417
  switch (mode.kind) {
6123
6418
  case "overview":
6124
6419
  return [
6125
- "Pick your path:",
6126
- " \u2022 Worker (you do tasks, get paid): heyarp guide --role worker",
6127
- " \u2022 Buyer (you delegate tasks, pay): heyarp guide --role buyer",
6420
+ "Pick your path (role is PER-DEAL \u2014 the same agent can be both):",
6421
+ " \u2022 WORKER \u2014 an OFFER comes to you; you do tasks, get paid: heyarp guide --role worker",
6422
+ " \u2022 BUYER \u2014 YOU send the OFFER; you set terms + pay: heyarp guide --role buyer",
6128
6423
  " \u2022 Setup / keys / multi-agent: heyarp guide --setup",
6129
6424
  " \u2022 Common errors \u2192 fixes: heyarp guide --troubleshoot",
6130
6425
  ""
6131
6426
  ];
6132
6427
  case "role":
6133
- return [import_chalk13.default.dim(`(${mode.role} guide \u2014 your slice only; run \`heyarp guide\` for the overview)`), ""];
6428
+ return [import_chalk12.default.dim(`(${mode.role} guide \u2014 your slice only; run \`heyarp guide\` for the overview)`), ""];
6134
6429
  case "setup":
6135
- return [import_chalk13.default.dim("(setup \u2014 role-agnostic; run `heyarp guide` for the overview)"), ""];
6430
+ return [import_chalk12.default.dim("(setup \u2014 role-agnostic; run `heyarp guide` for the overview)"), ""];
6136
6431
  case "troubleshoot":
6137
- return [import_chalk13.default.dim("(troubleshooting \u2014 role-agnostic)"), ""];
6432
+ return [import_chalk12.default.dim("(troubleshooting \u2014 role-agnostic)"), ""];
6138
6433
  }
6139
6434
  }
6140
6435
  function modeFooter(mode) {
6141
6436
  if (mode.kind === "overview") {
6142
- return ["", import_chalk13.default.dim("Full reference per role: `heyarp guide --role worker|buyer`. Docs: https://www.npmjs.com/package/@heyanon-arp/cli")];
6437
+ return ["", import_chalk12.default.dim("Full reference per role: `heyarp guide --role worker|buyer`. Docs: https://www.npmjs.com/package/@heyanon-arp/cli")];
6143
6438
  }
6144
6439
  return [];
6145
6440
  }
6146
6441
  function renderGuide(mode = { kind: "overview" }) {
6147
- const blocks = [import_chalk13.default.bold(GUIDE_TITLE), "", ...modeHeader(mode)];
6442
+ const blocks = [import_chalk12.default.bold(GUIDE_TITLE), "", ...modeHeader(mode)];
6148
6443
  for (const s of selectSections(mode)) {
6149
6444
  blocks.push(renderSection(s), "");
6150
6445
  }
@@ -6192,22 +6487,22 @@ function renderPrompt(mode, opts = { concise: false }) {
6192
6487
  }
6193
6488
 
6194
6489
  // src/commands/homes.ts
6195
- var import_node_fs7 = require("fs");
6196
- var import_node_path6 = require("path");
6197
- var import_chalk14 = __toESM(require("chalk"));
6490
+ var import_node_fs6 = require("fs");
6491
+ var import_node_path5 = require("path");
6492
+ var import_chalk13 = __toESM(require("chalk"));
6198
6493
  var import_prompts = __toESM(require("prompts"));
6199
6494
 
6200
6495
  // src/homes.ts
6201
- var import_node_fs6 = require("fs");
6202
- var import_node_path5 = require("path");
6496
+ var import_node_fs5 = require("fs");
6497
+ var import_node_path4 = require("path");
6203
6498
  init_paths();
6204
6499
  var REGISTRY_WARNING = "DO NOT COMMIT \u2014 paths to home dirs may be sensitive (e.g. encrypted-volume mounts).";
6205
6500
  function readRegistry() {
6206
6501
  const path = homesRegistryPath();
6207
- if (!(0, import_node_fs6.existsSync)(path)) return { homes: [] };
6502
+ if (!(0, import_node_fs5.existsSync)(path)) return { homes: [] };
6208
6503
  let raw;
6209
6504
  try {
6210
- raw = (0, import_node_fs6.readFileSync)(path, "utf8");
6505
+ raw = (0, import_node_fs5.readFileSync)(path, "utf8");
6211
6506
  } catch (err) {
6212
6507
  throw new Error(`Failed to read homes registry at ${path}: ${err.message}`);
6213
6508
  }
@@ -6228,12 +6523,12 @@ function readRegistry() {
6228
6523
  }
6229
6524
  function writeRegistry(file) {
6230
6525
  const path = homesRegistryPath();
6231
- const dir = (0, import_node_path5.dirname)(path);
6232
- if (!(0, import_node_fs6.existsSync)(dir)) (0, import_node_fs6.mkdirSync)(dir, { recursive: true, mode: 448 });
6526
+ const dir = (0, import_node_path4.dirname)(path);
6527
+ if (!(0, import_node_fs5.existsSync)(dir)) (0, import_node_fs5.mkdirSync)(dir, { recursive: true, mode: 448 });
6233
6528
  const body = JSON.stringify({ _warning: REGISTRY_WARNING, homes: file.homes }, null, 2);
6234
- (0, import_node_fs6.writeFileSync)(path, body, { encoding: "utf8", mode: 384 });
6529
+ (0, import_node_fs5.writeFileSync)(path, body, { encoding: "utf8", mode: 384 });
6235
6530
  try {
6236
- (0, import_node_fs6.chmodSync)(path, 384);
6531
+ (0, import_node_fs5.chmodSync)(path, 384);
6237
6532
  } catch {
6238
6533
  }
6239
6534
  }
@@ -6261,7 +6556,7 @@ function forgetHome(homePath) {
6261
6556
  return true;
6262
6557
  }
6263
6558
  function homeStillExists(homePath) {
6264
- return (0, import_node_fs6.existsSync)(`${homePath}/agents.json`);
6559
+ return (0, import_node_fs5.existsSync)(`${homePath}/agents.json`);
6265
6560
  }
6266
6561
 
6267
6562
  // src/commands/homes.ts
@@ -6288,27 +6583,27 @@ function runHomes(opts) {
6288
6583
  return;
6289
6584
  }
6290
6585
  if (rows.length === 0) {
6291
- console.log(import_chalk14.default.dim("(no HEYARP_HOME directories registered yet \u2014 run `heyarp register` once and the registry populates itself)"));
6292
- console.log(import_chalk14.default.dim(` registry path: ${homesRegistryPath()}`));
6586
+ console.log(import_chalk13.default.dim("(no HEYARP_HOME directories registered yet \u2014 run `heyarp register` once and the registry populates itself)"));
6587
+ console.log(import_chalk13.default.dim(` registry path: ${homesRegistryPath()}`));
6293
6588
  return;
6294
6589
  }
6295
6590
  const header = ["Path", "Agents", "Last seen", "Status"];
6296
6591
  const data = rows.map((r) => [
6297
- r.path + (r.isCurrent ? import_chalk14.default.green(" (current)") : ""),
6592
+ r.path + (r.isCurrent ? import_chalk13.default.green(" (current)") : ""),
6298
6593
  String(r.agentCount),
6299
6594
  formatRelativeTime(r.lastSeenAt),
6300
- r.exists ? import_chalk14.default.green("ok") : import_chalk14.default.red("missing")
6595
+ r.exists ? import_chalk13.default.green("ok") : import_chalk13.default.red("missing")
6301
6596
  ]);
6302
6597
  console.log("");
6303
6598
  console.log(formatTable(header, data));
6304
- console.log(import_chalk14.default.dim(`
6599
+ console.log(import_chalk13.default.dim(`
6305
6600
  Registry path: ${homesRegistryPath()}`));
6306
- console.log(import_chalk14.default.dim(`Set HEYARP_HOME=<path> in a shell to switch between homes; run \`heyarp homes forget <path>\` to drop a stale entry.`));
6601
+ console.log(import_chalk13.default.dim(`Set HEYARP_HOME=<path> in a shell to switch between homes; run \`heyarp homes forget <path>\` to drop a stale entry.`));
6307
6602
  }
6308
6603
  async function runForget(path, opts) {
6309
6604
  if (!opts.yes) {
6310
- console.log(import_chalk14.default.yellow(`About to remove '${path}' from the homes registry.`));
6311
- console.log(import_chalk14.default.dim(" Note: this only forgets the registry entry; the directory + its agents.json are NOT touched."));
6605
+ console.log(import_chalk13.default.yellow(`About to remove '${path}' from the homes registry.`));
6606
+ console.log(import_chalk13.default.dim(" Note: this only forgets the registry entry; the directory + its agents.json are NOT touched."));
6312
6607
  const answer = await (0, import_prompts.default)(
6313
6608
  {
6314
6609
  type: "confirm",
@@ -6318,28 +6613,28 @@ async function runForget(path, opts) {
6318
6613
  },
6319
6614
  {
6320
6615
  onCancel: () => {
6321
- console.log(import_chalk14.default.yellow("Aborted."));
6616
+ console.log(import_chalk13.default.yellow("Aborted."));
6322
6617
  process.exit(130);
6323
6618
  }
6324
6619
  }
6325
6620
  );
6326
6621
  if (!answer.confirm) {
6327
- console.log(import_chalk14.default.dim("Aborted (no changes)."));
6622
+ console.log(import_chalk13.default.dim("Aborted (no changes)."));
6328
6623
  return;
6329
6624
  }
6330
6625
  }
6331
6626
  const removed = forgetHome(path);
6332
6627
  if (removed) {
6333
- console.log(import_chalk14.default.green(`\u2713 forgot ${path}`));
6628
+ console.log(import_chalk13.default.green(`\u2713 forgot ${path}`));
6334
6629
  } else {
6335
- console.log(import_chalk14.default.dim(`(no entry for ${path} in the registry \u2014 already absent)`));
6630
+ console.log(import_chalk13.default.dim(`(no entry for ${path} in the registry \u2014 already absent)`));
6336
6631
  }
6337
6632
  }
6338
6633
  function countAgents(homePath) {
6339
- const file = (0, import_node_path6.join)(homePath, "agents.json");
6340
- if (!(0, import_node_fs7.existsSync)(file)) return 0;
6634
+ const file = (0, import_node_path5.join)(homePath, "agents.json");
6635
+ if (!(0, import_node_fs6.existsSync)(file)) return 0;
6341
6636
  try {
6342
- const parsed = JSON.parse((0, import_node_fs7.readFileSync)(file, "utf8"));
6637
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(file, "utf8"));
6343
6638
  if (!parsed || typeof parsed !== "object" || !parsed.servers) return 0;
6344
6639
  let total = 0;
6345
6640
  for (const server of Object.values(parsed.servers)) {
@@ -6357,13 +6652,13 @@ function formatTable(header, data) {
6357
6652
  const padding = " ".repeat(Math.max(0, widths[i] - lengths[i]));
6358
6653
  return cell + padding;
6359
6654
  }).join(" ");
6360
- const headerLine = import_chalk14.default.bold(
6655
+ const headerLine = import_chalk13.default.bold(
6361
6656
  pad(
6362
6657
  header,
6363
6658
  header.map((s) => s.length)
6364
6659
  )
6365
6660
  );
6366
- const sepLine = import_chalk14.default.dim(
6661
+ const sepLine = import_chalk13.default.dim(
6367
6662
  pad(
6368
6663
  widths.map((w) => "-".repeat(w)),
6369
6664
  widths
@@ -6390,7 +6685,7 @@ function formatRelativeTime(iso) {
6390
6685
  }
6391
6686
 
6392
6687
  // src/commands/inbox.ts
6393
- var import_chalk15 = __toESM(require("chalk"));
6688
+ var import_chalk14 = __toESM(require("chalk"));
6394
6689
  init_api();
6395
6690
  init_format();
6396
6691
  init_state();
@@ -6440,8 +6735,8 @@ async function runInbox(positionalDid, opts) {
6440
6735
  }
6441
6736
  const api = new ArpApiClient(opts.server);
6442
6737
  if (!opts.json) {
6443
- console.log(import_chalk15.default.dim(`Server: ${api.serverUrl}`));
6444
- console.log(import_chalk15.default.dim(`Signer: ${local.did}`));
6738
+ console.log(import_chalk14.default.dim(`Server: ${api.serverUrl}`));
6739
+ console.log(import_chalk14.default.dim(`Signer: ${local.did}`));
6445
6740
  }
6446
6741
  const query = { limit };
6447
6742
  if (opts.before) query.before = opts.before;
@@ -6455,7 +6750,7 @@ async function runInbox(positionalDid, opts) {
6455
6750
  return;
6456
6751
  }
6457
6752
  if (events.length === 0) {
6458
- console.log(import_chalk15.default.dim("\n(no events addressed to me \u2014 `heyarp events <relationship-id>` shows the chain-wide listing)"));
6753
+ console.log(import_chalk14.default.dim("\n(no events addressed to me \u2014 `heyarp events <relationship-id>` shows the chain-wide listing)"));
6459
6754
  return;
6460
6755
  }
6461
6756
  console.log("");
@@ -6470,16 +6765,16 @@ async function runInbox(positionalDid, opts) {
6470
6765
  secondary: `eventId=${ev.eventId} serverEventHash=${ev.serverEventHash}`
6471
6766
  }));
6472
6767
  }
6473
- const addressedToMeHint = import_chalk15.default.dim(" (envelopes addressed to me \u2014 for the full chain see `heyarp events <relationship-id>`)");
6768
+ const addressedToMeHint = import_chalk14.default.dim(" (envelopes addressed to me \u2014 for the full chain see `heyarp events <relationship-id>`)");
6474
6769
  if (opts.since && !opts.before) {
6475
6770
  console.log(
6476
- import_chalk15.default.dim(
6771
+ import_chalk14.default.dim(
6477
6772
  `
6478
6773
  ${events.length} event(s) (oldest-first).${addressedToMeHint} Advance the forward cursor with --since <serverTimestamp> --since-event-id <eventId> using the LAST row above.`
6479
6774
  )
6480
6775
  );
6481
6776
  } else {
6482
- console.log(import_chalk15.default.dim(`
6777
+ console.log(import_chalk14.default.dim(`
6483
6778
  ${events.length} event(s).${addressedToMeHint} Paginate with --before <serverTimestamp> --before-event-id <eventId> using the LAST row above.`));
6484
6779
  }
6485
6780
  }
@@ -6494,7 +6789,7 @@ async function runInboxTail(did, local, opts) {
6494
6789
  } else {
6495
6790
  warn(
6496
6791
  opts.json,
6497
- import_chalk15.default.yellow(
6792
+ import_chalk14.default.yellow(
6498
6793
  "\u26A0 inbox --tail: stdout is piped but `process.stdout._handle.setBlocking` is unavailable in this Node runtime. Buffered writes may delay event delivery. Fall back to polling (`heyarp inbox --json`) if events stop arriving."
6499
6794
  )
6500
6795
  );
@@ -6504,9 +6799,9 @@ async function runInboxTail(did, local, opts) {
6504
6799
  if (opts.json) {
6505
6800
  console.log(formatTailStartedPing({ server: api.serverUrl, signer: local.did, stdoutBlockingApplied }));
6506
6801
  } else {
6507
- console.log(import_chalk15.default.dim(`Server: ${api.serverUrl}`));
6508
- console.log(import_chalk15.default.dim(`Signer: ${local.did}`));
6509
- console.log(import_chalk15.default.dim("Mode: --tail (live SSE, Ctrl-C to stop)"));
6802
+ console.log(import_chalk14.default.dim(`Server: ${api.serverUrl}`));
6803
+ console.log(import_chalk14.default.dim(`Signer: ${local.did}`));
6804
+ console.log(import_chalk14.default.dim("Mode: --tail (live SSE, Ctrl-C to stop)"));
6510
6805
  }
6511
6806
  const controller = new AbortController();
6512
6807
  let userAborted = false;
@@ -6525,7 +6820,7 @@ async function runInboxTail(did, local, opts) {
6525
6820
  }
6526
6821
  if (event.type === "heartbeat") continue;
6527
6822
  if (event.type === "connected") {
6528
- console.log(import_chalk15.default.green("\u25CF stream open \u2014 listening for envelopes..."));
6823
+ console.log(import_chalk14.default.green("\u25CF stream open \u2014 listening for envelopes..."));
6529
6824
  continue;
6530
6825
  }
6531
6826
  if (event.type === "envelope") {
@@ -6539,7 +6834,7 @@ async function runInboxTail(did, local, opts) {
6539
6834
  }
6540
6835
  continue;
6541
6836
  }
6542
- console.log(import_chalk15.default.dim(`(unknown event: ${event.type})`));
6837
+ console.log(import_chalk14.default.dim(`(unknown event: ${event.type})`));
6543
6838
  }
6544
6839
  if (!userAborted) {
6545
6840
  throw new Error("inbox --tail: stream ended unexpectedly (server may have restarted, or change stream errored). Re-run to reconnect.");
@@ -6547,7 +6842,7 @@ async function runInboxTail(did, local, opts) {
6547
6842
  } catch (err) {
6548
6843
  const name = err.name;
6549
6844
  if (name === "AbortError" || userAborted) {
6550
- if (!opts.json) console.log(import_chalk15.default.dim("\nstream closed."));
6845
+ if (!opts.json) console.log(import_chalk14.default.dim("\nstream closed."));
6551
6846
  return;
6552
6847
  }
6553
6848
  throw err;
@@ -6567,15 +6862,15 @@ function formatInboxTable(events, opts = {}) {
6567
6862
  ]);
6568
6863
  const widths = header.map((h, i) => Math.max(h.length, ...data.map((row) => row[i].length)));
6569
6864
  const pad = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
6570
- const lines = [import_chalk15.default.bold(pad(header)), import_chalk15.default.dim(pad(widths.map((w) => "-".repeat(w)))), ...data.map((row) => pad(row))];
6571
- const detail = events.map((ev) => ` ${import_chalk15.default.dim("eventId:")} ${import_chalk15.default.cyan(ev.eventId)} ${import_chalk15.default.dim("serverTimestamp:")} ${import_chalk15.default.cyan(ev.serverTimestamp)}`).join("\n");
6865
+ const lines = [import_chalk14.default.bold(pad(header)), import_chalk14.default.dim(pad(widths.map((w) => "-".repeat(w)))), ...data.map((row) => pad(row))];
6866
+ const detail = events.map((ev) => ` ${import_chalk14.default.dim("eventId:")} ${import_chalk14.default.cyan(ev.eventId)} ${import_chalk14.default.dim("serverTimestamp:")} ${import_chalk14.default.cyan(ev.serverTimestamp)}`).join("\n");
6572
6867
  return `${lines.join("\n")}
6573
6868
 
6574
- ${import_chalk15.default.bold("Pagination cursors")} (last \u2192 first):
6869
+ ${import_chalk14.default.bold("Pagination cursors")} (last \u2192 first):
6575
6870
  ${detail}`;
6576
6871
  }
6577
6872
  function hashHead2(hash) {
6578
- if (!hash) return import_chalk15.default.dim("(none)");
6873
+ if (!hash) return import_chalk14.default.dim("(none)");
6579
6874
  if (hash.length <= 14) return hash;
6580
6875
  return `${hash.slice(0, 14)}...`;
6581
6876
  }
@@ -6590,35 +6885,35 @@ function parseLimit4(raw) {
6590
6885
 
6591
6886
  // src/commands/keys.ts
6592
6887
  var import_sdk8 = require("@heyanon-arp/sdk");
6593
- var import_chalk16 = __toESM(require("chalk"));
6888
+ var import_chalk15 = __toESM(require("chalk"));
6594
6889
  function registerKeysCommand(root) {
6595
6890
  const keys = root.command("keys").description("Local key utilities");
6596
6891
  keys.command("gen").description("Generate a fresh identity + settlement keypair (no save by default)").option("--save", "Reserved for future scratch-key storage; currently a no-op with a notice", false).action((opts) => {
6597
6892
  const identity = (0, import_sdk8.generateKeyPair)();
6598
6893
  const settlement = (0, import_sdk8.generateKeyPair)();
6599
6894
  const out = [
6600
- import_chalk16.default.bold("Identity key (Ed25519)"),
6601
- ` public (base58btc): ${import_chalk16.default.cyan((0, import_sdk8.base58btcEncode)(identity.publicKey))}`,
6602
- ` secret (base64) : ${import_chalk16.default.yellow(Buffer.from(identity.secretKey).toString("base64"))}`,
6895
+ import_chalk15.default.bold("Identity key (Ed25519)"),
6896
+ ` public (base58btc): ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(identity.publicKey))}`,
6897
+ ` secret (base64) : ${import_chalk15.default.yellow(Buffer.from(identity.secretKey).toString("base64"))}`,
6603
6898
  "",
6604
- import_chalk16.default.bold("Settlement key (Ed25519)"),
6605
- ` public (base58btc): ${import_chalk16.default.cyan((0, import_sdk8.base58btcEncode)(settlement.publicKey))}`,
6606
- ` secret (base64) : ${import_chalk16.default.yellow(Buffer.from(settlement.secretKey).toString("base64"))}`,
6899
+ import_chalk15.default.bold("Settlement key (Ed25519)"),
6900
+ ` public (base58btc): ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(settlement.publicKey))}`,
6901
+ ` secret (base64) : ${import_chalk15.default.yellow(Buffer.from(settlement.secretKey).toString("base64"))}`,
6607
6902
  "",
6608
- import_chalk16.default.bold("Resulting DID"),
6609
- ` ${import_chalk16.default.cyan((0, import_sdk8.formatDid)(identity.publicKey))}`
6903
+ import_chalk15.default.bold("Resulting DID"),
6904
+ ` ${import_chalk15.default.cyan((0, import_sdk8.formatDid)(identity.publicKey))}`
6610
6905
  ];
6611
6906
  console.log(out.join("\n"));
6612
6907
  if (opts.save) {
6613
- console.log(import_chalk16.default.yellow("\nNote: --save is not yet implemented. Capture the secret keys above before they scroll off-screen."));
6908
+ console.log(import_chalk15.default.yellow("\nNote: --save is not yet implemented. Capture the secret keys above before they scroll off-screen."));
6614
6909
  }
6615
6910
  });
6616
6911
  keys.command("whoami").description("Print the DID derived from a base64-encoded identity secret key").argument("<secret-key-b64>", "32-byte Ed25519 seed, base64").action((secretKeyB64) => {
6617
6912
  const seed = decodeSeed(secretKeyB64);
6618
6913
  const pub = (0, import_sdk8.getPublicKey)(seed);
6619
6914
  const did = (0, import_sdk8.formatDid)(pub);
6620
- console.log(`${import_chalk16.default.bold("DID")}: ${import_chalk16.default.cyan(did)}`);
6621
- console.log(`${import_chalk16.default.bold("Identity public key (base58btc)")}: ${import_chalk16.default.cyan((0, import_sdk8.base58btcEncode)(pub))}`);
6915
+ console.log(`${import_chalk15.default.bold("DID")}: ${import_chalk15.default.cyan(did)}`);
6916
+ console.log(`${import_chalk15.default.bold("Identity public key (base58btc)")}: ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(pub))}`);
6622
6917
  });
6623
6918
  }
6624
6919
  function decodeSeed(b64) {
@@ -6638,14 +6933,14 @@ function decodeSeed(b64) {
6638
6933
  init_lifecycle();
6639
6934
 
6640
6935
  // src/commands/list.ts
6641
- var import_chalk17 = __toESM(require("chalk"));
6936
+ var import_chalk16 = __toESM(require("chalk"));
6642
6937
  init_format();
6643
6938
  init_state();
6644
6939
  function registerListCommand(root) {
6645
6940
  root.command("list").description("List agents registered locally (~/.arp/agents.json)").action(() => {
6646
6941
  const rows = listAgents();
6647
6942
  if (rows.length === 0) {
6648
- console.log(import_chalk17.default.dim(`No local agents. State file: ${stateFilePath()}`));
6943
+ console.log(import_chalk16.default.dim(`No local agents. State file: ${stateFilePath()}`));
6649
6944
  return;
6650
6945
  }
6651
6946
  const grouped = /* @__PURE__ */ new Map();
@@ -6657,7 +6952,7 @@ function registerListCommand(root) {
6657
6952
  for (const [serverUrl, group] of grouped) {
6658
6953
  if (!first) console.log("");
6659
6954
  first = false;
6660
- console.log(import_chalk17.default.bold(`Server: ${serverUrl}`));
6955
+ console.log(import_chalk16.default.bold(`Server: ${serverUrl}`));
6661
6956
  console.log(formatAgentsTable(group.map(({ agent }) => ({ did: agent.did, name: agent.name, tags: agent.tags, registeredAt: agent.registeredAt }))));
6662
6957
  }
6663
6958
  });
@@ -6667,7 +6962,7 @@ function registerListCommand(root) {
6667
6962
  init_receipt();
6668
6963
 
6669
6964
  // src/commands/receipts.ts
6670
- var import_chalk20 = __toESM(require("chalk"));
6965
+ var import_chalk19 = __toESM(require("chalk"));
6671
6966
  init_api();
6672
6967
  init_format();
6673
6968
  init_state();
@@ -6699,9 +6994,9 @@ async function runReceipts(relationshipId, opts) {
6699
6994
  const api = new ArpApiClient(opts.server);
6700
6995
  const sender = resolveSenderAgent("receipts", opts.server, opts.fromDid);
6701
6996
  if (!opts.json) {
6702
- console.log(import_chalk20.default.dim(`Server: ${api.serverUrl}`));
6703
- console.log(import_chalk20.default.dim(`Signer: ${sender.did}`));
6704
- console.log(import_chalk20.default.dim(`Relationship: ${relationshipId}`));
6997
+ console.log(import_chalk19.default.dim(`Server: ${api.serverUrl}`));
6998
+ console.log(import_chalk19.default.dim(`Signer: ${sender.did}`));
6999
+ console.log(import_chalk19.default.dim(`Relationship: ${relationshipId}`));
6705
7000
  }
6706
7001
  const query = { limit };
6707
7002
  if (state) query.state = state;
@@ -6714,7 +7009,7 @@ async function runReceipts(relationshipId, opts) {
6714
7009
  return;
6715
7010
  }
6716
7011
  if (rows.length === 0) {
6717
- console.log(import_chalk20.default.dim("\n(no receipts for this relationship)"));
7012
+ console.log(import_chalk19.default.dim("\n(no receipts for this relationship)"));
6718
7013
  return;
6719
7014
  }
6720
7015
  console.log("");
@@ -6732,28 +7027,28 @@ async function runReceipts(relationshipId, opts) {
6732
7027
  }));
6733
7028
  }
6734
7029
  const lastId = rows[rows.length - 1].id;
6735
- console.log(import_chalk20.default.dim(`
7030
+ console.log(import_chalk19.default.dim(`
6736
7031
  ${rows.length} receipt row(s). Paginate with --after ${lastId}.`));
6737
7032
  }
6738
7033
  function formatReceiptLine(r, selfDid, opts = {}) {
6739
7034
  const delegationPart = opts.fullIds ? r.delegationId : idHead2(r.delegationId);
6740
7035
  const requestHashPart = opts.fullIds ? r.requestHash : hashHead3(r.requestHash);
6741
- const id = import_chalk20.default.bold(`${delegationPart}/${requestHashPart}`);
7036
+ const id = import_chalk19.default.bold(`${delegationPart}/${requestHashPart}`);
6742
7037
  const state = colorState2(r.state).padEnd(stateColumnWidth2());
6743
7038
  const callerHead = opts.fullIds ? r.callerDid : didHead3(r.callerDid);
6744
7039
  const payeeHead = opts.fullIds ? r.payeeDid : didHead3(r.payeeDid);
6745
- const direction = r.payeeDid === selfDid ? `${import_chalk20.default.bold("me")}(payee) \u2192 ${import_chalk20.default.dim(callerHead)}` : `${import_chalk20.default.dim(payeeHead)}(payee) \u2192 ${import_chalk20.default.bold("me")}`;
7040
+ const direction = r.payeeDid === selfDid ? `${import_chalk19.default.bold("me")}(payee) \u2192 ${import_chalk19.default.dim(callerHead)}` : `${import_chalk19.default.dim(payeeHead)}(payee) \u2192 ${import_chalk19.default.bold("me")}`;
6746
7041
  const verdict = formatVerdict(r);
6747
7042
  const responseTail = opts.fullIds ? `
6748
- ${import_chalk20.default.dim("responseHash:")} ${import_chalk20.default.cyan(r.responseHash)}` : "";
7043
+ ${import_chalk19.default.dim("responseHash:")} ${import_chalk19.default.cyan(r.responseHash)}` : "";
6749
7044
  return `${id} ${state} ${direction} ${verdict}${responseTail}`;
6750
7045
  }
6751
7046
  function colorState2(s) {
6752
7047
  switch (s) {
6753
7048
  case "proposed":
6754
- return import_chalk20.default.yellow("proposed");
7049
+ return import_chalk19.default.yellow("proposed");
6755
7050
  case "cosigned":
6756
- return import_chalk20.default.green("cosigned");
7051
+ return import_chalk19.default.green("cosigned");
6757
7052
  }
6758
7053
  }
6759
7054
  function stateColumnWidth2() {
@@ -6763,11 +7058,11 @@ function formatVerdict(r) {
6763
7058
  const final = r.verdictFinal ?? r.verdictProposed;
6764
7059
  switch (final) {
6765
7060
  case "accepted":
6766
- return import_chalk20.default.green("accepted");
7061
+ return import_chalk19.default.green("accepted");
6767
7062
  case "accepted_with_notes":
6768
- return import_chalk20.default.yellow("accepted_with_notes");
7063
+ return import_chalk19.default.yellow("accepted_with_notes");
6769
7064
  case "rejected":
6770
- return import_chalk20.default.red("rejected");
7065
+ return import_chalk19.default.red("rejected");
6771
7066
  }
6772
7067
  }
6773
7068
  function idHead2(id) {
@@ -6800,9 +7095,9 @@ function parseLimit5(raw) {
6800
7095
 
6801
7096
  // src/commands/register.ts
6802
7097
  var import_node_crypto = require("crypto");
6803
- var import_node_fs9 = require("fs");
7098
+ var import_node_fs8 = require("fs");
6804
7099
  var import_sdk12 = require("@heyanon-arp/sdk");
6805
- var import_chalk21 = __toESM(require("chalk"));
7100
+ var import_chalk20 = __toESM(require("chalk"));
6806
7101
  var import_prompts2 = __toESM(require("prompts"));
6807
7102
  init_api();
6808
7103
  init_format();
@@ -6835,22 +7130,15 @@ function registerRegisterCommand(root) {
6835
7130
  "--yes",
6836
7131
  "Strict non-interactive: fail if any required field is still missing after merging flags. With --yes, --password must be supplied explicitly (>= 8 chars).",
6837
7132
  false
6838
- ).option(
6839
- "--publication <state>",
6840
- "Post-register publication mode. 'active' (default) auto-publishes the agent into the discovery catalog so other agents can find it immediately via `heyarp agents`. 'draft' keeps it out of the catalog \u2014 curate the profile first, then run `heyarp publish <did>` manually.",
6841
- "active"
6842
7133
  ).option(
6843
7134
  "--json",
6844
7135
  // Emit a single JSON object instead of human-friendly
6845
7136
  // success prints. Output shape:
6846
7137
  // {did, settlementPublicKeyB58, identityPublicKeyB58,
6847
- // keyMode, publication, didDocument}
7138
+ // localStatePath, didDocument}
6848
7139
  // (V1-alpha: `accountId` parked from the output.)
6849
7140
  // REQUIRES --yes (interactive prompts would corrupt the
6850
- // single-doc contract). The auto-publish branch reports
6851
- // its outcome under `.publication` ('published', 'draft',
6852
- // or 'failed:<reason>') instead of emitting human-readable
6853
- // lines.
7141
+ // single-doc contract).
6854
7142
  "Machine-readable mode \u2014 emit `{did, settlementPublicKeyB58, \u2026, didDocument}` JSON instead of human prints. REQUIRES --yes (interactive prompts would corrupt the single-doc JSON output).",
6855
7143
  false
6856
7144
  ).action(async (opts) => {
@@ -6860,21 +7148,24 @@ function registerRegisterCommand(root) {
6860
7148
  function accumulate3(value, previous) {
6861
7149
  return [...previous, value];
6862
7150
  }
6863
- async function runRegister(opts) {
6864
- const publication = parsePublicationMode(opts.publication ?? "active");
7151
+ var defaultRegisterDeps = {
7152
+ makeApi: (serverOverride) => new ArpApiClient(serverOverride),
7153
+ save: saveAgent
7154
+ };
7155
+ async function runRegister(opts, deps = defaultRegisterDeps) {
6865
7156
  assertJsonRequiresYes(opts);
6866
- const api = new ArpApiClient(opts.server);
6867
- if (!opts.json) console.log(import_chalk21.default.dim(`Server: ${api.serverUrl}`));
7157
+ const api = deps.makeApi(opts.server);
7158
+ if (!opts.json) console.log(import_chalk20.default.dim(`Server: ${api.serverUrl}`));
6868
7159
  if (!opts.json) {
6869
7160
  warnIfAgentsAlreadyRegistered(opts.server);
6870
7161
  warnIfOrphanHomesPresent();
6871
7162
  }
6872
7163
  const keys = opts.fromKeys ? loadKeysFromFile(opts.fromKeys) : freshKeys();
6873
7164
  const did = (0, import_sdk12.formatDid)(keys.identityPublicKey);
6874
- if (!opts.json) console.log(import_chalk21.default.dim(`DID will be: ${did}`));
7165
+ if (!opts.json) console.log(import_chalk20.default.dim(`DID will be: ${did}`));
6875
7166
  const answers = await mergeAnswers(opts);
6876
7167
  const scryptSalt = (0, import_node_crypto.randomBytes)(16);
6877
- if (!opts.json) console.log(import_chalk21.default.dim("Deriving scrypt key, this may take a moment..."));
7168
+ if (!opts.json) console.log(import_chalk20.default.dim("Deriving scrypt key, this may take a moment..."));
6878
7169
  const scryptKey = (0, import_sdk12.deriveScryptKey)(answers.password, new Uint8Array(scryptSalt));
6879
7170
  const challenge = await api.issueChallenge("register");
6880
7171
  const challengeBytes = base64UrlNoPadDecode(challenge.challengeB64);
@@ -6890,14 +7181,11 @@ async function runRegister(opts) {
6890
7181
  });
6891
7182
  const settlementPublicKeyB58 = (0, import_sdk12.base58btcEncode)(keys.settlementPublicKey);
6892
7183
  const scryptSaltId = (0, import_sdk12.uuidV4)();
6893
- const ownerId = (0, import_sdk12.uuidV4)();
6894
7184
  const payload = {
6895
7185
  purpose: "ARP-KEY-LINK-v1",
6896
7186
  agent_did: did,
6897
7187
  identity_public_key: identityPublicKeyB58,
6898
7188
  settlement_public_key: settlementPublicKeyB58,
6899
- key_mode: "separated_soft",
6900
- owner_id: ownerId,
6901
7189
  owner_signing_method: "scrypt_password_proof",
6902
7190
  link_method: "manual",
6903
7191
  created_at: (0, import_sdk12.rfc3339)(),
@@ -6910,7 +7198,6 @@ async function runRegister(opts) {
6910
7198
  // accountId: answers.accountId,
6911
7199
  identityPublicKey: identityPublicKeyB58,
6912
7200
  settlementPublicKey: settlementPublicKeyB58,
6913
- keyMode: "separated_soft",
6914
7201
  ownerAttestation: {
6915
7202
  payload: attestation.payload,
6916
7203
  sig: attestation.sig,
@@ -6922,9 +7209,8 @@ async function runRegister(opts) {
6922
7209
  description: answers.description ? answers.description : void 0,
6923
7210
  tags: answers.tags.length > 0 ? answers.tags : void 0
6924
7211
  };
6925
- const result = await api.register(body);
6926
- saveAgent(opts.server, {
6927
- did: result.did,
7212
+ const durableState = {
7213
+ did,
6928
7214
  identityPublicKeyB58,
6929
7215
  identitySecretKeyB64: Buffer.from(keys.identitySecretKey).toString("base64"),
6930
7216
  settlementPublicKeyB58,
@@ -6932,60 +7218,45 @@ async function runRegister(opts) {
6932
7218
  scryptKeyB64: Buffer.from(scryptKey).toString("base64"),
6933
7219
  scryptSaltB64: Buffer.from(scryptSalt).toString("base64"),
6934
7220
  scryptSaltId,
6935
- ownerId,
6936
- // `metadata.owner_attestation_id` is the canonical name in the
6937
- // DID document — same value as server's `currentAttestationId`
6938
- // at the moment of registration.
6939
- currentAttestationId: result.didDocument.metadata.owner_attestation_id,
7221
+ // Placeholder until the server confirms registration and returns
7222
+ // the canonical attestation id (finalized in Step 8 below).
7223
+ currentAttestationId: "",
6940
7224
  // V1-alpha: accountId parked — local state file no longer
6941
7225
  // records the field. Existing state files written when the
6942
7226
  // field was required will still carry the value; readers
6943
7227
  // tolerate either shape.
6944
7228
  // accountId: answers.accountId,
6945
- keyMode: "separated_soft",
6946
7229
  name: answers.name,
6947
7230
  description: answers.description || void 0,
6948
7231
  tags: answers.tags.length > 0 ? answers.tags : void 0,
6949
7232
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
7233
+ };
7234
+ deps.save(opts.server, durableState);
7235
+ const result = await api.register(body);
7236
+ if (result.did !== did) {
7237
+ throw new Error(
7238
+ `register: server returned DID ${result.did} but the locally-derived DID is ${did}. These must be identical (both base58btc of the identity public key). Refusing to finalize \u2014 the secret keys are already saved locally under ${did}.`
7239
+ );
7240
+ }
7241
+ deps.save(opts.server, {
7242
+ ...durableState,
7243
+ currentAttestationId: result.didDocument.metadata.owner_attestation_id
6950
7244
  });
6951
7245
  try {
6952
7246
  recordHome(arpHomeDir());
6953
7247
  } catch (registryErr) {
6954
- if (!opts.json) console.log(import_chalk21.default.dim(`(homes registry write failed: ${registryErr.message})`));
7248
+ if (!opts.json) console.log(import_chalk20.default.dim(`(homes registry write failed: ${registryErr.message})`));
6955
7249
  }
6956
7250
  if (!opts.json) {
6957
- console.log(import_chalk21.default.green("\nRegistered."));
6958
- console.log(`${import_chalk21.default.bold("DID")}: ${import_chalk21.default.cyan(result.did)}`);
6959
- console.log(`${import_chalk21.default.bold("Settlement pubkey")} ${import_chalk21.default.dim("(fund with SOL)")}: ${import_chalk21.default.cyan(settlementPublicKeyB58)}`);
6960
- console.log(import_chalk21.default.bold("DID document:"));
7251
+ console.log(import_chalk20.default.green("\nRegistered."));
7252
+ console.log(`${import_chalk20.default.bold("DID")}: ${import_chalk20.default.cyan(result.did)}`);
7253
+ console.log(`${import_chalk20.default.bold("Settlement pubkey")} ${import_chalk20.default.dim("(fund with SOL)")}: ${import_chalk20.default.cyan(settlementPublicKeyB58)}`);
7254
+ console.log(import_chalk20.default.bold("DID document:"));
6961
7255
  console.log(formatJson(result.didDocument));
6962
7256
  }
6963
- const decision = decideAutoPublish({ publication });
6964
- let publicationOutcome;
6965
- if (decision === "try") {
6966
- try {
6967
- const signer = makeSignerFromSecret(keys.identitySecretKey, result.did);
6968
- await api.publishAgent(result.did, signer);
6969
- publicationOutcome = "published";
6970
- if (!opts.json) console.log(import_chalk21.default.green(`
6971
- \u2713 Published \u2014 discoverable now via \`heyarp agents\`.`));
6972
- } catch (err) {
6973
- publicationOutcome = `failed: ${err.message}`;
6974
- if (!opts.json)
6975
- console.log(
6976
- import_chalk21.default.yellow(`
6977
- \u26A0 Auto-publish failed (${err.message}). Agent saved as DRAFT \u2014 run \`heyarp publish ${result.did}\` to make it discoverable.`)
6978
- );
6979
- }
6980
- } else {
6981
- publicationOutcome = "draft";
6982
- if (!opts.json) {
6983
- console.log(import_chalk21.default.dim(`
6984
- Publication mode: DRAFT. Agent is registered but NOT discoverable via \`heyarp agents\`.`));
6985
- console.log(import_chalk21.default.dim(`Run \`heyarp publish ${result.did}\` when ready to appear in the catalog.`));
6986
- }
6987
- }
6988
- if (!opts.json) console.log(import_chalk21.default.dim(`
7257
+ if (!opts.json) console.log(import_chalk20.default.green(`
7258
+ \u2713 Discoverable now via \`heyarp agents\`.`));
7259
+ if (!opts.json) console.log(import_chalk20.default.dim(`
6989
7260
  Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
6990
7261
  if (opts.json) {
6991
7262
  console.log(
@@ -6997,8 +7268,6 @@ Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
6997
7268
  // V1-alpha: accountId parked — `--json` output no longer
6998
7269
  // includes it. Uncomment when launchpad↔ARP join lands.
6999
7270
  // accountId: answers.accountId,
7000
- keyMode: "separated_soft",
7001
- publication: publicationOutcome,
7002
7271
  localStatePath: `${arpHomeDir()}/agents.json`,
7003
7272
  didDocument: result.didDocument
7004
7273
  },
@@ -7065,7 +7334,7 @@ async function mergeAnswers(opts) {
7065
7334
  }
7066
7335
  const prompted = promptDefs.length > 0 ? await (0, import_prompts2.default)(promptDefs, {
7067
7336
  onCancel: () => {
7068
- console.log(import_chalk21.default.yellow("\nAborted."));
7337
+ console.log(import_chalk20.default.yellow("\nAborted."));
7069
7338
  process.exit(130);
7070
7339
  }
7071
7340
  }) : {};
@@ -7087,16 +7356,16 @@ function warnIfOrphanHomesPresent() {
7087
7356
  try {
7088
7357
  others = listHomes().filter((h) => h.path !== current);
7089
7358
  } catch (registryErr) {
7090
- console.log(import_chalk21.default.dim(`(homes registry unreadable, skipping orphan-home check: ${registryErr.message})`));
7359
+ console.log(import_chalk20.default.dim(`(homes registry unreadable, skipping orphan-home check: ${registryErr.message})`));
7091
7360
  return;
7092
7361
  }
7093
7362
  if (others.length === 0) return;
7094
- const list = others.map((h) => ` \u2022 ${import_chalk21.default.cyan(h.path)} ${import_chalk21.default.dim(`(last seen ${h.lastSeenAt})`)}`).join("\n");
7095
- console.log(import_chalk21.default.yellow(`
7363
+ const list = others.map((h) => ` \u2022 ${import_chalk20.default.cyan(h.path)} ${import_chalk20.default.dim(`(last seen ${h.lastSeenAt})`)}`).join("\n");
7364
+ console.log(import_chalk20.default.yellow(`
7096
7365
  \u26A0 HEYARP_HOME is unset, but other agent homes are registered on this machine:`));
7097
7366
  console.log(list);
7098
7367
  console.log(
7099
- import_chalk21.default.dim(
7368
+ import_chalk20.default.dim(
7100
7369
  ` Registering will create a NEW agent under ${current}.
7101
7370
  If you meant to add to an existing home, abort (Ctrl-C) and re-run with:
7102
7371
  HEYARP_HOME=<path> heyarp register \u2026
@@ -7109,11 +7378,11 @@ function warnIfAgentsAlreadyRegistered(serverOverride) {
7109
7378
  const targetServer = resolveServerUrl(serverOverride);
7110
7379
  const existing = listAgents().filter((row) => row.serverUrl === targetServer);
7111
7380
  if (existing.length === 0) return;
7112
- const list = existing.map((row) => ` \u2022 ${import_chalk21.default.cyan(row.agent.did)}${row.agent.name ? import_chalk21.default.dim(` (${row.agent.name})`) : ""}`).join("\n");
7113
- console.log(import_chalk21.default.yellow("\n\u26A0 ~/.arp/agents.json already has agent(s) for this server:"));
7381
+ const list = existing.map((row) => ` \u2022 ${import_chalk20.default.cyan(row.agent.did)}${row.agent.name ? import_chalk20.default.dim(` (${row.agent.name})`) : ""}`).join("\n");
7382
+ console.log(import_chalk20.default.yellow("\n\u26A0 ~/.arp/agents.json already has agent(s) for this server:"));
7114
7383
  console.log(list);
7115
7384
  console.log(
7116
- import_chalk21.default.dim(
7385
+ import_chalk20.default.dim(
7117
7386
  " After this register completes, you will have multiple local DIDs sharing one state file.\n To keep their state isolated, run with HEYARP_HOME pointing at a per-agent dir, e.g.\n HEYARP_HOME=/tmp/agent-alice heyarp register \u2026\n Otherwise, pass --from-did <did> explicitly on every signed command.\n"
7118
7387
  )
7119
7388
  );
@@ -7133,12 +7402,12 @@ function freshKeys() {
7133
7402
  };
7134
7403
  }
7135
7404
  function loadKeysFromFile(path) {
7136
- if (!(0, import_node_fs9.existsSync)(path)) {
7405
+ if (!(0, import_node_fs8.existsSync)(path)) {
7137
7406
  throw new Error(`--from-keys: file not found at ${path}`);
7138
7407
  }
7139
7408
  let parsed;
7140
7409
  try {
7141
- parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
7410
+ parsed = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
7142
7411
  } catch (err) {
7143
7412
  throw new Error(`--from-keys: ${path} is not valid JSON: ${err.message}`);
7144
7413
  }
@@ -7166,20 +7435,9 @@ function assertJsonRequiresYes(opts) {
7166
7435
  throw new Error("register --json REQUIRES --yes (interactive prompts would corrupt the single-doc JSON output)");
7167
7436
  }
7168
7437
  }
7169
- function parsePublicationMode(raw) {
7170
- if (raw === "active" || raw === "draft") return raw;
7171
- throw new Error(`register: --publication must be 'active' or 'draft' (got '${raw}')`);
7172
- }
7173
- function decideAutoPublish(input) {
7174
- if (input.publication !== "active") return "skip-draft";
7175
- return "try";
7176
- }
7177
- function makeSignerFromSecret(identitySecretKey, did) {
7178
- return { did, identitySecretKey };
7179
- }
7180
7438
 
7181
7439
  // src/commands/relationships.ts
7182
- var import_chalk22 = __toESM(require("chalk"));
7440
+ var import_chalk21 = __toESM(require("chalk"));
7183
7441
  init_api();
7184
7442
  init_format();
7185
7443
  init_state();
@@ -7202,25 +7460,25 @@ async function runRelationships(positionalDid, opts) {
7202
7460
  const local = explicitDid !== void 0 ? loadAgentOrThrow(opts.server, explicitDid) : resolveSenderAgent("relationships", opts.server, void 0);
7203
7461
  const did = local.did;
7204
7462
  const api = new ArpApiClient(opts.server);
7205
- console.log(import_chalk22.default.dim(`Server: ${api.serverUrl}`));
7206
- console.log(import_chalk22.default.dim(`Signer: ${local.did}`));
7463
+ console.log(import_chalk21.default.dim(`Server: ${api.serverUrl}`));
7464
+ console.log(import_chalk21.default.dim(`Signer: ${local.did}`));
7207
7465
  const query = { limit };
7208
7466
  if (state) query.state = state;
7209
7467
  const signer = makeSigner(local);
7210
7468
  const rows = await api.listRelationships(did, signer, query);
7211
7469
  if (rows.length === 0) {
7212
- console.log(import_chalk22.default.dim("\n(no relationships)"));
7470
+ console.log(import_chalk21.default.dim("\n(no relationships)"));
7213
7471
  return;
7214
7472
  }
7215
7473
  console.log("");
7216
7474
  console.log(formatRelationshipsTable(rows, did));
7217
7475
  if (opts.verbose) {
7218
- console.log(import_chalk22.default.bold("\nFull relationships:"));
7476
+ console.log(import_chalk21.default.bold("\nFull relationships:"));
7219
7477
  for (const r of rows) {
7220
7478
  console.log(formatJson(r));
7221
7479
  }
7222
7480
  }
7223
- console.log(import_chalk22.default.dim(`
7481
+ console.log(import_chalk21.default.dim(`
7224
7482
  ${rows.length} relationship(s).`));
7225
7483
  }
7226
7484
  function formatRelationshipsTable(rows, selfDid) {
@@ -7228,7 +7486,7 @@ function formatRelationshipsTable(rows, selfDid) {
7228
7486
  const data = rows.map((r) => [r.relationshipId, otherPair(r, selfDid), r.state, r.lastEventAt ?? "(none)", String(r.lastEventIndex)]);
7229
7487
  const widths = header.map((h, i) => Math.max(h.length, ...data.map((row) => row[i].length)));
7230
7488
  const pad = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
7231
- return [import_chalk22.default.bold(pad(header)), import_chalk22.default.dim(pad(widths.map((w) => "-".repeat(w)))), ...data.map((row) => pad(row))].join("\n");
7489
+ return [import_chalk21.default.bold(pad(header)), import_chalk21.default.dim(pad(widths.map((w) => "-".repeat(w)))), ...data.map((row) => pad(row))].join("\n");
7232
7490
  }
7233
7491
  function otherPair(r, selfDid) {
7234
7492
  if (r.pairDidA === selfDid) return r.pairDidB;
@@ -7251,141 +7509,9 @@ function parseLimit6(raw) {
7251
7509
  return n;
7252
7510
  }
7253
7511
 
7254
- // src/commands/rotate.ts
7255
- var import_sdk13 = require("@heyanon-arp/sdk");
7256
- var import_chalk23 = __toESM(require("chalk"));
7257
- var import_prompts3 = __toESM(require("prompts"));
7258
- init_api();
7259
- init_format();
7260
- init_state();
7261
- init_lifecycle();
7262
- var ROTATION_REASONS = ["scheduled", "compromise", "lost_device", "other"];
7263
- function registerRotateCommand(root) {
7264
- root.command("rotate").description("Rotate the agent identity key (DID stays frozen). Requires local state with ownerId + currentAttestationId.").argument("<did>").option("--server <url>", "Override ARP server base URL").option("--yes", "Skip the destructive-action confirmation prompt", false).option("--reason <s>", `Rotation reason \u2014 one of ${ROTATION_REASONS.join(" | ")}`, "scheduled").action(async (did, opts) => {
7265
- if (opts.reason && !ROTATION_REASONS.includes(opts.reason)) {
7266
- throw new Error(`rotate: --reason must be one of ${ROTATION_REASONS.join(", ")} (got '${opts.reason}')`);
7267
- }
7268
- await runRotate(did, opts);
7269
- });
7270
- }
7271
- async function runRotate(did, opts) {
7272
- const local = loadAgentOrThrow(opts.server, did);
7273
- if (!local.ownerId || !local.currentAttestationId) {
7274
- throw new Error("rotate: local state is missing ownerId / currentAttestationId. State predates the rotation flow \u2014 please re-register.");
7275
- }
7276
- const api = new ArpApiClient(opts.server);
7277
- console.log(import_chalk23.default.dim(`Server: ${api.serverUrl}`));
7278
- console.log(import_chalk23.default.dim(`Rotating: ${local.did}`));
7279
- if (!opts.yes) {
7280
- const confirm = await (0, import_prompts3.default)({
7281
- type: "confirm",
7282
- name: "go",
7283
- message: "Rotation invalidates the CURRENT private key the moment the server commits. Continue?",
7284
- initial: false
7285
- });
7286
- if (!confirm.go) {
7287
- console.log(import_chalk23.default.yellow("Aborted."));
7288
- return;
7289
- }
7290
- }
7291
- const next = (0, import_sdk13.generateKeyPair)();
7292
- const newIdentityPublicKeyB58 = (0, import_sdk13.base58btcEncode)(next.publicKey);
7293
- const challenge = await api.issueChallenge("rotate");
7294
- const challengeBytes = base64UrlNoPadDecode2(challenge.challengeB64);
7295
- if (challengeBytes.length !== 32) {
7296
- throw new Error(`Server returned a ${challengeBytes.length}-byte challenge; expected 32`);
7297
- }
7298
- const challengeSig = (0, import_sdk13.signChallenge)(challengeBytes, next.secretKey);
7299
- await api.submitChallengeResponse({
7300
- challengeId: challenge.challengeId,
7301
- identityPublicKey: newIdentityPublicKeyB58,
7302
- signature: Buffer.from(challengeSig).toString("base64")
7303
- });
7304
- const scryptKey = new Uint8Array(Buffer.from(local.scryptKeyB64, "base64"));
7305
- if (scryptKey.length !== 32) {
7306
- throw new Error("rotate: stored scryptKeyB64 is not 32 bytes \u2014 local state is corrupted, re-register.");
7307
- }
7308
- const reason = opts.reason ?? "scheduled";
7309
- const payload = {
7310
- purpose: "ARP-KEY-ROTATION-v1",
7311
- agent_did: did,
7312
- current_identity_public_key: local.identityPublicKeyB58,
7313
- new_identity_public_key: newIdentityPublicKeyB58,
7314
- settlement_public_key: local.settlementPublicKeyB58,
7315
- supersedes_attestation_id: local.currentAttestationId,
7316
- owner_id: local.ownerId,
7317
- owner_signing_method: "scrypt_password_proof",
7318
- rotation_reason: reason,
7319
- created_at: (0, import_sdk13.rfc3339)(),
7320
- nonce: (0, import_sdk13.senderNonce)()
7321
- };
7322
- const attestation = (0, import_sdk13.signKeyRotationAttestation)({
7323
- payload,
7324
- scryptKey,
7325
- scryptSaltId: local.scryptSaltId
7326
- });
7327
- const newIdentitySecretKeyB64 = Buffer.from(next.secretKey).toString("base64");
7328
- updateAgentLocal(opts.server, did, {
7329
- pendingRotation: {
7330
- newIdentityPublicKeyB58,
7331
- newIdentitySecretKeyB64,
7332
- issuedAt: (/* @__PURE__ */ new Date()).toISOString()
7333
- }
7334
- });
7335
- const body = {
7336
- challengeId: challenge.challengeId,
7337
- newIdentityPublicKey: newIdentityPublicKeyB58,
7338
- newKeyAttestation: {
7339
- payload: attestation.payload,
7340
- sig: attestation.sig,
7341
- scrypt_salt_id: attestation.scrypt_salt_id
7342
- }
7343
- };
7344
- const signer = makeSigner(local);
7345
- let updated;
7346
- try {
7347
- updated = await api.rotateIdentityKey(did, body, signer);
7348
- } catch (err) {
7349
- try {
7350
- updateAgentLocal(opts.server, did, { pendingRotation: void 0 });
7351
- } catch {
7352
- }
7353
- throw err;
7354
- }
7355
- try {
7356
- updateAgentLocal(opts.server, did, {
7357
- identityPublicKeyB58: newIdentityPublicKeyB58,
7358
- identitySecretKeyB64: newIdentitySecretKeyB64,
7359
- currentAttestationId: updated.currentAttestationId,
7360
- pendingRotation: void 0
7361
- });
7362
- } catch (err) {
7363
- console.error(import_chalk23.default.red("\nServer rotation succeeded but local state write failed."));
7364
- console.error(import_chalk23.default.red("Capture these values now \u2014 the new key is already live server-side:"));
7365
- console.error(` ${import_chalk23.default.bold("identityPublicKeyB58")} : ${newIdentityPublicKeyB58}`);
7366
- console.error(` ${import_chalk23.default.bold("identitySecretKeyB64")} : ${newIdentitySecretKeyB64}`);
7367
- console.error(` ${import_chalk23.default.bold("currentAttestationId")} : ${updated.currentAttestationId}`);
7368
- console.error(import_chalk23.default.dim(`(Also persisted in pendingRotation at ~/.arp/agents.json before the server call.)`));
7369
- console.error(import_chalk23.default.dim(`Underlying error: ${err.message}`));
7370
- throw err;
7371
- }
7372
- console.log(import_chalk23.default.green("\nRotated."));
7373
- console.log(`${import_chalk23.default.bold("DID")}: ${import_chalk23.default.cyan(updated.did)} ${import_chalk23.default.dim("(unchanged)")}`);
7374
- console.log(`${import_chalk23.default.bold("New identity public key")}: ${import_chalk23.default.cyan(newIdentityPublicKeyB58)}`);
7375
- console.log(`${import_chalk23.default.bold("New attestation id")}: ${import_chalk23.default.cyan(updated.currentAttestationId)}`);
7376
- console.log(import_chalk23.default.bold("\nAgent profile:"));
7377
- console.log(formatJson(updated));
7378
- console.log(import_chalk23.default.dim("\nLocal state updated; old private key is no longer valid."));
7379
- }
7380
- function base64UrlNoPadDecode2(s) {
7381
- const replaced = s.replace(/-/g, "+").replace(/_/g, "/");
7382
- const padded = replaced + "==".slice(0, (4 - replaced.length % 4) % 4);
7383
- return new Uint8Array(Buffer.from(padded, "base64"));
7384
- }
7385
-
7386
7512
  // src/commands/send-handshake.ts
7387
- var import_sdk14 = require("@heyanon-arp/sdk");
7388
- var import_chalk24 = __toESM(require("chalk"));
7513
+ var import_sdk13 = require("@heyanon-arp/sdk");
7514
+ var import_chalk22 = __toESM(require("chalk"));
7389
7515
  init_api();
7390
7516
  init_format();
7391
7517
  init_state();
@@ -7401,10 +7527,10 @@ async function runSendHandshake(recipientDid, opts) {
7401
7527
  }
7402
7528
  const ttlSeconds = parseTtl3(opts.ttl);
7403
7529
  const api = new ArpApiClient(opts.server);
7404
- console.log(import_chalk24.default.dim(`Server: ${api.serverUrl}`));
7530
+ console.log(import_chalk22.default.dim(`Server: ${api.serverUrl}`));
7405
7531
  const sender = resolveSenderAgent("send-handshake", opts.server, opts.fromDid);
7406
- console.log(import_chalk24.default.dim(`Sender: ${sender.did}`));
7407
- console.log(import_chalk24.default.dim(`Recipient: ${recipientDid}`));
7532
+ console.log(import_chalk22.default.dim(`Sender: ${sender.did}`));
7533
+ console.log(import_chalk22.default.dim(`Recipient: ${recipientDid}`));
7408
7534
  const content = {};
7409
7535
  if (opts.greeting) content.greeting = opts.greeting;
7410
7536
  if (opts.intent) content.intent = opts.intent;
@@ -7412,46 +7538,46 @@ async function runSendHandshake(recipientDid, opts) {
7412
7538
  const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
7413
7539
  const protectedBlock = {
7414
7540
  protocol_version: "arp/0.1",
7415
- purpose: import_sdk14.Purpose.ENVELOPE,
7416
- message_id: (0, import_sdk14.uuidV4)(),
7541
+ purpose: import_sdk13.Purpose.ENVELOPE,
7542
+ message_id: (0, import_sdk13.uuidV4)(),
7417
7543
  sender_did: sender.did,
7418
7544
  recipient_did: recipientDid,
7419
7545
  relationship_id: null,
7420
7546
  sender_sequence: nextSequence,
7421
- sender_nonce: (0, import_sdk14.senderNonce)(),
7422
- timestamp: (0, import_sdk14.rfc3339)(),
7423
- expires_at: (0, import_sdk14.expiresAt)(ttlSeconds),
7547
+ sender_nonce: (0, import_sdk13.senderNonce)(),
7548
+ timestamp: (0, import_sdk13.rfc3339)(),
7549
+ expires_at: (0, import_sdk13.expiresAt)(ttlSeconds),
7424
7550
  delivery_id: null
7425
7551
  };
7426
7552
  const signer = makeSigner(sender);
7427
- const envelope = (0, import_sdk14.signEnvelope)({
7553
+ const envelope = (0, import_sdk13.signEnvelope)({
7428
7554
  protected: protectedBlock,
7429
7555
  body,
7430
7556
  identitySecretKey: signer.identitySecretKey
7431
7557
  });
7432
7558
  if (opts.verbose) {
7433
- console.log(import_chalk24.default.bold("\nEnvelope (pre-send):"));
7559
+ console.log(import_chalk22.default.bold("\nEnvelope (pre-send):"));
7434
7560
  console.log(formatJson(envelope));
7435
7561
  }
7436
7562
  const result = await api.ingest(envelope);
7437
7563
  updateAgentLocal(opts.server, sender.did, { lastSenderSequence: nextSequence });
7438
- console.log(import_chalk24.default.green("\nDelivered."));
7439
- console.log(`${import_chalk24.default.bold("Event id")}: ${import_chalk24.default.cyan(result.eventId)}`);
7440
- console.log(`${import_chalk24.default.bold("Relationship id")}: ${import_chalk24.default.cyan(result.relationshipId)}`);
7441
- console.log(`${import_chalk24.default.bold("Chain index")}: ${import_chalk24.default.cyan(String(result.relationshipEventIndex))}`);
7442
- console.log(`${import_chalk24.default.bold("Server timestamp")}: ${import_chalk24.default.cyan(result.serverTimestamp)}`);
7443
- console.log(`${import_chalk24.default.bold("Signed message hash")}: ${import_chalk24.default.cyan(result.signedMessageHash)}`);
7444
- console.log(`${import_chalk24.default.bold("Server event hash")}: ${import_chalk24.default.cyan(result.serverEventHash)}`);
7564
+ console.log(import_chalk22.default.green("\nDelivered."));
7565
+ console.log(`${import_chalk22.default.bold("Event id")}: ${import_chalk22.default.cyan(result.eventId)}`);
7566
+ console.log(`${import_chalk22.default.bold("Relationship id")}: ${import_chalk22.default.cyan(result.relationshipId)}`);
7567
+ console.log(`${import_chalk22.default.bold("Chain index")}: ${import_chalk22.default.cyan(String(result.relationshipEventIndex))}`);
7568
+ console.log(`${import_chalk22.default.bold("Server timestamp")}: ${import_chalk22.default.cyan(result.serverTimestamp)}`);
7569
+ console.log(`${import_chalk22.default.bold("Signed message hash")}: ${import_chalk22.default.cyan(result.signedMessageHash)}`);
7570
+ console.log(`${import_chalk22.default.bold("Server event hash")}: ${import_chalk22.default.cyan(result.serverEventHash)}`);
7445
7571
  if (result.prevServerEventHash) {
7446
- console.log(`${import_chalk24.default.bold("Prev server event hash")}: ${import_chalk24.default.cyan(result.prevServerEventHash)}`);
7572
+ console.log(`${import_chalk22.default.bold("Prev server event hash")}: ${import_chalk22.default.cyan(result.prevServerEventHash)}`);
7447
7573
  } else {
7448
- console.log(`${import_chalk24.default.bold("Prev server event hash")}: ${import_chalk24.default.dim("(null \u2014 first event of this relationship)")}`);
7574
+ console.log(`${import_chalk22.default.bold("Prev server event hash")}: ${import_chalk22.default.dim("(null \u2014 first event of this relationship)")}`);
7449
7575
  }
7450
7576
  if (opts.verbose) {
7451
- console.log(import_chalk24.default.bold("\nFull server response:"));
7577
+ console.log(import_chalk22.default.bold("\nFull server response:"));
7452
7578
  console.log(formatJson(result));
7453
7579
  }
7454
- console.log(import_chalk24.default.dim(`
7580
+ console.log(import_chalk22.default.dim(`
7455
7581
  Local sender_sequence advanced to ${nextSequence}.`));
7456
7582
  }
7457
7583
  function parseTtl3(raw) {
@@ -7467,8 +7593,8 @@ function isDid(s) {
7467
7593
  }
7468
7594
 
7469
7595
  // src/commands/send-handshake-response.ts
7470
- var import_sdk15 = require("@heyanon-arp/sdk");
7471
- var import_chalk25 = __toESM(require("chalk"));
7596
+ var import_sdk14 = require("@heyanon-arp/sdk");
7597
+ var import_chalk23 = __toESM(require("chalk"));
7472
7598
  init_api();
7473
7599
  init_format();
7474
7600
  init_state();
@@ -7482,7 +7608,7 @@ function registerSendHandshakeResponseCommand(root) {
7482
7608
  // We don't use commander's requiredOption because it
7483
7609
  // would fire for the accept path too; validate manually
7484
7610
  // after decision is parsed.
7485
- `When --decision=decline: required reason code (one of: ${import_sdk15.DECLINE_REASONS.join(", ")}). Carried in body.content.reason.`
7611
+ `When --decision=decline: required reason code (one of: ${import_sdk14.DECLINE_REASONS.join(", ")}). Carried in body.content.reason.`
7486
7612
  ).option("--reason-detail <s>", "Optional free-text elaboration alongside --reason (max 512 chars).").option("--ttl <seconds>", "Envelope TTL in seconds (max 86400 = 24h)", "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
7487
7613
  "--json",
7488
7614
  "Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, decision, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash, prevServerEventHash}; idempotent short-circuit adds {idempotent:true}). Prelude + banners move off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.",
@@ -7513,11 +7639,11 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7513
7639
  declinePayload = detail ? { reason, reasonDetail: detail } : { reason };
7514
7640
  }
7515
7641
  const api = new ArpApiClient(opts.server);
7516
- progress(opts.json, import_chalk25.default.dim(`Server: ${api.serverUrl}`));
7642
+ progress(opts.json, import_chalk23.default.dim(`Server: ${api.serverUrl}`));
7517
7643
  const sender = resolveSenderAgent("send-handshake-response", opts.server, opts.fromDid);
7518
- progress(opts.json, import_chalk25.default.dim(`Sender: ${sender.did}`));
7519
- progress(opts.json, import_chalk25.default.dim(`Recipient: ${recipientDid}`));
7520
- progress(opts.json, import_chalk25.default.dim(`Decision: ${decision}`));
7644
+ progress(opts.json, import_chalk23.default.dim(`Sender: ${sender.did}`));
7645
+ progress(opts.json, import_chalk23.default.dim(`Recipient: ${recipientDid}`));
7646
+ progress(opts.json, import_chalk23.default.dim(`Decision: ${decision}`));
7521
7647
  const signer = makeSigner(sender);
7522
7648
  if (!opts.force) {
7523
7649
  try {
@@ -7546,13 +7672,13 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7546
7672
  });
7547
7673
  return;
7548
7674
  }
7549
- progress(opts.json, import_chalk25.default.yellow(`
7675
+ progress(opts.json, import_chalk23.default.yellow(`
7550
7676
  [--idempotency] Relationship ${existing.relationshipId} with ${recipientDid} is already 'active'.`));
7551
7677
  progress(
7552
7678
  opts.json,
7553
- import_chalk25.default.dim(`A previous accept from this signer (event ${previousResponseFromMe.eventId}) landed successfully. Skipping re-send (use --force to override).`)
7679
+ import_chalk23.default.dim(`A previous accept from this signer (event ${previousResponseFromMe.eventId}) landed successfully. Skipping re-send (use --force to override).`)
7554
7680
  );
7555
- progress(opts.json, import_chalk25.default.dim(`Last event index: ${existing.lastEventIndex}, last server event hash: ${existing.lastServerEventHash ?? "(none)"}`));
7681
+ progress(opts.json, import_chalk23.default.dim(`Last event index: ${existing.lastEventIndex}, last server event hash: ${existing.lastServerEventHash ?? "(none)"}`));
7556
7682
  return;
7557
7683
  }
7558
7684
  throw new Error(
@@ -7563,7 +7689,7 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7563
7689
  if (probeErr instanceof Error && /CLOSED|terminated|already 'active' from a previous ACCEPT|original initiator/i.test(probeErr.message)) {
7564
7690
  throw probeErr;
7565
7691
  }
7566
- progress(opts.json, import_chalk25.default.dim(`(idempotency probe failed; proceeding anyway: ${probeErr.message})`));
7692
+ progress(opts.json, import_chalk23.default.dim(`(idempotency probe failed; proceeding anyway: ${probeErr.message})`));
7567
7693
  }
7568
7694
  }
7569
7695
  const content = { decision };
@@ -7576,24 +7702,24 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7576
7702
  const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
7577
7703
  const protectedBlock = {
7578
7704
  protocol_version: "arp/0.1",
7579
- purpose: import_sdk15.Purpose.ENVELOPE,
7580
- message_id: (0, import_sdk15.uuidV4)(),
7705
+ purpose: import_sdk14.Purpose.ENVELOPE,
7706
+ message_id: (0, import_sdk14.uuidV4)(),
7581
7707
  sender_did: sender.did,
7582
7708
  recipient_did: recipientDid,
7583
7709
  relationship_id: null,
7584
7710
  sender_sequence: nextSequence,
7585
- sender_nonce: (0, import_sdk15.senderNonce)(),
7586
- timestamp: (0, import_sdk15.rfc3339)(),
7587
- expires_at: (0, import_sdk15.expiresAt)(ttlSeconds),
7711
+ sender_nonce: (0, import_sdk14.senderNonce)(),
7712
+ timestamp: (0, import_sdk14.rfc3339)(),
7713
+ expires_at: (0, import_sdk14.expiresAt)(ttlSeconds),
7588
7714
  delivery_id: null
7589
7715
  };
7590
- const envelope = (0, import_sdk15.signEnvelope)({
7716
+ const envelope = (0, import_sdk14.signEnvelope)({
7591
7717
  protected: protectedBlock,
7592
7718
  body,
7593
7719
  identitySecretKey: signer.identitySecretKey
7594
7720
  });
7595
7721
  if (opts.verbose) {
7596
- console.log(import_chalk25.default.bold("\nEnvelope (pre-send):"));
7722
+ console.log(import_chalk23.default.bold("\nEnvelope (pre-send):"));
7597
7723
  console.log(formatJson(envelope));
7598
7724
  }
7599
7725
  const result = await api.ingest(envelope);
@@ -7613,23 +7739,23 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7613
7739
  });
7614
7740
  return;
7615
7741
  }
7616
- console.log(import_chalk25.default.green("\nDelivered."));
7617
- console.log(`${import_chalk25.default.bold("Event id")}: ${import_chalk25.default.cyan(result.eventId)}`);
7618
- console.log(`${import_chalk25.default.bold("Relationship id")}: ${import_chalk25.default.cyan(result.relationshipId)}`);
7619
- console.log(`${import_chalk25.default.bold("Chain index")}: ${import_chalk25.default.cyan(String(result.relationshipEventIndex))}`);
7620
- console.log(`${import_chalk25.default.bold("Server timestamp")}: ${import_chalk25.default.cyan(result.serverTimestamp)}`);
7621
- console.log(`${import_chalk25.default.bold("Signed message hash")}: ${import_chalk25.default.cyan(result.signedMessageHash)}`);
7622
- console.log(`${import_chalk25.default.bold("Server event hash")}: ${import_chalk25.default.cyan(result.serverEventHash)}`);
7742
+ console.log(import_chalk23.default.green("\nDelivered."));
7743
+ console.log(`${import_chalk23.default.bold("Event id")}: ${import_chalk23.default.cyan(result.eventId)}`);
7744
+ console.log(`${import_chalk23.default.bold("Relationship id")}: ${import_chalk23.default.cyan(result.relationshipId)}`);
7745
+ console.log(`${import_chalk23.default.bold("Chain index")}: ${import_chalk23.default.cyan(String(result.relationshipEventIndex))}`);
7746
+ console.log(`${import_chalk23.default.bold("Server timestamp")}: ${import_chalk23.default.cyan(result.serverTimestamp)}`);
7747
+ console.log(`${import_chalk23.default.bold("Signed message hash")}: ${import_chalk23.default.cyan(result.signedMessageHash)}`);
7748
+ console.log(`${import_chalk23.default.bold("Server event hash")}: ${import_chalk23.default.cyan(result.serverEventHash)}`);
7623
7749
  if (result.prevServerEventHash) {
7624
- console.log(`${import_chalk25.default.bold("Prev server event hash")}: ${import_chalk25.default.cyan(result.prevServerEventHash)}`);
7750
+ console.log(`${import_chalk23.default.bold("Prev server event hash")}: ${import_chalk23.default.cyan(result.prevServerEventHash)}`);
7625
7751
  } else {
7626
- console.log(`${import_chalk25.default.bold("Prev server event hash")}: ${import_chalk25.default.dim("(null \u2014 first event of this relationship)")}`);
7752
+ console.log(`${import_chalk23.default.bold("Prev server event hash")}: ${import_chalk23.default.dim("(null \u2014 first event of this relationship)")}`);
7627
7753
  }
7628
7754
  if (opts.verbose) {
7629
- console.log(import_chalk25.default.bold("\nFull server response:"));
7755
+ console.log(import_chalk23.default.bold("\nFull server response:"));
7630
7756
  console.log(formatJson(result));
7631
7757
  }
7632
- console.log(import_chalk25.default.dim(`
7758
+ console.log(import_chalk23.default.dim(`
7633
7759
  Local sender_sequence advanced to ${nextSequence}.`));
7634
7760
  }
7635
7761
  function parseDecision(raw) {
@@ -7688,7 +7814,7 @@ init_status();
7688
7814
  init_wallet();
7689
7815
 
7690
7816
  // src/commands/watch.ts
7691
- var import_chalk26 = __toESM(require("chalk"));
7817
+ var import_chalk24 = __toESM(require("chalk"));
7692
7818
  init_api();
7693
7819
  init_format();
7694
7820
  init_state();
@@ -7702,9 +7828,9 @@ async function runWatch(relationshipId, opts) {
7702
7828
  const local = resolveSenderAgent("watch", opts.server, opts.fromDid);
7703
7829
  const api = new ArpApiClient(opts.server);
7704
7830
  if (!opts.json) {
7705
- console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
7706
- console.log(import_chalk26.default.dim(`Signer: ${local.did}`));
7707
- console.log(import_chalk26.default.dim(`Watching: ${relationshipId}`));
7831
+ console.log(import_chalk24.default.dim(`Server: ${api.serverUrl}`));
7832
+ console.log(import_chalk24.default.dim(`Signer: ${local.did}`));
7833
+ console.log(import_chalk24.default.dim(`Watching: ${relationshipId}`));
7708
7834
  }
7709
7835
  const controller = new AbortController();
7710
7836
  let userAborted = false;
@@ -7723,7 +7849,7 @@ async function runWatch(relationshipId, opts) {
7723
7849
  }
7724
7850
  if (event.type === "heartbeat") continue;
7725
7851
  if (event.type === "connected") {
7726
- console.log(import_chalk26.default.green(`\u25CF stream open \u2014 watching ${relationshipId}`));
7852
+ console.log(import_chalk24.default.green(`\u25CF stream open \u2014 watching ${relationshipId}`));
7727
7853
  continue;
7728
7854
  }
7729
7855
  if (event.type === "envelope") {
@@ -7737,7 +7863,7 @@ async function runWatch(relationshipId, opts) {
7737
7863
  }
7738
7864
  continue;
7739
7865
  }
7740
- console.log(import_chalk26.default.dim(`(unknown event: ${event.type})`));
7866
+ console.log(import_chalk24.default.dim(`(unknown event: ${event.type})`));
7741
7867
  }
7742
7868
  if (!userAborted) {
7743
7869
  throw new Error(`watch ${relationshipId}: stream ended unexpectedly (server may have restarted, or the change stream errored). Re-run to reconnect.`);
@@ -7745,7 +7871,7 @@ async function runWatch(relationshipId, opts) {
7745
7871
  } catch (err) {
7746
7872
  const name = err.name;
7747
7873
  if (name === "AbortError" || userAborted) {
7748
- if (!opts.json) console.log(import_chalk26.default.dim("\nstream closed."));
7874
+ if (!opts.json) console.log(import_chalk24.default.dim("\nstream closed."));
7749
7875
  return;
7750
7876
  }
7751
7877
  throw err;
@@ -7759,21 +7885,21 @@ function formatWatchLine(ev, selfDid, opts = {}) {
7759
7885
  const type = ev.type.padEnd(20);
7760
7886
  const direction = directionLabel2(ev, selfDid, opts);
7761
7887
  const hash = opts.fullIds ? ev.serverEventHash : hashHead4(ev.serverEventHash);
7762
- return `${import_chalk26.default.dim(`[${ts}]`)} ${type} ${direction} ${import_chalk26.default.cyan(hash)}`;
7888
+ return `${import_chalk24.default.dim(`[${ts}]`)} ${type} ${direction} ${import_chalk24.default.cyan(hash)}`;
7763
7889
  }
7764
7890
  function directionLabel2(ev, selfDid, opts = {}) {
7765
7891
  const senderHead = opts.fullIds ? ev.senderDid : didHead4(ev.senderDid);
7766
7892
  const recipientHead = opts.fullIds ? ev.recipientDid : didHead4(ev.recipientDid);
7767
- if (ev.senderDid === selfDid) return `${import_chalk26.default.bold("me")} \u2192 ${import_chalk26.default.dim(recipientHead)}`;
7768
- if (ev.recipientDid === selfDid) return `${import_chalk26.default.dim(senderHead)} \u2192 ${import_chalk26.default.bold("me")}`;
7769
- return `${import_chalk26.default.dim(senderHead)} \u2192 ${import_chalk26.default.dim(recipientHead)}`;
7893
+ if (ev.senderDid === selfDid) return `${import_chalk24.default.bold("me")} \u2192 ${import_chalk24.default.dim(recipientHead)}`;
7894
+ if (ev.recipientDid === selfDid) return `${import_chalk24.default.dim(senderHead)} \u2192 ${import_chalk24.default.bold("me")}`;
7895
+ return `${import_chalk24.default.dim(senderHead)} \u2192 ${import_chalk24.default.dim(recipientHead)}`;
7770
7896
  }
7771
7897
  function didHead4(did) {
7772
7898
  if (did.length <= 20) return did;
7773
7899
  return `${did.slice(0, 20)}...`;
7774
7900
  }
7775
7901
  function hashHead4(hash) {
7776
- if (!hash) return import_chalk26.default.dim("(none)");
7902
+ if (!hash) return import_chalk24.default.dim("(none)");
7777
7903
  if (hash.length <= 14) return hash;
7778
7904
  return `${hash.slice(0, 14)}...`;
7779
7905
  }
@@ -7785,7 +7911,7 @@ function formatClock(iso) {
7785
7911
  }
7786
7912
 
7787
7913
  // src/commands/whoami.ts
7788
- var import_chalk27 = __toESM(require("chalk"));
7914
+ var import_chalk25 = __toESM(require("chalk"));
7789
7915
  init_api();
7790
7916
  init_format();
7791
7917
  init_state();
@@ -7798,10 +7924,10 @@ function registerWhoamiCommand(root) {
7798
7924
  // Persona scripts want the local settlement pubkey for
7799
7925
  // `--payer-settlement-pubkey` / on-chain ops without a
7800
7926
  // server round-trip. `--local` short-circuits the signed
7801
- // GET, prints just the resolved DID + pubkeys + keyMode
7802
- // from local state. (V1-alpha: accountId parked, no longer
7803
- // in --local output.)
7804
- "Print local-state info only (DID + settlement + identity pubkeys + keyMode). Skips the signed server fetch \u2014 useful for scripts that need the local pubkey before the server is up, or to grep from a shell without burning a network round-trip.",
7927
+ // GET, prints just the resolved DID + pubkeys from local
7928
+ // state. (V1-alpha: accountId parked, no longer in --local
7929
+ // output.)
7930
+ "Print local-state info only (DID + settlement + identity pubkeys). Skips the signed server fetch \u2014 useful for scripts that need the local pubkey before the server is up, or to grep from a shell without burning a network round-trip.",
7805
7931
  false
7806
7932
  ).option("--json", "JSON output (jq-pipeable). Combines local + server data when --local is unset, or just local when --local is set.", false).action(async (didArg, opts, cmd) => {
7807
7933
  try {
@@ -7815,18 +7941,16 @@ function registerWhoamiCommand(root) {
7815
7941
  // longer carries the field. Uncomment when launchpad↔ARP
7816
7942
  // join lands.
7817
7943
  // accountId: local.accountId,
7818
- keyMode: local.keyMode,
7819
7944
  name: local.name
7820
7945
  };
7821
7946
  if (opts.local) {
7822
7947
  if (opts.json) {
7823
7948
  console.log(formatJson(localJson));
7824
7949
  } else {
7825
- console.log(import_chalk27.default.bold("Local agent:"));
7826
- console.log(` DID: ${import_chalk27.default.cyan(local.did)}`);
7827
- console.log(` Settlement pubkey: ${import_chalk27.default.cyan(local.settlementPublicKeyB58)}`);
7828
- console.log(` Identity pubkey: ${import_chalk27.default.cyan(local.identityPublicKeyB58)}`);
7829
- console.log(` Key mode: ${local.keyMode}`);
7950
+ console.log(import_chalk25.default.bold("Local agent:"));
7951
+ console.log(` DID: ${import_chalk25.default.cyan(local.did)}`);
7952
+ console.log(` Settlement pubkey: ${import_chalk25.default.cyan(local.settlementPublicKeyB58)}`);
7953
+ console.log(` Identity pubkey: ${import_chalk25.default.cyan(local.identityPublicKeyB58)}`);
7830
7954
  if (local.name) console.log(` Name: ${local.name}`);
7831
7955
  }
7832
7956
  return;
@@ -7837,12 +7961,12 @@ function registerWhoamiCommand(root) {
7837
7961
  if (opts.json) {
7838
7962
  console.log(formatJson({ local: localJson, server: agent }));
7839
7963
  } else {
7840
- console.log(import_chalk27.default.dim(`Server: ${api.serverUrl}`));
7841
- console.log(import_chalk27.default.bold("\nLocal agent:"));
7842
- console.log(` DID: ${import_chalk27.default.cyan(local.did)}`);
7843
- console.log(` Settlement pubkey: ${import_chalk27.default.cyan(local.settlementPublicKeyB58)}`);
7844
- console.log(` Identity pubkey: ${import_chalk27.default.cyan(local.identityPublicKeyB58)}`);
7845
- console.log(import_chalk27.default.bold("\nServer profile:"));
7964
+ console.log(import_chalk25.default.dim(`Server: ${api.serverUrl}`));
7965
+ console.log(import_chalk25.default.bold("\nLocal agent:"));
7966
+ console.log(` DID: ${import_chalk25.default.cyan(local.did)}`);
7967
+ console.log(` Settlement pubkey: ${import_chalk25.default.cyan(local.settlementPublicKeyB58)}`);
7968
+ console.log(` Identity pubkey: ${import_chalk25.default.cyan(local.identityPublicKeyB58)}`);
7969
+ console.log(import_chalk25.default.bold("\nServer profile:"));
7846
7970
  console.log(formatJson(agent));
7847
7971
  }
7848
7972
  } catch (err) {
@@ -7853,8 +7977,8 @@ function registerWhoamiCommand(root) {
7853
7977
  }
7854
7978
 
7855
7979
  // src/commands/work.ts
7856
- var import_sdk16 = require("@heyanon-arp/sdk");
7857
- var import_chalk28 = __toESM(require("chalk"));
7980
+ var import_sdk15 = require("@heyanon-arp/sdk");
7981
+ var import_chalk26 = __toESM(require("chalk"));
7858
7982
  init_api();
7859
7983
  init_format();
7860
7984
  init_id_format();
@@ -7873,7 +7997,16 @@ var POST_COMMIT_ERROR_CODES3 = /* @__PURE__ */ new Set([
7873
7997
  "WORK_REQUEST_ALREADY_EXISTS",
7874
7998
  "WORK_REQUEST_NOT_FOUND",
7875
7999
  "WORK_RESPONDER_IS_CALLER",
7876
- "WORK_INVALID_STATE"
8000
+ "WORK_INVALID_STATE",
8001
+ // M6 lock-at-accept: the work handler's LOCKED gate emits
8002
+ // `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
8003
+ // the on-chain lock isn't confirmed yet (state
8004
+ // `pending_lock_finalization`). It fires from the body handler AFTER
8005
+ // the event row is committed — same lifecycle as
8006
+ // `WORK_DELEGATION_NOT_ACTIVE` — so the CLI must advance
8007
+ // `lastSenderSequence`, otherwise a retry once the lock confirms
8008
+ // reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
8009
+ "DELEGATION_PENDING_LOCK"
7877
8010
  ]);
7878
8011
  function registerRequest(parent) {
7879
8012
  parent.command("request").description("Send a work_request to <recipient-did> under <delegation-id> (must be ACCEPTED).").argument("<recipient-did>", "Recipient agent DID (the payee \u2014 the OTHER side of the delegation pair)").argument("<delegation-id>", "Parent delegation id (UUID, must be ACCEPTED)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--request-id <id>", "Override the auto-generated request id (must be unique within the delegation)").option(
@@ -7900,17 +8033,17 @@ async function runRequest(recipientDid, delegationId, opts) {
7900
8033
  params
7901
8034
  };
7902
8035
  const body = { type: "work_request", content };
7903
- console.log(import_chalk28.default.dim(`Server: ${api.serverUrl}`));
7904
- console.log(import_chalk28.default.dim(`Sender: ${sender.did}`));
7905
- console.log(import_chalk28.default.dim(`Recipient: ${recipientDid}`));
7906
- console.log(import_chalk28.default.dim(`Delegation: ${delegationId}`));
7907
- console.log(import_chalk28.default.dim(`Request id: ${requestId}`));
8036
+ console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
8037
+ console.log(import_chalk26.default.dim(`Sender: ${sender.did}`));
8038
+ console.log(import_chalk26.default.dim(`Recipient: ${recipientDid}`));
8039
+ console.log(import_chalk26.default.dim(`Delegation: ${delegationId}`));
8040
+ console.log(import_chalk26.default.dim(`Request id: ${requestId}`));
7908
8041
  const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
7909
8042
  printIngestResult3(result);
7910
- console.log(import_chalk28.default.dim(`
8043
+ console.log(import_chalk26.default.dim(`
7911
8044
  The payee can reply with:`));
7912
- console.log(import_chalk28.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --output '<json>'`));
7913
- console.log(import_chalk28.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --error CODE:message`));
8045
+ console.log(import_chalk26.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --output '<json>'`));
8046
+ console.log(import_chalk26.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --error CODE:message`));
7914
8047
  }
7915
8048
  function registerRespond(parent) {
7916
8049
  parent.command("respond").description("Send a work_response under <relationship-id> for <delegation-id> / <request-id>. Payee-only.").argument("<relationship-id>", "Relationship UUID").argument("<delegation-id>", "Parent delegation id (UUID)").argument("<request-id>", "Request id supplied on the work_request").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--output <json>", "Success payload as a JSON object literal. Mutually exclusive with --error and --output-file.").option(
@@ -7935,13 +8068,13 @@ async function runRespond(relationshipId, delegationId, requestId, opts) {
7935
8068
  ...responsePayload
7936
8069
  };
7937
8070
  const body = { type: "work_response", content };
7938
- console.log(import_chalk28.default.dim(`Server: ${api.serverUrl}`));
7939
- console.log(import_chalk28.default.dim(`Sender: ${sender.did}`));
7940
- console.log(import_chalk28.default.dim(`Recipient: ${recipientDid}`));
7941
- console.log(import_chalk28.default.dim(`Relationship: ${relationshipId}`));
7942
- console.log(import_chalk28.default.dim(`Delegation: ${delegationId}`));
7943
- console.log(import_chalk28.default.dim(`Request id: ${requestId}`));
7944
- console.log(import_chalk28.default.dim(`Outcome: ${responsePayload.output ? "success" : "error"}`));
8071
+ console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
8072
+ console.log(import_chalk26.default.dim(`Sender: ${sender.did}`));
8073
+ console.log(import_chalk26.default.dim(`Recipient: ${recipientDid}`));
8074
+ console.log(import_chalk26.default.dim(`Relationship: ${relationshipId}`));
8075
+ console.log(import_chalk26.default.dim(`Delegation: ${delegationId}`));
8076
+ console.log(import_chalk26.default.dim(`Request id: ${requestId}`));
8077
+ console.log(import_chalk26.default.dim(`Outcome: ${responsePayload.output ? "success" : "error"}`));
7945
8078
  const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
7946
8079
  printIngestResult3(result);
7947
8080
  }
@@ -7949,25 +8082,25 @@ async function sendWorkEnvelope(args) {
7949
8082
  const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
7950
8083
  const protectedBlock = {
7951
8084
  protocol_version: "arp/0.1",
7952
- purpose: import_sdk16.Purpose.ENVELOPE,
7953
- message_id: (0, import_sdk16.uuidV4)(),
8085
+ purpose: import_sdk15.Purpose.ENVELOPE,
8086
+ message_id: (0, import_sdk15.uuidV4)(),
7954
8087
  sender_did: args.sender.did,
7955
8088
  recipient_did: args.recipientDid,
7956
8089
  relationship_id: null,
7957
8090
  sender_sequence: nextSequence,
7958
- sender_nonce: (0, import_sdk16.senderNonce)(),
7959
- timestamp: (0, import_sdk16.rfc3339)(),
7960
- expires_at: (0, import_sdk16.expiresAt)(args.ttlSeconds),
8091
+ sender_nonce: (0, import_sdk15.senderNonce)(),
8092
+ timestamp: (0, import_sdk15.rfc3339)(),
8093
+ expires_at: (0, import_sdk15.expiresAt)(args.ttlSeconds),
7961
8094
  delivery_id: null
7962
8095
  };
7963
8096
  const signer = makeSigner(args.sender);
7964
- const envelope = (0, import_sdk16.signEnvelope)({
8097
+ const envelope = (0, import_sdk15.signEnvelope)({
7965
8098
  protected: protectedBlock,
7966
8099
  body: args.body,
7967
8100
  identitySecretKey: signer.identitySecretKey
7968
8101
  });
7969
8102
  if (args.verbose) {
7970
- console.log(import_chalk28.default.bold("\nEnvelope (pre-send):"));
8103
+ console.log(import_chalk26.default.bold("\nEnvelope (pre-send):"));
7971
8104
  console.log(formatJson(envelope));
7972
8105
  }
7973
8106
  try {
@@ -8007,12 +8140,12 @@ async function resolveResponseRecipient(cmdName, api, signer, args) {
8007
8140
  );
8008
8141
  }
8009
8142
  function printIngestResult3(result) {
8010
- console.log(import_chalk28.default.green("\nDelivered."));
8011
- console.log(`${import_chalk28.default.bold("Event id")}: ${import_chalk28.default.cyan(result.eventId)}`);
8012
- console.log(`${import_chalk28.default.bold("Relationship id")}: ${import_chalk28.default.cyan(result.relationshipId)}`);
8013
- console.log(`${import_chalk28.default.bold("Chain index")}: ${import_chalk28.default.cyan(String(result.relationshipEventIndex))}`);
8014
- console.log(`${import_chalk28.default.bold("Server timestamp")}: ${import_chalk28.default.cyan(result.serverTimestamp)}`);
8015
- console.log(`${import_chalk28.default.bold("Server event hash")}: ${import_chalk28.default.cyan(result.serverEventHash)}`);
8143
+ console.log(import_chalk26.default.green("\nDelivered."));
8144
+ console.log(`${import_chalk26.default.bold("Event id")}: ${import_chalk26.default.cyan(result.eventId)}`);
8145
+ console.log(`${import_chalk26.default.bold("Relationship id")}: ${import_chalk26.default.cyan(result.relationshipId)}`);
8146
+ console.log(`${import_chalk26.default.bold("Chain index")}: ${import_chalk26.default.cyan(String(result.relationshipEventIndex))}`);
8147
+ console.log(`${import_chalk26.default.bold("Server timestamp")}: ${import_chalk26.default.cyan(result.serverTimestamp)}`);
8148
+ console.log(`${import_chalk26.default.bold("Server event hash")}: ${import_chalk26.default.cyan(result.serverEventHash)}`);
8016
8149
  }
8017
8150
  function parseJsonObject(cmdName, flagName, raw) {
8018
8151
  let parsed;
@@ -8050,13 +8183,13 @@ function parseParamsInput(cmdName, opts) {
8050
8183
  return parseJsonObject(cmdName, "--params", opts.params ?? "{}");
8051
8184
  }
8052
8185
  function readJsonObjectFile(cmdName, flagName, path) {
8053
- const { existsSync: existsSync7, readFileSync: readFileSync9 } = require("fs");
8054
- if (!existsSync7(path)) {
8186
+ const { existsSync: existsSync6, readFileSync: readFileSync8 } = require("fs");
8187
+ if (!existsSync6(path)) {
8055
8188
  throw new Error(`${cmdName}: ${flagName} file not found at ${path}`);
8056
8189
  }
8057
8190
  let raw;
8058
8191
  try {
8059
- raw = readFileSync9(path, "utf8");
8192
+ raw = readFileSync8(path, "utf8");
8060
8193
  } catch (err) {
8061
8194
  const detail = err instanceof Error ? err.message : String(err);
8062
8195
  throw new Error(`${cmdName}: failed to read ${flagName} (${path}): ${detail}`);
@@ -8104,7 +8237,7 @@ function parseTtl5(cmdName, raw) {
8104
8237
  }
8105
8238
  function parseRequestId(cmdName, raw) {
8106
8239
  if (raw === void 0 || raw === "") {
8107
- return (0, import_sdk16.uuidV4)();
8240
+ return (0, import_sdk15.uuidV4)();
8108
8241
  }
8109
8242
  if (raw.length === 0) {
8110
8243
  throw new Error(`${cmdName}: --request-id must be a non-empty string`);
@@ -8125,7 +8258,7 @@ function requireDid3(cmdName, did, label) {
8125
8258
  }
8126
8259
 
8127
8260
  // src/commands/work-list.ts
8128
- var import_chalk29 = __toESM(require("chalk"));
8261
+ var import_chalk27 = __toESM(require("chalk"));
8129
8262
  init_api();
8130
8263
  init_format();
8131
8264
  init_state();
@@ -8155,9 +8288,9 @@ async function runWorkList(relationshipId, opts) {
8155
8288
  const api = new ArpApiClient(opts.server);
8156
8289
  const sender = resolveSenderAgent("work-list", opts.server, opts.fromDid);
8157
8290
  if (!opts.json) {
8158
- console.log(import_chalk29.default.dim(`Server: ${api.serverUrl}`));
8159
- console.log(import_chalk29.default.dim(`Signer: ${sender.did}`));
8160
- console.log(import_chalk29.default.dim(`Relationship: ${relationshipId}`));
8291
+ console.log(import_chalk27.default.dim(`Server: ${api.serverUrl}`));
8292
+ console.log(import_chalk27.default.dim(`Signer: ${sender.did}`));
8293
+ console.log(import_chalk27.default.dim(`Relationship: ${relationshipId}`));
8161
8294
  }
8162
8295
  const query = { limit };
8163
8296
  if (state) query.state = state;
@@ -8170,7 +8303,7 @@ async function runWorkList(relationshipId, opts) {
8170
8303
  return;
8171
8304
  }
8172
8305
  if (rows.length === 0) {
8173
- console.log(import_chalk29.default.dim("\n(no work-logs for this relationship)"));
8306
+ console.log(import_chalk27.default.dim("\n(no work-logs for this relationship)"));
8174
8307
  return;
8175
8308
  }
8176
8309
  console.log("");
@@ -8187,36 +8320,36 @@ async function runWorkList(relationshipId, opts) {
8187
8320
  }));
8188
8321
  }
8189
8322
  const lastId = rows[rows.length - 1].id;
8190
- console.log(import_chalk29.default.dim(`
8323
+ console.log(import_chalk27.default.dim(`
8191
8324
  ${rows.length} work-log row(s). Paginate with --after ${lastId}.`));
8192
8325
  }
8193
8326
  function formatWorkLogLine(w, selfDid, opts = {}) {
8194
8327
  const delegationPart = opts.fullIds ? w.delegationId : idHead3(w.delegationId);
8195
8328
  const requestPart = opts.fullIds ? w.requestId : truncate3(w.requestId, 16);
8196
- const id = import_chalk29.default.bold(`${delegationPart}/${requestPart}`);
8329
+ const id = import_chalk27.default.bold(`${delegationPart}/${requestPart}`);
8197
8330
  const state = colorState3(w.state).padEnd(stateColumnWidth3());
8198
8331
  const peerCallerHead = opts.fullIds ? w.callerDid : didHead5(w.callerDid);
8199
8332
  const peerPayeeHead = opts.fullIds ? w.payeeDid : didHead5(w.payeeDid);
8200
- const direction = w.callerDid === selfDid ? `${import_chalk29.default.bold("me")} \u2192 ${import_chalk29.default.dim(peerPayeeHead)}` : `${import_chalk29.default.dim(peerCallerHead)} \u2192 ${import_chalk29.default.bold("me")}`;
8333
+ const direction = w.callerDid === selfDid ? `${import_chalk27.default.bold("me")} \u2192 ${import_chalk27.default.dim(peerPayeeHead)}` : `${import_chalk27.default.dim(peerCallerHead)} \u2192 ${import_chalk27.default.bold("me")}`;
8201
8334
  const outcome = formatOutcome(w);
8202
8335
  return `${id} ${state} ${direction} ${outcome}`;
8203
8336
  }
8204
8337
  function colorState3(s) {
8205
8338
  switch (s) {
8206
8339
  case "requested":
8207
- return import_chalk29.default.yellow("requested");
8340
+ return import_chalk27.default.yellow("requested");
8208
8341
  case "responded":
8209
- return import_chalk29.default.green("responded");
8342
+ return import_chalk27.default.green("responded");
8210
8343
  }
8211
8344
  }
8212
8345
  function stateColumnWidth3() {
8213
8346
  return 9;
8214
8347
  }
8215
8348
  function formatOutcome(w) {
8216
- if (w.state === "requested") return import_chalk29.default.dim("(in flight)");
8217
- if (w.responseError) return import_chalk29.default.red(`error ${w.responseError.code}: ${truncate3(w.responseError.message, 32)}`);
8218
- if (w.responseOutput) return import_chalk29.default.cyan("ok");
8219
- return import_chalk29.default.dim("(empty response)");
8349
+ if (w.state === "requested") return import_chalk27.default.dim("(in flight)");
8350
+ if (w.responseError) return import_chalk27.default.red(`error ${w.responseError.code}: ${truncate3(w.responseError.message, 32)}`);
8351
+ if (w.responseOutput) return import_chalk27.default.cyan("ok");
8352
+ return import_chalk27.default.dim("(empty response)");
8220
8353
  }
8221
8354
  function idHead3(id) {
8222
8355
  if (id.length <= 12) return id;
@@ -8279,10 +8412,8 @@ async function main() {
8279
8412
  registerDidDocCommand(program);
8280
8413
  registerDoctorCommand(program);
8281
8414
  registerEscrowCommands(program);
8282
- registerExamplesCommand(program);
8283
8415
  registerWhoamiCommand(program);
8284
8416
  registerLifecycleCommands(program);
8285
- registerRotateCommand(program);
8286
8417
  registerSendHandshakeCommand(program);
8287
8418
  registerSendHandshakeResponseCommand(program);
8288
8419
  registerInboxCommand(program);