@heyanon-arp/cli 0.0.8 → 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
@@ -3547,9 +3547,15 @@ async function readOnChainLock(api, opts, delegationId) {
3547
3547
  const programId = new import_web33.PublicKey(resolved.programId);
3548
3548
  const { lockPda } = deriveLockPda(programId, delegationId);
3549
3549
  const conn = new import_web33.Connection(rpcUrl, "confirmed");
3550
- 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
+ }
3551
3555
  if (!info) {
3552
- 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
+ };
3553
3559
  }
3554
3560
  const lock = decodeLockAccount(info.data);
3555
3561
  if (!lock) {
@@ -3832,7 +3838,7 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3832
3838
  serverTimestamp: result.serverTimestamp
3833
3839
  };
3834
3840
  }
3835
- var import_sdk10, import_utils3, import_web33, import_chalk17, 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;
3836
3842
  var init_settlement = __esm({
3837
3843
  "src/commands/settlement.ts"() {
3838
3844
  import_sdk10 = require("@heyanon-arp/sdk");
@@ -3852,6 +3858,8 @@ var init_settlement = __esm({
3852
3858
  NATIVE_SOL_MINT2 = "11111111111111111111111111111111";
3853
3859
  SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS = 120;
3854
3860
  SETTLEMENT_REFRESH_HEADROOM_SECS = 600;
3861
+ LOCK_READ_NOT_VISIBLE_RETRIES = 3;
3862
+ LOCK_READ_RETRY_DELAY_MS = 2e3;
3855
3863
  }
3856
3864
  });
3857
3865
 
@@ -3907,6 +3915,14 @@ function registerPropose(parent) {
3907
3915
  await runPropose(recipientDid, delegationId, requestHash, responseHash, opts);
3908
3916
  } catch (err) {
3909
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
+ }
3910
3926
  process.exitCode = 1;
3911
3927
  }
3912
3928
  });
@@ -4054,7 +4070,12 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
4054
4070
  json.settlement = {
4055
4071
  delivered: false,
4056
4072
  error: settlementError.message,
4057
- 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)`
4058
4079
  };
4059
4080
  } else if (settlementResult) {
4060
4081
  json.settlement = {
@@ -4079,14 +4100,9 @@ Receipt event hash: ${import_chalk18.default.cyan(result.serverEventHash)}`));
4079
4100
  if (settlementError) {
4080
4101
  console.error(import_chalk18.default.yellow("\nWARNING: receipt proposed, but the payee settlement signature was NOT delivered."));
4081
4102
  console.error(import_chalk18.default.yellow(` reason: ${settlementError.message}`));
4082
- console.error(import_chalk18.default.dim(" The receipt stays committed \u2014 re-running `receipt propose` will NOT help (it hits RECEIPT_ALREADY_EXISTS)."));
4083
- console.error(import_chalk18.default.dim(" Recover by signing + delivering the payee sig directly, once the on-chain lock / RPC is reachable:"));
4084
- console.error(
4085
- import_chalk18.default.dim(
4086
- ` 'heyarp wallet sign-settlement-release ... --write-to <path>' then
4087
- 'heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --receipt-event-hash ${result.serverEventHash} --sig-from-file <path> --expires-at <unix>'`
4088
- )
4089
- );
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>"}'`));
4090
4106
  } else if (settlementResult?.delivered) {
4091
4107
  console.log(import_chalk18.default.green("\nPayee settlement signature signed + delivered (escrow)."));
4092
4108
  console.log(
@@ -4551,18 +4567,32 @@ async function runCosign(relationshipId, delegationId, requestHashArg, responseH
4551
4567
  }
4552
4568
  }
4553
4569
  function registerSendPayeeSig(parent) {
4554
- 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(
4555
4585
  "--receipt-event-hash <sha256:hex>",
4556
- "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."
4557
- ).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(
4558
4588
  "--sig-from-file <path>",
4559
- "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)."
4560
- ).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(
4561
4591
  "--expires-at <unix>",
4562
- "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)."
4563
4593
  ).option(
4564
4594
  "--payee-amount <int>",
4565
- "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."
4566
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) => {
4567
4597
  try {
4568
4598
  await runSendPayeeSig(recipientDid, opts);
@@ -4574,20 +4604,85 @@ function registerSendPayeeSig(parent) {
4574
4604
  }
4575
4605
  async function runSendPayeeSig(recipientDid, opts) {
4576
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
+ }
4577
4654
  requireDid2(cmdName, recipientDid, "<recipient-did>");
4655
+ const recipient = recipientDid;
4578
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
+ }
4579
4672
  requireSha256(cmdName, opts.receiptEventHash, "--receipt-event-hash");
4673
+ const receiptEventHash = opts.receiptEventHash;
4674
+ const sigFromFile = opts.sigFromFile;
4580
4675
  const expiresAtSeconds = parseInteger(cmdName, "--expires-at", opts.expiresAt);
4581
4676
  if (expiresAtSeconds <= 0) {
4582
4677
  throw new Error(`${cmdName}: --expires-at must be a positive unix-seconds integer (got ${opts.expiresAt})`);
4583
4678
  }
4584
4679
  const ttlSeconds = parseTtl2(cmdName, opts.ttl);
4585
- const sigFile = loadSettlementSigFromFile(opts.sigFromFile, "--sig-from-file");
4680
+ const sigFile = loadSettlementSigFromFile(sigFromFile, "--sig-from-file");
4586
4681
  const isPartial = sigFile.purpose === "ARP-SOLANA-PARTIAL-RELEASE-v1.5";
4587
4682
  const isFull = sigFile.purpose === "ARP-SOLANA-RELEASE-v1.5";
4588
4683
  if (!isPartial && !isFull) {
4589
4684
  throw new Error(
4590
- `${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.)`
4591
4686
  );
4592
4687
  }
4593
4688
  if (isPartial && (opts.payeeAmount === void 0 || opts.payeeAmount === "")) {
@@ -4609,7 +4704,7 @@ async function runSendPayeeSig(recipientDid, opts) {
4609
4704
  const api = new ArpApiClient(opts.server);
4610
4705
  const content = {
4611
4706
  delegation_id: delegationId,
4612
- receipt_event_hash: opts.receiptEventHash,
4707
+ receipt_event_hash: receiptEventHash,
4613
4708
  purpose: sigFile.purpose,
4614
4709
  payee_settlement_pubkey: sigFile.settlement_pubkey,
4615
4710
  sig: sigFile.sig,
@@ -4618,9 +4713,9 @@ async function runSendPayeeSig(recipientDid, opts) {
4618
4713
  };
4619
4714
  console.log(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
4620
4715
  console.log(import_chalk18.default.dim(`Sender (payee): ${sender.did}`));
4621
- console.log(import_chalk18.default.dim(`Recipient (buyer): ${recipientDid}`));
4716
+ console.log(import_chalk18.default.dim(`Recipient (buyer): ${recipient}`));
4622
4717
  console.log(import_chalk18.default.dim(`Delegation: ${delegationId}`));
4623
- console.log(import_chalk18.default.dim(`Receipt event hash: ${opts.receiptEventHash}`));
4718
+ console.log(import_chalk18.default.dim(`Receipt event hash: ${receiptEventHash}`));
4624
4719
  console.log(import_chalk18.default.dim(`Purpose: ${sigFile.purpose}`));
4625
4720
  if (isPartial) {
4626
4721
  console.log(import_chalk18.default.dim(`Payee amount: ${opts.payeeAmount}`));
@@ -4630,7 +4725,7 @@ async function runSendPayeeSig(recipientDid, opts) {
4630
4725
  const result = await sendSettlementSignatureEnvelope({
4631
4726
  api,
4632
4727
  sender,
4633
- recipientDid,
4728
+ recipientDid: recipient,
4634
4729
  content,
4635
4730
  ttlSeconds,
4636
4731
  verbose: opts.verbose,
@@ -4934,7 +5029,7 @@ var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
4934
5029
  // package.json
4935
5030
  var package_default = {
4936
5031
  name: "@heyanon-arp/cli",
4937
- version: "0.0.8",
5032
+ version: "0.0.9",
4938
5033
  description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
4939
5034
  license: "MIT",
4940
5035
  keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
@@ -5820,7 +5915,7 @@ var GUIDE_SECTIONS = [
5820
5915
  { when: "a `work_request` arrives", then: "do the task, then `heyarp work respond <rel-id> <del-id> <req-id> --output '<json>'`" },
5821
5916
  {
5822
5917
  when: "your `work respond` is sent",
5823
- 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 (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)."
5824
5919
  },
5825
5920
  { when: "the buyer cosigns (cycle released)", then: "you are paid \u2014 the cycle is done; wait for the next offer" }
5826
5921
  ],
@@ -6016,7 +6111,17 @@ var GUIDE_SECTIONS = [
6016
6111
  " `heyarp wallet sign-settlement-release` signs the release / partial",
6017
6112
  " digest. Output `sig` is RAW base64 (NO `ed25519:` prefix). Pass",
6018
6113
  " `--partial-payee-amount <lamports>` to switch the digest to",
6019
- " `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.",
6020
6125
  " `heyarp receipt cosign` attaches both parties' signatures into",
6021
6126
  " `attachments.settlement_signatures` via --settlement-purpose,",
6022
6127
  " --settlement-expires-at, --payer-settlement-{pubkey,sig},",
@@ -6231,6 +6336,13 @@ var GUIDE_SECTIONS = [
6231
6336
  " account for that mint exists.",
6232
6337
  " \u2022 handshake stuck `pending` / delegation stuck `offered` \u2192 the COUNTERPARTY",
6233
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).",
6234
6346
  ' \u2022 "wrong move for my role" \u2192 you mixed up buyer vs worker; role is',
6235
6347
  ' PER-RELATIONSHIP \u2014 re-check with `heyarp guide` ("Which role am I?").',
6236
6348
  " More: README at https://www.npmjs.com/package/@heyanon-arp/cli"
@@ -7069,13 +7181,11 @@ async function runRegister(opts, deps = defaultRegisterDeps) {
7069
7181
  });
7070
7182
  const settlementPublicKeyB58 = (0, import_sdk12.base58btcEncode)(keys.settlementPublicKey);
7071
7183
  const scryptSaltId = (0, import_sdk12.uuidV4)();
7072
- const ownerId = (0, import_sdk12.uuidV4)();
7073
7184
  const payload = {
7074
7185
  purpose: "ARP-KEY-LINK-v1",
7075
7186
  agent_did: did,
7076
7187
  identity_public_key: identityPublicKeyB58,
7077
7188
  settlement_public_key: settlementPublicKeyB58,
7078
- owner_id: ownerId,
7079
7189
  owner_signing_method: "scrypt_password_proof",
7080
7190
  link_method: "manual",
7081
7191
  created_at: (0, import_sdk12.rfc3339)(),
@@ -7108,7 +7218,6 @@ async function runRegister(opts, deps = defaultRegisterDeps) {
7108
7218
  scryptKeyB64: Buffer.from(scryptKey).toString("base64"),
7109
7219
  scryptSaltB64: Buffer.from(scryptSalt).toString("base64"),
7110
7220
  scryptSaltId,
7111
- ownerId,
7112
7221
  // Placeholder until the server confirms registration and returns
7113
7222
  // the canonical attestation id (finalized in Step 8 below).
7114
7223
  currentAttestationId: "",