@heyanon-arp/cli 0.0.15 → 0.0.17

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
@@ -34,10 +34,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
34
34
  function arpHomeDir() {
35
35
  const fromEnv = process.env.HEYARP_HOME;
36
36
  if (fromEnv && fromEnv.length > 0) return fromEnv;
37
- return (0, import_node_path.join)((0, import_node_os.homedir)(), ".arp");
37
+ return (0, import_node_path.join)((0, import_node_os.homedir)(), ".heyarp");
38
38
  }
39
39
  function homesRegistryPath() {
40
- return (0, import_node_path.join)((0, import_node_os.homedir)(), ".arp", "homes.json");
40
+ return (0, import_node_path.join)((0, import_node_os.homedir)(), ".heyarp", "homes.json");
41
41
  }
42
42
  var import_node_os, import_node_path;
43
43
  var init_paths = __esm({
@@ -354,9 +354,8 @@ var init_api = __esm({
354
354
  }
355
355
  /**
356
356
  * Signed-request authenticated GET /v1/agents/:did. Owner-only on
357
- * the server side, but in V1 it returns the same shape as
358
- * /did-document — useful as a sanity check that a stored secret
359
- * key still verifies.
357
+ * the server side; it returns the same shape as /did-document —
358
+ * useful as a sanity check that a stored secret key still verifies.
360
359
  */
361
360
  async getAgent(did, signer) {
362
361
  return this.signedRequest("GET", `/v1/agents/${encodeURIComponent(did)}`, null, signer);
@@ -723,17 +722,17 @@ var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
723
722
  // package.json
724
723
  var package_default = {
725
724
  name: "@heyanon-arp/cli",
726
- version: "0.0.15",
725
+ version: "0.0.17",
727
726
  description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
728
727
  license: "MIT",
729
728
  keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
730
729
  bin: {
731
- heyarp: "./dist/cli.js"
730
+ heyarp: "./bin/heyarp.cjs"
732
731
  },
733
732
  publishConfig: {
734
733
  access: "public"
735
734
  },
736
- files: ["dist", "LICENSE", "README.md"],
735
+ files: ["dist", "bin", "LICENSE", "README.md"],
737
736
  engines: {
738
737
  node: ">=22"
739
738
  },
@@ -805,6 +804,14 @@ function onboardingHelpFooter() {
805
804
  `${import_chalk.default.dim("Then:")} ${import_chalk.default.cyan("heyarp register")} ${import_chalk.default.dim("\u2192 handshake \u2192 delegation \u2192 work \u2192 receipt \u2192 on-chain claim.")}`
806
805
  ].join("\n");
807
806
  }
807
+ function optionPlacementHelpNote() {
808
+ return [
809
+ "",
810
+ `${import_chalk.default.bold("Option placement:")} only ${import_chalk.default.cyan("--trace")}, ${import_chalk.default.cyan("--help")}, ${import_chalk.default.cyan("--version")} are global (before the command).`,
811
+ `Everything else \u2014 ${import_chalk.default.cyan("--json")}, ${import_chalk.default.cyan("--from-did")}, ${import_chalk.default.cyan("--verbose")}, ${import_chalk.default.cyan("--server")}, \u2026 \u2014 is a command option and goes AFTER the command:`,
812
+ ` ${import_chalk.default.cyan("heyarp <command> [options]")} ${import_chalk.default.dim("e.g. heyarp relationships --json --from-did did:arp:\u2026")}`
813
+ ].join("\n");
814
+ }
808
815
  function toCliErrorJson(err, includeStack = false) {
809
816
  const { ApiError: ApiError2 } = (init_api(), __toCommonJS(api_exports));
810
817
  if (err instanceof ApiError2) {
@@ -822,7 +829,9 @@ function toCliErrorJson(err, includeStack = false) {
822
829
  }
823
830
  function emitError(err, opts) {
824
831
  if (opts.json) {
825
- console.error(JSON.stringify(toCliErrorJson(err, opts.verbose)));
832
+ const obj = toCliErrorJson(err, opts.verbose);
833
+ if (opts.hint) obj.hint = opts.hint;
834
+ console.error(JSON.stringify(obj));
826
835
  return;
827
836
  }
828
837
  const { ApiError: ApiError2 } = (init_api(), __toCommonJS(api_exports));
@@ -831,6 +840,7 @@ function emitError(err, opts) {
831
840
  } else {
832
841
  console.error(formatGenericError(err, opts.verbose));
833
842
  }
843
+ if (opts.hint) console.error(`${import_chalk.default.yellow("hint")}: ${opts.hint}`);
834
844
  }
835
845
  function emitActionError(err, command) {
836
846
  let root = command;
@@ -1163,19 +1173,28 @@ function listAgents() {
1163
1173
  var import_chalk2 = __toESM(require("chalk"));
1164
1174
  init_api();
1165
1175
  function registerLifecycleCommands(root) {
1166
- 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", accumulate, []).option("--clear-tags", "Drop all tags (cannot be combined with --tag)", false).action(
1176
+ root.command("update").description("Update an agent profile (name / description / tags). At least one flag is required.").argument("[did]", "Agent DID (did:arp:...) \u2014 optional when --from-did is given OR exactly one agent is registered for the resolved server.").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Alias for the positional <did> \u2014 accepted for consistency with other signed commands. Must match the positional if both are given.").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", accumulate, []).option("--clear-tags", "Drop all tags (cannot be combined with --tag)", false).option("--json", "Machine-readable mode \u2014 emit the updated profile as a single JSON object on stdout (AgentPublic). Prelude moves to stderr. ", false).action(
1167
1177
  async (did, opts) => {
1178
+ if (did !== void 0 && opts.fromDid !== void 0 && did !== opts.fromDid) {
1179
+ throw new Error(`update: positional <did> (${did}) and --from-did (${opts.fromDid}) disagree \u2014 pass only one`);
1180
+ }
1181
+ const explicitDid = did ?? opts.fromDid;
1182
+ const targetDid = (explicitDid !== void 0 ? loadAgentOrThrow(opts.server, explicitDid) : resolveSenderAgent("update", opts.server, void 0)).did;
1168
1183
  const body = buildUpdateBody(opts);
1169
1184
  if (Object.keys(body).length === 0) {
1170
1185
  throw new Error("update: pass at least one of --name / --description / --tag / --clear-tags");
1171
1186
  }
1172
- const agent = await actSigned(did, opts.server, (api, signer) => api.updateAgent(did, body, signer));
1173
- updateAgentLocal(opts.server, did, {
1187
+ const agent = await actSigned(targetDid, opts.server, opts.json, (api, signer) => api.updateAgent(targetDid, body, signer));
1188
+ updateAgentLocal(opts.server, targetDid, {
1174
1189
  name: agent.name,
1175
1190
  description: agent.description,
1176
1191
  tags: agent.tags
1177
1192
  });
1178
- printAgent("Updated", agent);
1193
+ if (opts.json) {
1194
+ jsonOut(agent);
1195
+ } else {
1196
+ printAgent("Updated", agent);
1197
+ }
1179
1198
  }
1180
1199
  );
1181
1200
  }
@@ -1196,11 +1215,11 @@ function buildUpdateBody(opts) {
1196
1215
  }
1197
1216
  return body;
1198
1217
  }
1199
- async function actSigned(did, serverOverride, act) {
1218
+ async function actSigned(did, serverOverride, json, act) {
1200
1219
  const local = loadAgentOrThrow(serverOverride, did);
1201
1220
  const api = new ArpApiClient(serverOverride);
1202
- console.log(import_chalk2.default.dim(`Server: ${api.serverUrl}`));
1203
- console.log(import_chalk2.default.dim(`Signer: ${local.did}`));
1221
+ progress(json, import_chalk2.default.dim(`Server: ${api.serverUrl}`));
1222
+ progress(json, import_chalk2.default.dim(`Signer: ${local.did}`));
1204
1223
  const signer = makeSigner(local);
1205
1224
  return act(api, signer);
1206
1225
  }
@@ -1246,10 +1265,10 @@ function registerAgentsCommand(root) {
1246
1265
  }
1247
1266
  async function runAgents(opts) {
1248
1267
  const limit = parseLimit(opts.limit);
1249
- if (opts.sort && opts.sort !== "reputation" && opts.sort !== "recent" && opts.sort !== "created") {
1268
+ if (opts.sort && opts.sort !== import_sdk2.DiscoverySorts.REPUTATION && opts.sort !== import_sdk2.DiscoverySorts.RECENT && opts.sort !== import_sdk2.DiscoverySorts.CREATED) {
1250
1269
  throw new Error(`agents: --sort must be 'reputation', 'recent', or 'created' (got '${opts.sort}')`);
1251
1270
  }
1252
- const sort = opts.sort === "recent" ? "recent" : opts.sort === "created" ? "created" : "reputation";
1271
+ const sort = opts.sort === import_sdk2.DiscoverySorts.RECENT ? import_sdk2.DiscoverySorts.RECENT : opts.sort === import_sdk2.DiscoverySorts.CREATED ? import_sdk2.DiscoverySorts.CREATED : import_sdk2.DiscoverySorts.REPUTATION;
1253
1272
  const api = new ArpApiClient(opts.server);
1254
1273
  progress(opts.json, import_chalk3.default.dim(`Server: ${api.serverUrl}`));
1255
1274
  const query = { limit, sort };
@@ -1258,7 +1277,7 @@ async function runAgents(opts) {
1258
1277
  if (opts.accountId) query.accountId = opts.accountId;
1259
1278
  if (opts.accepts) query.accepts = resolveAcceptsAsset(opts.accepts);
1260
1279
  if (opts.online) query.online = true;
1261
- if (sort === "created") {
1280
+ if (sort === import_sdk2.DiscoverySorts.CREATED) {
1262
1281
  if (opts.after) query.after = opts.after;
1263
1282
  } else {
1264
1283
  query.page = parsePage(opts.page);
@@ -1297,10 +1316,10 @@ ${rule}`);
1297
1316
  `);
1298
1317
  }
1299
1318
  if (pagination.hasMore) {
1300
- if (sort === "created" && pagination.nextCursor) {
1319
+ if (sort === import_sdk2.DiscoverySorts.CREATED && pagination.nextCursor) {
1301
1320
  console.log(import_chalk3.default.dim(`
1302
1321
  Next page: re-run with --after ${pagination.nextCursor}`));
1303
- } else if (sort !== "created") {
1322
+ } else if (sort !== import_sdk2.DiscoverySorts.CREATED) {
1304
1323
  console.log(import_chalk3.default.dim(`
1305
1324
  Next page: re-run with --page ${parsePage(opts.page) + 1}`));
1306
1325
  }
@@ -1496,13 +1515,14 @@ function registerAssetsCommand(root) {
1496
1515
  }
1497
1516
 
1498
1517
  // src/commands/block.ts
1518
+ var import_sdk4 = require("@heyanon-arp/sdk");
1499
1519
  var import_chalk5 = __toESM(require("chalk"));
1500
1520
  init_api();
1501
1521
  function resolveSigningAgent(opts) {
1502
1522
  return opts.fromDid !== void 0 ? loadAgentOrThrow(opts.server, opts.fromDid) : resolveSenderAgent("block", opts.server, void 0);
1503
1523
  }
1504
1524
  function formatBlockRow(b) {
1505
- const scope = b.scope === "account" ? import_chalk5.default.yellow("account") : import_chalk5.default.cyan("did");
1525
+ const scope = b.scope === import_sdk4.InboxBlockScopes.ACCOUNT ? import_chalk5.default.yellow("account") : import_chalk5.default.cyan("did");
1506
1526
  const reason = b.reason ? import_chalk5.default.dim(` \u2014 ${b.reason}`) : "";
1507
1527
  return `${scope.padEnd(7)} ${b.did}${reason} ${import_chalk5.default.dim(b.blockedAt)}`;
1508
1528
  }
@@ -1527,7 +1547,7 @@ async function runAdd(did, opts) {
1527
1547
  jsonOut(result);
1528
1548
  return;
1529
1549
  }
1530
- const what = result.scope === "account" ? `the owner account of ${did} (all its agents)` : did;
1550
+ const what = result.scope === import_sdk4.InboxBlockScopes.ACCOUNT ? `the owner account of ${did} (all its agents)` : did;
1531
1551
  console.log(import_chalk5.default.green(`\u2713 Blocked ${what}.`));
1532
1552
  }
1533
1553
  async function runRemove(did, opts) {
@@ -1564,7 +1584,7 @@ var import_chalk6 = __toESM(require("chalk"));
1564
1584
  init_api();
1565
1585
  init_config();
1566
1586
  function registerConfigCommand(root) {
1567
- const config = root.command("config").description("Read/write persistent CLI configuration (~/.arp/config.json)").option("--server <url>", "Override the server URL whose bucket per-server keys (e.g. defaultDid) read/write to");
1587
+ const config = root.command("config").description("Read/write persistent CLI configuration (~/.heyarp/config.json)").option("--server <url>", "Override the server URL whose bucket per-server keys (e.g. defaultDid) read/write to");
1568
1588
  config.command("set <key> <value>").description(`Persist a config value. Global keys: ${GLOBAL_CONFIG_KEYS.join(", ")}. Per-server keys: ${SERVER_CONFIG_KEYS.join(", ")}`).action((key, value) => {
1569
1589
  const opts = config.opts();
1570
1590
  if (isGlobalConfigKey(key)) {
@@ -1643,14 +1663,15 @@ function unknownKey(key) {
1643
1663
 
1644
1664
  // src/commands/delegation.ts
1645
1665
  var import_node_fs4 = require("fs");
1646
- var import_sdk7 = require("@heyanon-arp/sdk");
1666
+ var import_sdk10 = require("@heyanon-arp/sdk");
1647
1667
  var import_chalk8 = __toESM(require("chalk"));
1648
1668
  init_api();
1649
1669
 
1650
1670
  // src/id-format.ts
1671
+ var import_sdk5 = require("@heyanon-arp/sdk");
1651
1672
  var UUID_RE = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;
1652
1673
  var OBJECT_ID_24_HEX_RE = /^[0-9a-f]{24}$/;
1653
- var SHA256_PREFIX_RE = /^sha256:[0-9a-f]{64}$/;
1674
+ var SHA256_PREFIX_RE = import_sdk5.SHA256_HEX_RE;
1654
1675
  var UUID_NO_DASHES_RE = /^[a-fA-F0-9]{32}$/;
1655
1676
  function describeNonUuidShape(raw) {
1656
1677
  if (raw === "") return "empty string";
@@ -1682,11 +1703,11 @@ function requireUuid(cmdName, raw, label) {
1682
1703
  }
1683
1704
 
1684
1705
  // src/commands/status.ts
1685
- var import_sdk4 = require("@heyanon-arp/sdk");
1706
+ var import_sdk6 = require("@heyanon-arp/sdk");
1686
1707
  var import_chalk7 = __toESM(require("chalk"));
1687
1708
  init_api();
1688
1709
  var UNTIL_PHASES = [
1689
- "delegation.proposed",
1710
+ "delegation.offered",
1690
1711
  "delegation.accepted",
1691
1712
  "delegation.locked",
1692
1713
  "delegation.disputing",
@@ -1695,7 +1716,6 @@ var UNTIL_PHASES = [
1695
1716
  "work.requested",
1696
1717
  "work.responded",
1697
1718
  "receipt.proposed",
1698
- "receipt.cosigned",
1699
1719
  "relationship.pending",
1700
1720
  "relationship.active",
1701
1721
  "relationship.paused",
@@ -1784,13 +1804,13 @@ async function awaitFsmTransitionAfterAction(input) {
1784
1804
  }
1785
1805
  }
1786
1806
  async function runWaitLoop(opts) {
1787
- const isTerminal = (s) => s.cycleComplete || s.relationshipState === "closed" || s.relationshipState === "not_found";
1807
+ const isTerminal = (s) => s.cycleComplete || s.relationshipState === import_sdk6.RelationshipStates.CLOSED || s.relationshipState === "not_found";
1788
1808
  const isActionable = (s) => {
1789
1809
  if (s.nextActionOwner === "me") {
1790
1810
  if (s.relationshipState === "unknown") return false;
1791
1811
  return true;
1792
1812
  }
1793
- if (s.nextActionOwner === "either" && s.relationshipState === "active") {
1813
+ if (s.nextActionOwner === "either" && s.relationshipState === import_sdk6.RelationshipStates.ACTIVE) {
1794
1814
  return true;
1795
1815
  }
1796
1816
  return false;
@@ -1926,43 +1946,41 @@ function parseUntilPhase(raw) {
1926
1946
  function isPhaseTerminallyUnreachable(phase, s) {
1927
1947
  if (untilPhaseMatched(phase, s)) return false;
1928
1948
  if (s.relationshipState === "not_found") return true;
1929
- if (s.relationshipState === "closed" && phase !== "relationship.closed") return true;
1949
+ if (s.relationshipState === import_sdk6.RelationshipStates.CLOSED && phase !== "relationship.closed") return true;
1930
1950
  return false;
1931
1951
  }
1932
1952
  function untilPhaseMatched(phase, s) {
1933
1953
  switch (phase) {
1934
- case "delegation.proposed":
1935
- return s.latestDelegation?.state === "proposed" || s.latestDelegation?.state === "offered";
1954
+ case "delegation.offered":
1955
+ return s.latestDelegation?.state === import_sdk6.DelegationStates.OFFERED;
1936
1956
  case "delegation.accepted":
1937
- return s.latestDelegation?.state === "accepted";
1957
+ return s.latestDelegation?.state === import_sdk6.DelegationStates.ACCEPTED;
1938
1958
  case "delegation.locked":
1939
- return s.latestDelegation?.state === "locked";
1959
+ return s.latestDelegation?.state === import_sdk6.DelegationStates.LOCKED;
1940
1960
  case "delegation.disputing":
1941
- return s.latestDelegation?.state === "disputing";
1961
+ return s.latestDelegation?.state === import_sdk6.DelegationStates.DISPUTING;
1942
1962
  case "delegation.canceled":
1943
- return s.latestDelegation?.state === "canceled";
1963
+ return s.latestDelegation?.state === import_sdk6.DelegationStates.CANCELED;
1944
1964
  case "delegation.declined":
1945
- return s.latestDelegation?.state === "declined";
1965
+ return s.latestDelegation?.state === import_sdk6.DelegationStates.DECLINED;
1946
1966
  case "work.requested":
1947
- return s.latestWorkLog?.state === "requested";
1967
+ return s.latestWorkLog?.state === import_sdk6.WorkLogStates.REQUESTED;
1948
1968
  case "work.responded":
1949
- return s.latestWorkLog?.state === "responded";
1969
+ return s.latestWorkLog?.state === import_sdk6.WorkLogStates.RESPONDED;
1950
1970
  case "receipt.proposed":
1951
- return s.latestReceipt?.state === "proposed";
1952
- case "receipt.cosigned":
1953
- return s.latestReceipt?.state === "cosigned";
1971
+ return s.latestReceipt != null;
1954
1972
  case "relationship.pending":
1955
- return s.relationshipState === "pending";
1973
+ return s.relationshipState === import_sdk6.RelationshipStates.PENDING;
1956
1974
  case "relationship.active":
1957
- return s.relationshipState === "active";
1975
+ return s.relationshipState === import_sdk6.RelationshipStates.ACTIVE;
1958
1976
  case "relationship.paused":
1959
- return s.relationshipState === "paused";
1977
+ return s.relationshipState === import_sdk6.RelationshipStates.PAUSED;
1960
1978
  case "relationship.closed":
1961
- return s.relationshipState === "closed";
1979
+ return s.relationshipState === import_sdk6.RelationshipStates.CLOSED;
1962
1980
  case "cycle.complete":
1963
1981
  return s.cycleComplete;
1964
1982
  case "cycle.released":
1965
- return s.latestDelegation?.state === "completed";
1983
+ return s.latestDelegation?.state === import_sdk6.DelegationStates.COMPLETED;
1966
1984
  }
1967
1985
  }
1968
1986
  function parseWaitInterval(raw) {
@@ -2013,7 +2031,7 @@ async function composeStatus(api, signerDid, relationshipId, signer) {
2013
2031
  const relationship = relationships.find((r) => r.relationshipId === relationshipId);
2014
2032
  const counterpartyDid = relationship ? relationship.pairDidA === signerDid ? relationship.pairDidB : relationship.pairDidA : inferCounterpartyFromEntities(signerDid, allDelegations);
2015
2033
  const delegations = allDelegations;
2016
- const latestDelegation = pickLatestLive(delegations, ["proposed", "offered", "accepted", "pending_lock_finalization", "locked"]);
2034
+ const latestDelegation = pickLatestLive(delegations, [...import_sdk6.DELEGATION_ACTIVE_STATES]);
2017
2035
  const [workLogs, receipts] = await Promise.all([
2018
2036
  latestDelegation ? fetchAllPages(
2019
2037
  (after) => api.listWorkLogs(relationshipId, signer, { limit: 100, delegationId: latestDelegation.delegationId, ...after ? { after } : {} })
@@ -2077,8 +2095,8 @@ function findReceiptForWorkLog(receipts, workLog, allWorkLogs = [workLog]) {
2077
2095
  };
2078
2096
  const responseBody = workLog.responseOutput !== void 0 ? { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, output: workLog.responseOutput } } : workLog.responseError !== void 0 ? { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, error: workLog.responseError } } : null;
2079
2097
  if (!responseBody) return null;
2080
- const expectedRequestHash = (0, import_sdk4.canonicalSha256Hex)(requestBody);
2081
- const expectedResponseHash = (0, import_sdk4.canonicalSha256Hex)(responseBody);
2098
+ const expectedRequestHash = (0, import_sdk6.canonicalSha256Hex)(requestBody);
2099
+ const expectedResponseHash = (0, import_sdk6.canonicalSha256Hex)(responseBody);
2082
2100
  const matches = receipts.filter((r) => r.requestHash === expectedRequestHash && r.responseHash === expectedResponseHash);
2083
2101
  if (matches.length > 0) return pickLatest(matches);
2084
2102
  const respondedSiblings = allWorkLogs.filter((wl) => wl.delegationId === workLog.delegationId && (wl.responseOutput !== void 0 || wl.responseError !== void 0)).length;
@@ -2120,49 +2138,49 @@ function nextAction(input) {
2120
2138
  complete: false
2121
2139
  };
2122
2140
  }
2123
- if (relationship.state === "pending") {
2141
+ if (relationship.state === import_sdk6.RelationshipStates.PENDING) {
2124
2142
  return {
2125
2143
  hint: "Relationship is PENDING \u2014 one side owes `heyarp send-handshake-response <peer> --decision accept | decline`",
2126
2144
  owner: "either",
2127
2145
  complete: false
2128
2146
  };
2129
2147
  }
2130
- if (relationship.state === "paused") {
2148
+ if (relationship.state === import_sdk6.RelationshipStates.PAUSED) {
2131
2149
  return { hint: "Relationship is PAUSED \u2014 owner must `heyarp unpause` before any envelopes flow", owner: "either", complete: false };
2132
2150
  }
2133
- if (relationship.state === "closed") {
2151
+ if (relationship.state === import_sdk6.RelationshipStates.CLOSED) {
2134
2152
  return { hint: "Relationship is CLOSED \u2014 terminal state, no further action possible. Start a fresh handshake to reopen.", owner: "none", complete: false };
2135
2153
  }
2136
- if (!latestDelegation || latestDelegation.state === "declined" || latestDelegation.state === "canceled") {
2154
+ if (!latestDelegation || latestDelegation.state === import_sdk6.DelegationStates.DECLINED || latestDelegation.state === import_sdk6.DelegationStates.CANCELED) {
2137
2155
  return {
2138
2156
  hint: "No live delegation \u2014 buyer offers (terms only, no lock) `heyarp delegation offer <peer> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --amount \u2026 --currency SOL:solana-devnet --deadline \u2026`, then funds the escrow lock AFTER the worker accepts via `heyarp delegation fund <del-id> --escrow-lock-from-file <path>`",
2139
2157
  owner: "either",
2140
2158
  complete: false
2141
2159
  };
2142
2160
  }
2143
- if (latestDelegation.state === "disputing") {
2161
+ if (latestDelegation.state === import_sdk6.DelegationStates.DISPUTING) {
2144
2162
  return {
2145
2163
  hint: `Delegation ${latestDelegation.delegationId} is DISPUTING \u2014 the buyer opened an on-chain dispute (their stake is escrowed too). The operator may rule until the dispute window lapses; after that either party closes with \`heyarp escrow dispute close ${latestDelegation.delegationId}\` (escrow returns to the buyer, both stakes return). Deadline: \`heyarp escrow show ${latestDelegation.delegationId}\`. No envelopes to send meanwhile.`,
2146
2164
  owner: "none",
2147
2165
  complete: false
2148
2166
  };
2149
2167
  }
2150
- if (latestDelegation.state === "completed") {
2168
+ if (latestDelegation.state === import_sdk6.DelegationStates.COMPLETED) {
2151
2169
  return {
2152
2170
  hint: "Cycle COMPLETE \u2014 payment claimed on chain (lock paid; the receipt carries releaseStatus=paid)",
2153
2171
  owner: "none",
2154
2172
  complete: true
2155
2173
  };
2156
2174
  }
2157
- if (latestDelegation.state === "failed" || latestDelegation.state === "refunded" || latestDelegation.state === "dispute_resolved") {
2175
+ if (latestDelegation.state === import_sdk6.DelegationStates.FAILED || latestDelegation.state === import_sdk6.DelegationStates.REFUNDED || latestDelegation.state === import_sdk6.DelegationStates.DISPUTE_RESOLVED) {
2158
2176
  const stateLabel = latestDelegation.state.toUpperCase();
2159
- const reason = latestDelegation.state === "failed" ? "on-chain escrow lock failed to finalise (typical causes: insufficient payer funds, wrong program-id, ProgramState PDA uninitialised \u2014 check `heyarp delegations <rel-id> --json | jq .[].escrowError` for the worker-side reason)" : latestDelegation.state === "refunded" ? (
2177
+ const reason = latestDelegation.state === import_sdk6.DelegationStates.FAILED ? "on-chain escrow lock failed to finalise (typical causes: insufficient payer funds, wrong program-id, ProgramState PDA uninitialised \u2014 check `heyarp delegations <rel-id> --json | jq .[].escrowError` for the worker-side reason)" : latestDelegation.state === import_sdk6.DelegationStates.REFUNDED ? (
2160
2178
  // Keyed on the DELEGATION row's releaseStatus, not the
2161
2179
  // receipt's: in the claim-expired path the worker never
2162
2180
  // submitted anything, so no receipt row exists to carry
2163
2181
  // the outcome — the delegation copy is always present
2164
2182
  // on indexer-driven terminals.
2165
- latestDelegation.releaseStatus === "dispute_closed" ? "the dispute window lapsed without an operator ruling and the dispute was closed \u2014 escrow returned to the buyer, both stakes returned (work was delivered but not paid)" : latestDelegation.releaseStatus === "revoked" ? "the work window lapsed without an on-chain submit \u2014 the buyer reclaimed the escrow AND the worker stake (claim_expired_work)" : "lock was refunded after expiry \u2014 no work was delivered against this delegation"
2183
+ latestDelegation.releaseStatus === import_sdk6.LockStates.DISPUTE_CLOSED ? "the dispute window lapsed without an operator ruling and the dispute was closed \u2014 escrow returned to the buyer, both stakes returned (work was delivered but not paid)" : latestDelegation.releaseStatus === import_sdk6.LockStates.REVOKED ? "the work window lapsed without an on-chain submit \u2014 the buyer reclaimed the escrow AND the worker stake (claim_expired_work)" : "lock was refunded after expiry \u2014 no work was delivered against this delegation"
2166
2184
  ) : "admin closed the delegation via the dispute-resolution path";
2167
2185
  return {
2168
2186
  hint: `Delegation ${latestDelegation.delegationId} is ${stateLabel} (terminal) \u2014 ${reason}. Issue a fresh \`heyarp delegation offer <peer> --delegation-id <new-uuid> \u2026\` to retry (FSM does not auto-retry).`,
@@ -2170,16 +2188,16 @@ function nextAction(input) {
2170
2188
  complete: false
2171
2189
  };
2172
2190
  }
2173
- if (latestDelegation.state === "proposed" || latestDelegation.state === "offered") {
2191
+ if (latestDelegation.state === import_sdk6.DelegationStates.OFFERED) {
2174
2192
  const iAmOfferer = latestDelegation.offererDid === signerDid;
2175
- const stateLabel = latestDelegation.state === "proposed" ? "PROPOSED" : `${latestDelegation.state.toUpperCase()} (\u2261 proposed)`;
2193
+ const stateLabel = "OFFERED";
2176
2194
  return {
2177
2195
  hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is ${stateLabel} \u2014 counterparty owes \`heyarp delegation accept\` / \`decline\`` : `Delegation ${latestDelegation.delegationId} is ${stateLabel} \u2014 you owe \`heyarp delegation accept\` / \`decline\``,
2178
2196
  owner: iAmOfferer ? "counterparty" : "me",
2179
2197
  complete: false
2180
2198
  };
2181
2199
  }
2182
- if (latestDelegation.state === "accepted" && !latestWorkLog) {
2200
+ if (latestDelegation.state === import_sdk6.DelegationStates.ACCEPTED && !latestWorkLog) {
2183
2201
  const iAmOfferer = latestDelegation.offererDid === signerDid;
2184
2202
  return {
2185
2203
  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`,
@@ -2187,7 +2205,7 @@ function nextAction(input) {
2187
2205
  complete: false
2188
2206
  };
2189
2207
  }
2190
- if (latestDelegation.state === "pending_lock_finalization" && !latestWorkLog) {
2208
+ if (latestDelegation.state === import_sdk6.DelegationStates.PENDING_LOCK_FINALIZATION && !latestWorkLog) {
2191
2209
  return {
2192
2210
  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\`.`,
2193
2211
  owner: "none",
@@ -2202,7 +2220,7 @@ function nextAction(input) {
2202
2220
  complete: false
2203
2221
  };
2204
2222
  }
2205
- if (latestWorkLog.state === "requested") {
2223
+ if (latestWorkLog.state === import_sdk6.WorkLogStates.REQUESTED) {
2206
2224
  const iAmPayee = latestWorkLog.payeeDid === signerDid;
2207
2225
  return {
2208
2226
  hint: iAmPayee ? `work_request ${latestWorkLog.requestId} is REQUESTED \u2014 you owe \`heyarp work respond\` (output OR --error)` : `work_request ${latestWorkLog.requestId} is REQUESTED \u2014 counterparty (payee) owes \`heyarp work respond\``,
@@ -2233,24 +2251,21 @@ function nextAction(input) {
2233
2251
  complete: false
2234
2252
  };
2235
2253
  }
2236
- if (latestReceipt.state === "proposed") {
2237
- const iAmCaller = latestReceipt.callerDid === signerDid;
2238
- return {
2239
- // V2: there is NO cosign — buyer consent is the on-chain
2240
- // claim_work_payment tx, worker recourse is the
2241
- // review-window self-claim.
2242
- hint: iAmCaller ? `Receipt PROPOSED \u2014 you (caller): review the deliverable and approve payment ON-CHAIN with \`heyarp escrow claim ${latestReceipt.delegationId}\` (irreversible release; \`heyarp escrow dispute open ${latestReceipt.delegationId}\` within the review window if the work is unacceptable)` : `Receipt PROPOSED \u2014 counterparty (the buyer) owes the on-chain approval (\`heyarp escrow claim\`); once the review window lapses you may self-claim with \`heyarp escrow claim ${latestReceipt.delegationId}\``,
2243
- owner: iAmCaller ? "me" : "counterparty",
2244
- complete: false
2245
- };
2246
- }
2247
- return { hint: "Cycle COMPLETE \u2014 settlement recorded", owner: "none", complete: true };
2254
+ const iAmCaller = latestReceipt.callerDid === signerDid;
2255
+ return {
2256
+ // There is no cosign step — buyer consent is the on-chain
2257
+ // claim_work_payment tx, worker recourse is the
2258
+ // review-window self-claim.
2259
+ hint: iAmCaller ? `Receipt PROPOSED \u2014 you (caller): review the deliverable and approve payment ON-CHAIN with \`heyarp escrow claim ${latestReceipt.delegationId}\` (irreversible release; \`heyarp escrow dispute open ${latestReceipt.delegationId}\` within the review window if the work is unacceptable)` : `Receipt PROPOSED \u2014 counterparty (the buyer) owes the on-chain approval (\`heyarp escrow claim\`); once the review window lapses you may self-claim with \`heyarp escrow claim ${latestReceipt.delegationId}\``,
2260
+ owner: iAmCaller ? "me" : "counterparty",
2261
+ complete: false
2262
+ };
2248
2263
  }
2249
2264
  function formatPhaseSnapshot(s) {
2250
2265
  const parts = [];
2251
2266
  parts.push(`delegation=${s.latestDelegation?.state ?? "\u2205"}`);
2252
2267
  if (s.latestWorkLog) parts.push(`work=${s.latestWorkLog.state}`);
2253
- if (s.latestReceipt) parts.push(`receipt=${s.latestReceipt.state}`);
2268
+ if (s.latestReceipt) parts.push("receipt=proposed");
2254
2269
  return parts.join(", ");
2255
2270
  }
2256
2271
  function formatStatusReport(s) {
@@ -2280,7 +2295,7 @@ function formatStatusReport(s) {
2280
2295
  lines.push("");
2281
2296
  lines.push(import_chalk7.default.bold("Latest receipt"));
2282
2297
  if (s.latestReceipt) {
2283
- lines.push(` state=${stateColor(s.latestReceipt.state)} verdict=${s.latestReceipt.verdictFinal ?? s.latestReceipt.verdictProposed}`);
2298
+ lines.push(` ${stateColor("proposed")} verdict=${s.latestReceipt.verdictProposed}`);
2284
2299
  } else {
2285
2300
  lines.push(import_chalk7.default.dim(" (none)"));
2286
2301
  }
@@ -2294,26 +2309,26 @@ function formatStatusReport(s) {
2294
2309
  }
2295
2310
  function cycleHeadline(s) {
2296
2311
  if (s.cycleComplete) {
2297
- return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.green.bold("COMPLETE")} ${import_chalk7.default.dim("\u2014 receipt cosigned, payment proven on chain")}`;
2312
+ return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.green.bold("COMPLETE")} ${import_chalk7.default.dim("\u2014 payment proven on chain")}`;
2298
2313
  }
2299
2314
  switch (s.relationshipState) {
2300
2315
  case "not_found":
2301
2316
  return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.red.bold("NOT FOUND")} ${import_chalk7.default.dim("\u2014 relationship missing or signer is not a member")}`;
2302
2317
  case "unknown":
2303
2318
  return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.yellow.bold("UNKNOWN")} ${import_chalk7.default.dim("\u2014 state probe inconclusive; hint below is advisory")}`;
2304
- case "closed":
2319
+ case import_sdk6.RelationshipStates.CLOSED:
2305
2320
  return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.red.bold("CLOSED")} ${import_chalk7.default.dim("\u2014 relationship terminal, no further protocol action")}`;
2306
- case "paused":
2321
+ case import_sdk6.RelationshipStates.PAUSED:
2307
2322
  return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.yellow.bold("PAUSED")} ${import_chalk7.default.dim("\u2014 relationship soft-disabled, resume to continue")}`;
2308
- case "pending":
2323
+ case import_sdk6.RelationshipStates.PENDING:
2309
2324
  return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.yellow.bold("PENDING")} ${import_chalk7.default.dim("\u2014 awaiting handshake_response")}`;
2310
- case "active":
2325
+ case import_sdk6.RelationshipStates.ACTIVE:
2311
2326
  default:
2312
2327
  return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.cyan.bold("ACTIVE")} ${import_chalk7.default.dim('\u2014 work in progress; see "Next action" below')}`;
2313
2328
  }
2314
2329
  }
2315
2330
  function stateColor(state) {
2316
- const c = state === "active" || state === "accepted" || state === "locked" || state === "cosigned" || state === "responded" || state === "completed" ? import_chalk7.default.green : state === "pending" || state === "proposed" || state === "requested" || state === "offered" || state === "pending_lock_finalization" ? import_chalk7.default.yellow : state === "closed" || state === "declined" || state === "canceled" || state === "replaced" || state === "not_found" || // Terminal failure states deserve the same red
2331
+ const c = state === "active" || state === "accepted" || state === "locked" || state === "responded" || state === "completed" ? import_chalk7.default.green : state === "pending" || state === "requested" || state === "offered" || state === "pending_lock_finalization" ? import_chalk7.default.yellow : state === "closed" || state === "declined" || state === "canceled" || state === "replaced" || state === "not_found" || // Terminal failure states deserve the same red
2317
2332
  // treatment as declined/canceled so an operator
2318
2333
  // scanning `heyarp status` immediately sees
2319
2334
  // "this is dead, don't follow the hint blindly".
@@ -2322,41 +2337,41 @@ function stateColor(state) {
2322
2337
  }
2323
2338
 
2324
2339
  // src/commands/wallet.ts
2325
- var import_sdk6 = require("@heyanon-arp/sdk");
2340
+ var import_sdk9 = require("@heyanon-arp/sdk");
2326
2341
  var import_utils = require("@noble/hashes/utils");
2327
2342
  var import_web32 = require("@solana/web3.js");
2328
2343
  init_api();
2329
2344
  init_config();
2330
2345
 
2331
2346
  // src/solana/escrow-ix.ts
2332
- var import_sdk5 = require("@heyanon-arp/sdk");
2347
+ var import_sdk7 = require("@heyanon-arp/sdk");
2333
2348
  var import_web3 = require("@solana/web3.js");
2334
- var SPL_TOKEN_PROGRAM_ID = new import_web3.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
2335
- var ASSOCIATED_TOKEN_PROGRAM_ID = new import_web3.PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
2336
- var NATIVE_SOL_MINT = new import_web3.PublicKey(new Uint8Array(32));
2349
+ var SPL_TOKEN_PROGRAM_ID = new import_web3.PublicKey(import_sdk7.SPL_TOKEN_PROGRAM_ID_BASE58);
2350
+ var ASSOCIATED_TOKEN_PROGRAM_ID = new import_web3.PublicKey(import_sdk7.ASSOCIATED_TOKEN_PROGRAM_ID_BASE58);
2351
+ var NATIVE_SOL_MINT = new import_web3.PublicKey(import_sdk7.NATIVE_SOL_MINT_BASE58);
2337
2352
  function deriveLockPda(programId, lockId) {
2338
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.LOCK), Buffer.from(lockId)], programId)[0];
2353
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.LOCK), Buffer.from(lockId)], programId)[0];
2339
2354
  }
2340
2355
  function deriveEscrowPda(programId, lockId) {
2341
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.ESCROW), Buffer.from(lockId)], programId)[0];
2356
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.ESCROW), Buffer.from(lockId)], programId)[0];
2342
2357
  }
2343
2358
  function deriveConfigPda(programId) {
2344
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.CONFIG)], programId)[0];
2359
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.CONFIG)], programId)[0];
2345
2360
  }
2346
2361
  function deriveStakeVaultPda(programId) {
2347
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.STAKE_VAULT)], programId)[0];
2362
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.STAKE_VAULT)], programId)[0];
2348
2363
  }
2349
2364
  function deriveCollateralConfigPda(programId, mint) {
2350
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.COLLATERAL), mint.toBuffer()], programId)[0];
2365
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.COLLATERAL), mint.toBuffer()], programId)[0];
2351
2366
  }
2352
2367
  function deriveDisputeResolutionPda(programId, lockId) {
2353
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.DISPUTE_RESOLUTION), Buffer.from(lockId)], programId)[0];
2368
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.DISPUTE_RESOLUTION), Buffer.from(lockId)], programId)[0];
2354
2369
  }
2355
2370
  function deriveOperatorAuthPda(programId, operator) {
2356
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.OPERATOR_AUTH), operator.toBuffer()], programId)[0];
2371
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.OPERATOR_AUTH), operator.toBuffer()], programId)[0];
2357
2372
  }
2358
2373
  function deriveEventAuthorityPda(programId) {
2359
- return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk5.ESCROW_PDA_SEEDS.EVENT_AUTHORITY)], programId)[0];
2374
+ return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.EVENT_AUTHORITY)], programId)[0];
2360
2375
  }
2361
2376
  function deriveAta(owner, mint) {
2362
2377
  return import_web3.PublicKey.findProgramAddressSync([owner.toBuffer(), SPL_TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
@@ -2371,12 +2386,12 @@ function meta(pubkey, opts = {}) {
2371
2386
  return { pubkey, isSigner: opts.signer === true, isWritable: opts.writable === true };
2372
2387
  }
2373
2388
  function noArgIx(programId, name, keys) {
2374
- return new import_web3.TransactionInstruction({ programId, keys, data: Buffer.from((0, import_sdk5.buildLifecycleIxData)(name)) });
2389
+ return new import_web3.TransactionInstruction({ programId, keys, data: Buffer.from((0, import_sdk7.buildLifecycleIxData)(name)) });
2375
2390
  }
2376
2391
  function buildCreateLockIx(input) {
2377
2392
  const { programId, lockId } = input;
2378
2393
  const native = input.mint === null;
2379
- const data = Buffer.from((0, import_sdk5.buildCreateLockIxData)({ lockId, amount: input.amount, conditionHash: input.conditionHash }, { native }));
2394
+ const data = Buffer.from((0, import_sdk7.buildCreateLockIxData)({ lockId, amount: input.amount, conditionHash: input.conditionHash }, { native }));
2380
2395
  const head = [
2381
2396
  meta(input.payer, { signer: true, writable: true }),
2382
2397
  meta(input.payee),
@@ -2512,7 +2527,7 @@ function buildOpenDisputeIx(input) {
2512
2527
  function buildResolveDisputeIx(input) {
2513
2528
  const { programId, lockId } = input;
2514
2529
  const native = input.mint === null;
2515
- const data = Buffer.from((0, import_sdk5.buildResolveDisputeIxData)(input.args, { native }));
2530
+ const data = Buffer.from((0, import_sdk7.buildResolveDisputeIxData)(input.args, { native }));
2516
2531
  if (native) {
2517
2532
  return new import_web3.TransactionInstruction({
2518
2533
  programId,
@@ -2589,16 +2604,17 @@ function buildCloseDisputeIx(input) {
2589
2604
  ]);
2590
2605
  }
2591
2606
  async function fetchLockAccount(conn, programId, delegationId) {
2592
- const lockId = (0, import_sdk5.deriveLockId)(delegationId);
2607
+ const lockId = (0, import_sdk7.deriveLockId)(delegationId);
2593
2608
  const lockPda = deriveLockPda(programId, lockId);
2594
2609
  const info = await conn.getAccountInfo(lockPda);
2595
2610
  if (!info) return null;
2596
- return { lockId, lockPda, lock: (0, import_sdk5.decodeLockAccount)(Uint8Array.from(info.data)) };
2611
+ return { lockId, lockPda, lock: (0, import_sdk7.decodeLockAccount)(Uint8Array.from(info.data)) };
2597
2612
  }
2598
2613
 
2599
2614
  // src/commands/token-amount.ts
2615
+ var import_sdk8 = require("@heyanon-arp/sdk");
2600
2616
  function toBaseUnits(amountDecimal, decimals) {
2601
- if (!/^[0-9]+(\.[0-9]+)?$/.test(amountDecimal)) {
2617
+ if (!(0, import_sdk8.isDecimalAmountString)(amountDecimal)) {
2602
2618
  throw new Error(`amount '${amountDecimal}' is not a non-negative decimal number`);
2603
2619
  }
2604
2620
  if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
@@ -2627,8 +2643,8 @@ function normaliseDelegationId(raw) {
2627
2643
  }
2628
2644
  throw new Error(`wallet: --delegation-id must be either 'del_<uuid>' or a bare canonical-lowercase UUID (got '${raw}')`);
2629
2645
  }
2630
- var SPL_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
2631
- var ASSOCIATED_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
2646
+ var SPL_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey(import_sdk9.SPL_TOKEN_PROGRAM_ID_BASE58);
2647
+ var ASSOCIATED_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey(import_sdk9.ASSOCIATED_TOKEN_PROGRAM_ID_BASE58);
2632
2648
  var FALLBACK_RPC_URL = "https://api.mainnet-beta.solana.com";
2633
2649
  function resolveRpcUrl(opts) {
2634
2650
  if (opts.rpcUrl !== void 0 && opts.rpcUrl !== "") return opts.rpcUrl;
@@ -2639,7 +2655,16 @@ function resolveRpcUrl(opts) {
2639
2655
  if (fromConfig !== void 0 && fromConfig.length > 0) return fromConfig;
2640
2656
  return FALLBACK_RPC_URL;
2641
2657
  }
2642
- var FALLBACK_PROGRAM_ID = "7trAdKybX4kMKARia9nrRPj9rBuUjDTxRzExzwFTvvXg";
2658
+ function redactRpcUrl(url) {
2659
+ try {
2660
+ const u = new URL(url);
2661
+ const hasSecret = u.pathname !== "" && u.pathname !== "/" || u.search !== "" || u.username !== "" || u.password !== "";
2662
+ return hasSecret ? `${u.protocol}//${u.host}/<redacted>` : url;
2663
+ } catch {
2664
+ return "<redacted>";
2665
+ }
2666
+ }
2667
+ var FALLBACK_PROGRAM_ID = import_sdk9.ESCROW_PROGRAM_ID_BASE58;
2643
2668
  async function resolveProgramIdWithSource(api, opts) {
2644
2669
  if (opts.programId !== void 0 && opts.programId !== "") {
2645
2670
  return { programId: opts.programId, source: "flag" };
@@ -2699,10 +2724,7 @@ function registerDerivePdas(cmd) {
2699
2724
  ).requiredOption("--delegation-id <id>", "Delegation UUID (canonical `del_<uuid>` or bare `<uuid>` \u2014 both accepted)").option("--server <url>", "Override server URL (used only for --program-id auto-discovery)").option(
2700
2725
  "--program-id <pubkey>",
2701
2726
  "Deployed ARP escrow program id. Precedence: --program-id flag > ARP_ESCROW_PROGRAM_ID env > GET /v1/escrow/config (auto-discover). NO hardcoded fallback \u2014 wrong-program PDAs are silently wrong, so resolution failure is a loud error."
2702
- ).option("--rpc-url <url>", "Accepted for symmetry with `wallet verify-release` but never read \u2014 PDAs are derived locally from `program_id + delegation_id`; no RPC needed.").option(
2703
- "--recipient-pubkey <base58>",
2704
- "Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id; recipient pubkey has no derivation role."
2705
- ).option("--condition-hash <hex>", "Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id.").option("--amount-lamports <int>", "Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id.").option("--expiry-secs <int>", "Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id.").option("--json", "Emit JSON instead of human text (jq-pipeable: `.lock_pda`, `.escrow_pda`, `.config_pda`, `.lock_id_hex`).", false).action(async (opts) => {
2727
+ ).option("--json", "Emit JSON instead of human text (jq-pipeable: `.lock_pda`, `.escrow_pda`, `.config_pda`, `.lock_id_hex`).", false).action(async (opts) => {
2706
2728
  try {
2707
2729
  const out = await derivePdasHandler(opts);
2708
2730
  if (opts.json) {
@@ -2726,7 +2748,7 @@ async function derivePdasHandler(opts) {
2726
2748
  const normalisedDelegationId = normaliseDelegationId(opts.delegationId);
2727
2749
  const api = new ArpApiClient(opts.server);
2728
2750
  const programId = new import_web32.PublicKey(await resolveProgramIdStrict(api, opts, "wallet derive-pdas"));
2729
- const lockIdBytes = (0, import_sdk6.deriveLockId)(normalisedDelegationId);
2751
+ const lockIdBytes = (0, import_sdk9.deriveLockId)(normalisedDelegationId);
2730
2752
  const lockIdSeed = Buffer.from(lockIdBytes);
2731
2753
  const lockIdHex = Buffer.from(lockIdBytes).toString("hex");
2732
2754
  const [lockPda] = import_web32.PublicKey.findProgramAddressSync([Buffer.from("lock"), lockIdSeed], programId);
@@ -2738,30 +2760,29 @@ async function derivePdasHandler(opts) {
2738
2760
  lock_id_hex: lockIdHex,
2739
2761
  program_id: programId.toBase58(),
2740
2762
  lock_pda: lockPda.toBase58(),
2741
- // Single escrow PDA replaces the V1.5 vault + vault_authority
2742
- // pair in the new contract.
2763
+ // Single escrow PDA at `[b"escrow", lock_id]`.
2743
2764
  escrow_pda: escrowPda.toBase58(),
2744
2765
  config_pda: configPda.toBase58(),
2745
2766
  event_authority_pda: eventAuthorityPda.toBase58()
2746
2767
  };
2747
2768
  }
2748
2769
  var TERMINAL_METHOD = {
2749
- paid: "claim_work_payment",
2750
- canceled: "cancel_lock",
2751
- revoked: "claim_expired_work",
2752
- dispute_resolved: "resolve_dispute",
2753
- dispute_closed: "close_dispute"
2770
+ [import_sdk9.LockStates.PAID]: "claim_work_payment",
2771
+ [import_sdk9.LockStates.CANCELED]: "cancel_lock",
2772
+ [import_sdk9.LockStates.REVOKED]: "claim_expired_work",
2773
+ [import_sdk9.LockStates.DISPUTE_RESOLVED]: "resolve_dispute",
2774
+ [import_sdk9.LockStates.DISPUTE_CLOSED]: "close_dispute"
2754
2775
  };
2755
2776
  function registerVerifyRelease(cmd) {
2756
2777
  cmd.command("verify-release").description(
2757
- "Post-cycle on-chain assertion. Decodes the Lock account and reports the V2 state verdict: `released: true` IFF the lock is `paid` (the payee was paid via claim_work_payment); exits non-zero otherwise so `set -euo pipefail` scripts catch incomplete cycles. For the full decoded Lock use `heyarp escrow show`."
2778
+ "Post-cycle on-chain assertion. Decodes the Lock account and reports the state verdict: `released: true` IFF the lock is `paid` (the payee was paid via claim_work_payment); exits non-zero otherwise so `set -euo pipefail` scripts catch incomplete cycles. For the full decoded Lock use `heyarp escrow show`."
2758
2779
  ).requiredOption("--delegation-id <id>", "Delegation UUID (canonical `del_<uuid>` or bare `<uuid>` \u2014 both accepted)").option("--server <url>", "Override ARP server URL (used only for --program-id auto-discovery)").option(
2759
2780
  "--rpc-url <url>",
2760
2781
  "Solana RPC URL. STRICT: --rpc-url flag > ARP_ESCROW_RPC_URL env > `heyarp config set rpcUrl` > FAIL \u2014 a wrong-cluster read would silently report lock_never_created."
2761
2782
  ).option(
2762
2783
  "--program-id <pubkey>",
2763
2784
  "Deployed ARP escrow program id. STRICT: flag > ARP_ESCROW_PROGRAM_ID env > GET /v1/escrow/config > FAIL \u2014 a wrong-program read would silently report lock_never_created."
2764
- ).option("--no-sig-retry", "V1 leftover, tolerated no-op \u2014 V2 locks are never closed, so there is no signature-index race to retry around.").option("--json", "Emit JSON instead of human text. jq-pipeable.", false).action(async (opts) => {
2785
+ ).option("--from-did <did>", "Accepted for scripting symmetry with the signing commands; verify-release reads on-chain state only and needs no signer, so this is ignored.").option("--json", "Emit JSON instead of human text. jq-pipeable.", false).action(async (opts) => {
2765
2786
  try {
2766
2787
  const out = await verifyReleaseHandler(opts);
2767
2788
  if (opts.json) {
@@ -2790,16 +2811,13 @@ function registerVerifyRelease(cmd) {
2790
2811
  });
2791
2812
  }
2792
2813
  async function verifyReleaseHandler(opts) {
2793
- if (opts.noSigRetry) {
2794
- process.stderr.write("wallet verify-release: --no-sig-retry is a no-op on V2 (locks are never closed) \u2014 ignoring\n");
2795
- }
2796
2814
  const normalisedDelegationId = normaliseDelegationId(opts.delegationId);
2797
2815
  const api = new ArpApiClient(opts.server);
2798
2816
  const programId = new import_web32.PublicKey(await resolveProgramIdStrict(api, opts));
2799
2817
  const rpcUrl = resolveRpcUrlStrict(opts);
2800
2818
  const conn = new import_web32.Connection(rpcUrl, "confirmed");
2801
2819
  const fetched = await fetchLockAccount(conn, programId, normalisedDelegationId);
2802
- const lockId = (0, import_sdk6.deriveLockId)(normalisedDelegationId);
2820
+ const lockId = (0, import_sdk9.deriveLockId)(normalisedDelegationId);
2803
2821
  const lockPda = deriveLockPda(programId, lockId);
2804
2822
  const escrowPda = deriveEscrowPda(programId, lockId);
2805
2823
  if (!fetched) {
@@ -2810,7 +2828,7 @@ async function verifyReleaseHandler(opts) {
2810
2828
  lock_account_exists: false,
2811
2829
  released: false,
2812
2830
  status: "lock_never_created",
2813
- rpc_url: rpcUrl
2831
+ rpc_url: redactRpcUrl(rpcUrl)
2814
2832
  };
2815
2833
  }
2816
2834
  const lock = fetched.lock;
@@ -2820,7 +2838,7 @@ async function verifyReleaseHandler(opts) {
2820
2838
  lock_pda: lockPda.toBase58(),
2821
2839
  escrow_pda: escrowPda.toBase58(),
2822
2840
  lock_account_exists: true,
2823
- released: status === "paid",
2841
+ released: status === import_sdk9.LockStates.PAID,
2824
2842
  status,
2825
2843
  ...TERMINAL_METHOD[status] !== void 0 ? { release_method: TERMINAL_METHOD[status] } : {},
2826
2844
  lock_state: lock.stateByte,
@@ -2830,23 +2848,23 @@ async function verifyReleaseHandler(opts) {
2830
2848
  }
2831
2849
  function renderStatusLine(status) {
2832
2850
  switch (status) {
2833
- case "paid":
2851
+ case import_sdk9.LockStates.PAID:
2834
2852
  return "\u2713 paid \u2014 claim_work_payment landed (buyer approval or post-review self-claim); the payee was paid net of any protocol fee";
2835
- case "created":
2853
+ case import_sdk9.LockStates.CREATED:
2836
2854
  return "\u2717 created \u2014 funded, awaiting the worker accept_lock (stake) \u2014 or a buyer cancel";
2837
- case "in_progress":
2855
+ case import_sdk9.LockStates.IN_PROGRESS:
2838
2856
  return "\u2717 in_progress \u2014 worker accepted + staked; work window running";
2839
- case "submitted":
2857
+ case import_sdk9.LockStates.SUBMITTED:
2840
2858
  return "\u2717 submitted \u2014 work delivered on-chain; review window running (buyer claims to approve, disputes to refuse; worker self-claims after expiry)";
2841
- case "disputing":
2859
+ case import_sdk9.LockStates.DISPUTING:
2842
2860
  return "\u2717 disputing \u2014 buyer disputed; operator has the dispute window to rule, after that either party can close";
2843
- case "canceled":
2861
+ case import_sdk9.LockStates.CANCELED:
2844
2862
  return "\u2717 canceled \u2014 buyer canceled pre-accept; escrow returned";
2845
- case "revoked":
2863
+ case import_sdk9.LockStates.REVOKED:
2846
2864
  return "\u2717 revoked \u2014 work window lapsed unsubmitted; buyer reclaimed the escrow + the worker stake";
2847
- case "dispute_resolved":
2865
+ case import_sdk9.LockStates.DISPUTE_RESOLVED:
2848
2866
  return "\u2717 dispute_resolved \u2014 operator ruled; winner took the escrow per the on-chain split";
2849
- case "dispute_closed":
2867
+ case import_sdk9.LockStates.DISPUTE_CLOSED:
2850
2868
  return "\u2717 dispute_closed \u2014 dispute window lapsed unresolved; escrow returned to the buyer, stakes returned";
2851
2869
  case "lock_never_created":
2852
2870
  return "\u2717 lock_never_created \u2014 no Lock PDA on this cluster/program; create_lock never fired (or wrong --rpc-url/--program-id)";
@@ -2854,14 +2872,11 @@ function renderStatusLine(status) {
2854
2872
  }
2855
2873
  function registerCreateLock(cmd) {
2856
2874
  cmd.command("create-lock").description(
2857
- "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."
2875
+ "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. 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."
2858
2876
  ).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(
2859
2877
  "--amount <decimal>",
2860
2878
  "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."
2861
2879
  ).requiredOption("--condition-hash <hex>", "32-byte hex condition_hash from `heyarp escrow derive-condition-hash` (binds the delegation terms)").option(
2862
- "--expiry-secs <int>",
2863
- "DEPRECATED no-op \u2014 V2 derives deadlines from the on-chain Config windows; the flag is tolerated so old scripts keep running and dies with the V2 builder rewrite."
2864
- ).option(
2865
2880
  "--rpc-url <url>",
2866
2881
  `Solana RPC URL. Default precedence: --rpc-url flag > ARP_ESCROW_RPC_URL env > \`heyarp config set rpcUrl\` > built-in fallback (${FALLBACK_RPC_URL}).`
2867
2882
  ).option(
@@ -3029,16 +3044,13 @@ async function createLockHandler(opts) {
3029
3044
  const normalisedDelegationId = normaliseDelegationId(opts.delegationId);
3030
3045
  const payee = parsePubkey(opts.recipientPubkey, "--recipient-pubkey");
3031
3046
  const conditionHash = parseHex32(opts.conditionHash, "--condition-hash");
3032
- if (opts.expirySecs !== void 0) {
3033
- process.stderr.write("wallet create-lock: --expiry-secs is a no-op on V2 (deadlines come from the on-chain Config windows) \u2014 ignoring\n");
3034
- }
3035
3047
  const api = new ArpApiClient(opts.server);
3036
3048
  const offlineMode = typeof opts.programId === "string" && opts.programId !== "" || typeof process.env.ARP_ESCROW_PROGRAM_ID === "string" && process.env.ARP_ESCROW_PROGRAM_ID !== "";
3037
3049
  const clusterTag = opts.clusterTag !== void 0 ? Number.parseInt(opts.clusterTag, 10) : 0;
3038
3050
  if (clusterTag !== 0 && clusterTag !== 1) {
3039
3051
  throw new Error(`--cluster-tag must be 0 (devnet) or 1 (mainnet) (got ${clusterTag})`);
3040
3052
  }
3041
- const clusterCaip2 = clusterTag === 1 ? import_sdk6.SOLANA_CLUSTER_IDS["solana-mainnet"] : import_sdk6.SOLANA_CLUSTER_IDS["solana-devnet"];
3053
+ const clusterCaip2 = clusterTag === 1 ? import_sdk9.SOLANA_CLUSTER_IDS["solana-mainnet"] : import_sdk9.SOLANA_CLUSTER_IDS["solana-devnet"];
3042
3054
  const expectedLockAsset = typeof opts.mintPubkey === "string" && opts.mintPubkey !== "" ? { kind: "spl", mint: parsePubkey(opts.mintPubkey, "--mint-pubkey").toBase58(), cluster: clusterCaip2 } : { kind: "native" };
3043
3055
  if (!offlineMode) {
3044
3056
  await preflightLockCurrency(api, agent, normalisedDelegationId, expectedLockAsset);
@@ -3048,7 +3060,7 @@ async function createLockHandler(opts) {
3048
3060
  const conn = new import_web32.Connection(rpcUrl, "confirmed");
3049
3061
  const asset = await resolveCreateLockAsset(conn, opts, payerKp.publicKey, clusterCaip2);
3050
3062
  const amount = asset.amount;
3051
- const lockIdBytes = (0, import_sdk6.deriveLockId)(normalisedDelegationId);
3063
+ const lockIdBytes = (0, import_sdk9.deriveLockId)(normalisedDelegationId);
3052
3064
  const ix = buildCreateLockIx({
3053
3065
  programId,
3054
3066
  lockId: lockIdBytes,
@@ -3132,63 +3144,12 @@ function registerDelegationCommands(root) {
3132
3144
  registerDecline(cmd);
3133
3145
  registerCancel(cmd);
3134
3146
  }
3135
- var POST_COMMIT_ERROR_CODES = /* @__PURE__ */ new Set([
3136
- "DELEGATION_ALREADY_EXISTS",
3137
- "DELEGATION_INVALID_STATE",
3138
- "DELEGATION_NOT_FOUND",
3139
- "DELEGATION_RELATIONSHIP_MISMATCH",
3140
- "DELEGATION_ACCEPTER_IS_OFFERER",
3141
- "DELEGATION_DECLINER_IS_OFFERER",
3142
- "DELEGATION_CANCELER_NOT_OFFERER",
3143
- // PricingPolicy: the offer's terms fell outside the RECIPIENT's
3144
- // published accept-prefs. Fires in `handleOffer` AFTER the event
3145
- // row commit (pre-materialization on the server), so the sequence
3146
- // was consumed — advance it or the corrected re-offer trips
3147
- // ENV_SEQUENCE_BACKWARDS.
3148
- // Asset whitelist gate: the offer currency is not a whitelisted
3149
- // payment asset on this server (or its decimals diverge from the
3150
- // canonical entry). Same lifecycle as the pricing gate — sequence
3151
- // consumed. Fix the --currency and re-offer.
3152
- "DELEGATION_ASSET_NOT_ALLOWED",
3153
- "DELEGATION_PRICING_MISMATCH",
3154
- // Capacity gate: the recipient is at its published
3155
- // maxActiveDelegations cap (or closed with 0). Same lifecycle as
3156
- // the pricing gate — sequence consumed. TRANSIENT: retry later.
3157
- "DELEGATION_CAPACITY_EXCEEDED",
3158
- // `DELEGATION_PENDING_LOCK` fires from the body handler's
3159
- // `requireDelegationInState` AFTER the event row is persisted
3160
- // (same code path as DELEGATION_INVALID_STATE), so an accept
3161
- // against a PENDING_LOCK delegation consumes sender_sequence
3162
- // even though it rejects.
3163
- "DELEGATION_PENDING_LOCK",
3164
- // M6 lock-at-accept: the `fund` body handler (`handleFund`) emits
3165
- // these AFTER the event row is committed (same lifecycle as
3166
- // DELEGATION_INVALID_STATE). `DELEGATION_FUNDER_NOT_OFFERER` (403)
3167
- // when a non-offerer attempts the fund; `DELEGATION_ALREADY_FUNDED`
3168
- // (409) on a re-fund of a delegation that already moved into the
3169
- // lock lifecycle (pending_lock_finalization / locked, or an
3170
- // in-flight create_lock op). Both consume sender_sequence, so the
3171
- // CLI must advance `lastSenderSequence` or a retry trips
3172
- // `ENV_SEQUENCE_BACKWARDS`.
3173
- "DELEGATION_FUNDER_NOT_OFFERER",
3174
- "DELEGATION_ALREADY_FUNDED",
3175
- // V1.5 lock-validator mint.owner pre-flight — kept here for
3176
- // documentation, but the `isPostCommitErrorCode` helper below
3177
- // also matches any `ESC_LOCK_*` prefix. The full lock-validator
3178
- // cross-check set (ID_MISMATCH, CONDITION_HASH_MISMATCH,
3179
- // AMOUNT_DELEGATION_MISMATCH, EXPIRY_TOO_*, PDA_*, etc.) ALSO
3180
- // fires after the delegation event is committed; missing
3181
- // entries here would leave `lastSenderSequence` stale and stall
3182
- // retries on ENV_SEQUENCE_BACKWARDS.
3183
- "ESC_LOCK_MINT_RPC_FAILED",
3184
- "ESC_LOCK_MINT_NOT_FOUND",
3185
- "ESC_LOCK_MINT_OWNER_MISMATCH"
3186
- ]);
3187
- function isPostCommitErrorCode(code) {
3188
- return POST_COMMIT_ERROR_CODES.has(code) || code.startsWith("ESC_LOCK_") || code.startsWith("SDK_");
3189
- }
3190
3147
  function registerOffer(parent) {
3191
- 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("--amount <s>", 'Optional decimal-as-string amount (e.g. "10.00"). REQUIRES --currency if set.').option("--currency <s>", `Asset identifier: shorthand (${import_sdk7.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(
3148
+ 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("--amount <s>", 'Optional decimal-as-string amount (e.g. "10.00"). REQUIRES --currency if set.').option("--currency <s>", `Asset identifier: shorthand (${import_sdk10.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 ${import_sdk10.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
3149
+ "--json",
3150
+ 'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"offer", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + the "reference this delegation" hints move to stderr; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
3151
+ false
3152
+ ).option(
3192
3153
  "--wait-until <phase>",
3193
3154
  '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.'
3194
3155
  ).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(
@@ -3294,6 +3255,11 @@ function assembleEscrowLockAttachment(opts) {
3294
3255
  };
3295
3256
  }
3296
3257
  async function runOffer(recipientDid, opts) {
3258
+ if (opts.verbose && opts.json) {
3259
+ throw new Error(
3260
+ "delegation offer: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
3261
+ );
3262
+ }
3297
3263
  requireDid("delegation offer", recipientDid, "<recipient-did>");
3298
3264
  const ttlSeconds = parseTtl("delegation offer", opts.ttl);
3299
3265
  if (!opts.title || opts.title.length === 0) {
@@ -3301,7 +3267,7 @@ async function runOffer(recipientDid, opts) {
3301
3267
  }
3302
3268
  const terms = parseOfferTerms("delegation offer", opts);
3303
3269
  const offeredAssetId = terms.currency?.asset_id;
3304
- if (offeredAssetId && !(0, import_sdk7.isWhitelistedAssetId)(offeredAssetId)) {
3270
+ if (offeredAssetId && !(0, import_sdk10.isWhitelistedAssetId)(offeredAssetId)) {
3305
3271
  console.error(
3306
3272
  import_chalk8.default.yellow(
3307
3273
  `warning: currency '${offeredAssetId}' is not in the static payment whitelist \u2014 the server will reject the offer (DELEGATION_ASSET_NOT_ALLOWED) unless its deploy registers this asset. See 'heyarp assets'.`
@@ -3322,21 +3288,21 @@ async function runOffer(recipientDid, opts) {
3322
3288
  const api = new ArpApiClient(opts.server);
3323
3289
  const sender = resolveSenderAgent("delegation offer", opts.server, opts.fromDid);
3324
3290
  const content = {
3325
- action: "offer",
3291
+ action: import_sdk10.DelegationActions.OFFER,
3326
3292
  delegation_id: delegationId,
3327
3293
  title: opts.title,
3328
3294
  ...terms
3329
3295
  };
3330
3296
  const body = { type: "delegation", content };
3331
- console.log(import_chalk8.default.dim(`Server: ${api.serverUrl}`));
3332
- console.log(import_chalk8.default.dim(`Sender: ${sender.did}`));
3333
- console.log(import_chalk8.default.dim(`Recipient: ${recipientDid}`));
3334
- console.log(import_chalk8.default.dim(`Delegation: ${delegationId}`));
3297
+ progress(opts.json, import_chalk8.default.dim(`Server: ${api.serverUrl}`));
3298
+ progress(opts.json, import_chalk8.default.dim(`Sender: ${sender.did}`));
3299
+ progress(opts.json, import_chalk8.default.dim(`Recipient: ${recipientDid}`));
3300
+ progress(opts.json, import_chalk8.default.dim(`Delegation: ${delegationId}`));
3335
3301
  let result;
3336
3302
  try {
3337
3303
  result = await sendDelegationEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
3338
3304
  } catch (err) {
3339
- if (err instanceof ApiError && err.payload.code === "DELEGATION_ASSET_NOT_ALLOWED") {
3305
+ if (err instanceof ApiError && err.payload.code === import_sdk10.DelegationOfferRejectionCodes.ASSET_NOT_ALLOWED) {
3340
3306
  const d = err.payload.details;
3341
3307
  console.error(import_chalk8.default.yellow(`
3342
3308
  The server's payment-asset whitelist rejected this currency.`));
@@ -3349,7 +3315,7 @@ See the whitelist (shorthand keys + canonical decimals):
3349
3315
  heyarp assets
3350
3316
  then re-offer with a whitelisted --currency.`));
3351
3317
  }
3352
- if (err instanceof ApiError && err.payload.code === "DELEGATION_PRICING_MISMATCH") {
3318
+ if (err instanceof ApiError && err.payload.code === import_sdk10.DelegationOfferRejectionCodes.PRICING_MISMATCH) {
3353
3319
  const d = err.payload.details;
3354
3320
  console.error(import_chalk8.default.yellow(`
3355
3321
  The recipient's published accept-prefs rejected this offer${d?.reason ? ` (mismatch: ${d.reason})` : ""}.`));
@@ -3366,7 +3332,7 @@ then re-run with matching --currency / --amount.`
3366
3332
  )
3367
3333
  );
3368
3334
  }
3369
- if (err instanceof ApiError && err.payload.code === "DELEGATION_CAPACITY_EXCEEDED") {
3335
+ if (err instanceof ApiError && err.payload.code === import_sdk10.DelegationOfferRejectionCodes.CAPACITY_EXCEEDED) {
3370
3336
  const d = err.payload.details;
3371
3337
  const cap = d?.maxActive === 0 ? "closed for new offers (busy)" : `at capacity (${d?.currentActive}/${d?.maxActive} active delegations)`;
3372
3338
  console.error(import_chalk8.default.yellow(`
@@ -3375,17 +3341,31 @@ The recipient is ${cap}.`));
3375
3341
  }
3376
3342
  throw err;
3377
3343
  }
3378
- printIngestResult(result);
3379
- console.log(import_chalk8.default.dim(`
3344
+ if (opts.json) {
3345
+ jsonOut({
3346
+ ok: true,
3347
+ action: "offer",
3348
+ delegationId,
3349
+ eventId: result.eventId,
3350
+ relationshipId: result.relationshipId,
3351
+ relationshipEventIndex: result.relationshipEventIndex,
3352
+ serverTimestamp: result.serverTimestamp,
3353
+ serverEventHash: result.serverEventHash,
3354
+ prevServerEventHash: result.prevServerEventHash ?? null
3355
+ });
3356
+ } else {
3357
+ printIngestResult(result);
3358
+ console.log(import_chalk8.default.dim(`
3380
3359
  Reference this delegation on subsequent calls with:`));
3381
- console.log(import_chalk8.default.dim(` heyarp delegation accept ${result.relationshipId} ${delegationId}`));
3382
- console.log(import_chalk8.default.dim(` heyarp delegation decline ${result.relationshipId} ${delegationId}`));
3383
- console.log(import_chalk8.default.dim(` heyarp delegation cancel ${result.relationshipId} ${delegationId}`));
3384
- console.log(import_chalk8.default.dim(`
3360
+ console.log(import_chalk8.default.dim(` heyarp delegation accept ${result.relationshipId} ${delegationId}`));
3361
+ console.log(import_chalk8.default.dim(` heyarp delegation decline ${result.relationshipId} ${delegationId}`));
3362
+ console.log(import_chalk8.default.dim(` heyarp delegation cancel ${result.relationshipId} ${delegationId}`));
3363
+ console.log(import_chalk8.default.dim(`
3385
3364
  After the worker accepts, fund the escrow lock:`));
3386
- console.log(import_chalk8.default.dim(` heyarp wallet create-lock --delegation-id ${delegationId} --condition-hash <hex> ... > lock.json`));
3387
- console.log(import_chalk8.default.dim(` heyarp delegation fund ${delegationId} --escrow-lock-from-file lock.json`));
3388
- if (opts.waitUntil) {
3365
+ console.log(import_chalk8.default.dim(` heyarp wallet create-lock --delegation-id ${delegationId} --condition-hash <hex> ... > lock.json`));
3366
+ console.log(import_chalk8.default.dim(` heyarp delegation fund ${delegationId} --escrow-lock-from-file lock.json`));
3367
+ }
3368
+ if (opts.waitUntil && !opts.json) {
3389
3369
  const untilPhase = parseUntilPhase(opts.waitUntil);
3390
3370
  if (untilPhase === void 0) {
3391
3371
  throw new Error(`delegation offer: --wait-until requires a phase value (got ${JSON.stringify(opts.waitUntil)})`);
@@ -3405,7 +3385,7 @@ After the worker accepts, fund the escrow lock:`));
3405
3385
  }
3406
3386
  }
3407
3387
  function registerFund(parent) {
3408
- 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(
3388
+ 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 ${import_sdk10.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
3409
3389
  "--json",
3410
3390
  '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.',
3411
3391
  false
@@ -3469,7 +3449,7 @@ async function runFund(delegationId, opts) {
3469
3449
  );
3470
3450
  }
3471
3451
  }
3472
- const content = { action: "fund", delegation_id: delegationId };
3452
+ const content = { action: import_sdk10.DelegationActions.FUND, delegation_id: delegationId };
3473
3453
  const body = { type: "delegation", content };
3474
3454
  const attachments = { escrow_lock: escrowResult.attachment };
3475
3455
  progress(opts.json, import_chalk8.default.dim(`Server: ${api.serverUrl}`));
@@ -3570,8 +3550,12 @@ function registerDecline(parent) {
3570
3550
  "--reason <code>",
3571
3551
  // surface the closed enum at help time so operators
3572
3552
  // don't have to read source to find acceptable values.
3573
- `Required: decline reason code (one of: ${import_sdk7.DECLINE_REASONS.join(", ")}). Carried in body.content.reason so the counterparty's reactor can branch on it.`
3574
- ).option("--reason-detail <s>", 'Optional free-text elaboration alongside --reason (e.g. "rate floor 0.20 USDC"). Max 512 chars.').option("--verbose", "Print the full envelope before sending and the full server response", false).option(
3553
+ `Required: decline reason code (one of: ${import_sdk10.DECLINE_REASONS.join(", ")}). Carried in body.content.reason so the counterparty's reactor can branch on it.`
3554
+ ).option("--reason-detail <s>", 'Optional free-text elaboration alongside --reason (e.g. "rate floor 0.20 USDC"). Max 512 chars.').option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
3555
+ "--json",
3556
+ 'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"decline", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + pending-lock poll chatter move off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
3557
+ false
3558
+ ).option(
3575
3559
  "--no-wait-for-lock",
3576
3560
  "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)."
3577
3561
  ).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) => {
@@ -3579,7 +3563,11 @@ function registerDecline(parent) {
3579
3563
  });
3580
3564
  }
3581
3565
  function registerCancel(parent) {
3582
- 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(
3566
+ 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. Mutually exclusive with --json.", false).option(
3567
+ "--json",
3568
+ 'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"cancel", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + pending-lock poll chatter move off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
3569
+ false
3570
+ ).option(
3583
3571
  "--no-wait-for-lock",
3584
3572
  "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)."
3585
3573
  ).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) => {
@@ -3599,7 +3587,7 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
3599
3587
  const lockWaitTimeoutSec = parseLockWaitTimeout(cmdName, opts.lockWaitTimeout);
3600
3588
  const lockWaitIntervalSec = parseLockWaitInterval(cmdName, opts.lockWaitInterval);
3601
3589
  let declinePayload = null;
3602
- if (action === "decline") {
3590
+ if (action === import_sdk10.DelegationActions.DECLINE) {
3603
3591
  const reason = parseDeclineReason(cmdName, opts.reason);
3604
3592
  const validatedDetail = parseReasonDetail(cmdName, opts.reasonDetail);
3605
3593
  declinePayload = validatedDetail ? { reason, reasonDetail: validatedDetail } : { reason };
@@ -3608,7 +3596,7 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
3608
3596
  const sender = resolveSenderAgent(cmdName, opts.server, opts.fromDid);
3609
3597
  const signer = makeSigner(sender);
3610
3598
  const resolved = await resolveDelegationRefs(cmdName, api, signer, { relationshipId, delegationId, action, selfDid: sender.did });
3611
- if (resolved.state === "pending_lock_finalization" && opts.waitForLock !== false) {
3599
+ if (resolved.state === import_sdk10.DelegationStates.PENDING_LOCK_FINALIZATION && opts.waitForLock !== false) {
3612
3600
  await awaitDelegationLockFinalized(cmdName, api, signer, {
3613
3601
  relationshipId,
3614
3602
  delegationId,
@@ -3633,7 +3621,7 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
3633
3621
  progress(opts.json, import_chalk8.default.dim(`Server: ${api.serverUrl}`));
3634
3622
  progress(opts.json, import_chalk8.default.dim(`Sender: ${sender.did}`));
3635
3623
  progress(opts.json, import_chalk8.default.dim(`Relationship: ${relationshipId}`));
3636
- progress(opts.json, import_chalk8.default.dim(`Delegation: ${delegationId} (action=${action}${action === "decline" ? `, reason=${content.reason}` : ""})`));
3624
+ progress(opts.json, import_chalk8.default.dim(`Delegation: ${delegationId} (action=${action}${action === import_sdk10.DelegationActions.DECLINE ? `, reason=${content.reason}` : ""})`));
3637
3625
  const result = await sendDelegationEnvelope({
3638
3626
  api,
3639
3627
  sender,
@@ -3662,9 +3650,9 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
3662
3650
  async function sendDelegationEnvelope(args) {
3663
3651
  const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
3664
3652
  const protectedBlock = {
3665
- protocol_version: "arp/0.1",
3666
- purpose: import_sdk7.Purpose.ENVELOPE,
3667
- message_id: (0, import_sdk7.uuidV4)(),
3653
+ protocol_version: import_sdk10.CURRENT_PROTOCOL_VERSION,
3654
+ purpose: import_sdk10.Purpose.ENVELOPE,
3655
+ message_id: (0, import_sdk10.uuidV4)(),
3668
3656
  sender_did: args.sender.did,
3669
3657
  recipient_did: args.recipientDid,
3670
3658
  // `relationship_id: null` matches the handshake
@@ -3673,13 +3661,13 @@ async function sendDelegationEnvelope(args) {
3673
3661
  // existing relationship row.
3674
3662
  relationship_id: null,
3675
3663
  sender_sequence: nextSequence,
3676
- sender_nonce: (0, import_sdk7.senderNonce)(),
3677
- timestamp: (0, import_sdk7.rfc3339)(),
3678
- expires_at: (0, import_sdk7.expiresAt)(args.ttlSeconds),
3664
+ sender_nonce: (0, import_sdk10.senderNonce)(),
3665
+ timestamp: (0, import_sdk10.rfc3339)(),
3666
+ expires_at: (0, import_sdk10.expiresAt)(args.ttlSeconds),
3679
3667
  delivery_id: null
3680
3668
  };
3681
3669
  const signer = makeSigner(args.sender);
3682
- const envelope = (0, import_sdk7.signEnvelope)({
3670
+ const envelope = (0, import_sdk10.signEnvelope)({
3683
3671
  protected: protectedBlock,
3684
3672
  body: args.body,
3685
3673
  identitySecretKey: signer.identitySecretKey,
@@ -3694,7 +3682,7 @@ async function sendDelegationEnvelope(args) {
3694
3682
  updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
3695
3683
  return result;
3696
3684
  } catch (err) {
3697
- if (err instanceof ApiError && isPostCommitErrorCode(err.payload.code)) {
3685
+ if (err instanceof ApiError && (0, import_sdk10.isPostCommitErrorCode)(err.payload.code)) {
3698
3686
  updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
3699
3687
  }
3700
3688
  throw err;
@@ -3716,7 +3704,7 @@ async function resolveDelegationRefs(cmdName, api, signer, args) {
3716
3704
  );
3717
3705
  }
3718
3706
  let recipientDid;
3719
- if (args.action === "cancel") {
3707
+ if (args.action === import_sdk10.DelegationActions.CANCEL) {
3720
3708
  const firstEvents = await api.listEvents(args.relationshipId, signer, { since: 0, limit: 1 });
3721
3709
  const handshake = firstEvents[0];
3722
3710
  if (!handshake) {
@@ -3756,7 +3744,7 @@ async function awaitDelegationLockFinalized(cmdName, api, signer, args) {
3756
3744
  after = page[page.length - 1].id;
3757
3745
  }
3758
3746
  };
3759
- const outcome = await (0, import_sdk7.pollUntil)({
3747
+ const outcome = await (0, import_sdk10.pollUntil)({
3760
3748
  fetch: fetchRow,
3761
3749
  // Match on either "row not found at all" OR "state moved
3762
3750
  // past pending_lock_finalization". The post-poll branch
@@ -3766,7 +3754,7 @@ async function awaitDelegationLockFinalized(cmdName, api, signer, args) {
3766
3754
  // rows return cleanly. Polling on null would loop pointlessly
3767
3755
  // until the deadline fires and surface a misleading "timed
3768
3756
  // out" message for what's actually a wrong-id problem.
3769
- predicate: (row2) => row2 === null || row2.state !== "pending_lock_finalization",
3757
+ predicate: (row2) => row2 === null || row2.state !== import_sdk10.DelegationStates.PENDING_LOCK_FINALIZATION,
3770
3758
  intervalMs: args.intervalSec * 1e3,
3771
3759
  timeoutMs: args.timeoutSec * 1e3,
3772
3760
  // Swallow ONLY transient errors. A 4xx during the poll is a
@@ -3797,7 +3785,7 @@ async function awaitDelegationLockFinalized(cmdName, api, signer, args) {
3797
3785
  `${cmdName}: delegation ${args.delegationId} disappeared from relationship ${args.relationshipId} during --wait-for-lock pre-flight. Re-check the delegation id with \`heyarp delegations ${args.relationshipId}\`.`
3798
3786
  );
3799
3787
  }
3800
- if (row.state !== "offered" && row.state !== "proposed") {
3788
+ if (row.state !== import_sdk10.DelegationStates.OFFERED) {
3801
3789
  throw new Error(
3802
3790
  `${cmdName}: delegation ${args.delegationId} transitioned to '${row.state}' while waiting for on-chain lock confirmation; cannot ${args.action}. Re-read the delegation timeline with \`heyarp delegations ${args.relationshipId}\` to see the latest state.`
3803
3791
  );
@@ -3857,7 +3845,7 @@ function parseOfferTerms(cmdName, opts) {
3857
3845
  if (opts.amount) {
3858
3846
  out.amount = opts.amount;
3859
3847
  if (!opts.currency) {
3860
- throw new Error(`${cmdName}: --amount requires --currency. Shorthand: ${import_sdk7.WELL_KNOWN_ASSET_KEYS.join(", ")}, or raw CAIP-19 + --currency-decimals.`);
3848
+ throw new Error(`${cmdName}: --amount requires --currency. Shorthand: ${import_sdk10.WELL_KNOWN_ASSET_KEYS.join(", ")}, or raw CAIP-19 + --currency-decimals.`);
3861
3849
  }
3862
3850
  out.currency = buildAssetIdentifier(cmdName, DELEGATION_CURRENCY_FLAGS, opts.currency, opts.currencyDecimals, opts.currencySymbol);
3863
3851
  } else if (opts.currency) {
@@ -3880,7 +3868,7 @@ function resolveOfferDelegationId(rawCliId, escrow) {
3880
3868
  cliId = rawCliId.toLowerCase();
3881
3869
  }
3882
3870
  if (escrow === void 0) {
3883
- return cliId ?? (0, import_sdk7.uuidV4)();
3871
+ return cliId ?? (0, import_sdk10.uuidV4)();
3884
3872
  }
3885
3873
  if (escrow.delegationIdFromLock !== void 0) {
3886
3874
  const fileId = escrow.delegationIdFromLock;
@@ -3915,10 +3903,10 @@ function collectRepeated(value, previous) {
3915
3903
  }
3916
3904
  function parseDeclineReason(cmdName, raw) {
3917
3905
  if (raw === void 0 || raw === "") {
3918
- throw new Error(`${cmdName}: --reason is required when declining (one of: ${import_sdk7.DECLINE_REASONS.join(", ")})`);
3906
+ throw new Error(`${cmdName}: --reason is required when declining (one of: ${import_sdk10.DECLINE_REASONS.join(", ")})`);
3919
3907
  }
3920
- if (!(0, import_sdk7.isDeclineReason)(raw)) {
3921
- throw new Error(`${cmdName}: --reason must be one of ${import_sdk7.DECLINE_REASONS.join(", ")} (got '${raw}')`);
3908
+ if (!(0, import_sdk10.isDeclineReason)(raw)) {
3909
+ throw new Error(`${cmdName}: --reason must be one of ${import_sdk10.DECLINE_REASONS.join(", ")} (got '${raw}')`);
3922
3910
  }
3923
3911
  return raw;
3924
3912
  }
@@ -3933,10 +3921,10 @@ function parseReasonDetail(cmdName, raw) {
3933
3921
  return raw;
3934
3922
  }
3935
3923
  function buildAssetIdentifier(cmdName, labels, rawCurrency, rawDecimals, rawSymbol) {
3936
- const resolved = (0, import_sdk7.resolveAsset)(rawCurrency);
3924
+ const resolved = (0, import_sdk10.resolveAsset)(rawCurrency);
3937
3925
  if (!resolved) {
3938
3926
  throw new Error(
3939
- `${cmdName}: ${labels.currencyFlag} '${rawCurrency}' is not a known shorthand or a valid CAIP-19 string. Shorthand: ${import_sdk7.WELL_KNOWN_ASSET_KEYS.join(", ")}. Or pass a raw CAIP-19 id (e.g. "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") with ${labels.decimalsFlag}.`
3927
+ `${cmdName}: ${labels.currencyFlag} '${rawCurrency}' is not a known shorthand or a valid CAIP-19 string. Shorthand: ${import_sdk10.WELL_KNOWN_ASSET_KEYS.join(", ")}. Or pass a raw CAIP-19 id (e.g. "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") with ${labels.decimalsFlag}.`
3940
3928
  );
3941
3929
  }
3942
3930
  let decimals = resolved.decimals;
@@ -3945,20 +3933,20 @@ function buildAssetIdentifier(cmdName, labels, rawCurrency, rawDecimals, rawSymb
3945
3933
  throw new Error(`${cmdName}: ${labels.currencyFlag} is a raw CAIP-19 string; ${labels.decimalsFlag} (0-18) is required to convert amounts to base units.`);
3946
3934
  }
3947
3935
  const parsed = Number(rawDecimals);
3948
- if (!Number.isInteger(parsed) || parsed < 0 || parsed > 18) {
3936
+ if (!Number.isInteger(parsed) || parsed < import_sdk10.ASSET_DECIMALS_MIN || parsed > import_sdk10.ASSET_DECIMALS_MAX) {
3949
3937
  throw new Error(`${cmdName}: ${labels.decimalsFlag} must be an integer in [0, 18] (got '${rawDecimals}').`);
3950
3938
  }
3951
3939
  decimals = parsed;
3952
3940
  } else if (rawDecimals !== void 0) {
3953
3941
  const parsed = Number(rawDecimals);
3954
- if (!Number.isInteger(parsed) || parsed < 0 || parsed > 18) {
3942
+ if (!Number.isInteger(parsed) || parsed < import_sdk10.ASSET_DECIMALS_MIN || parsed > import_sdk10.ASSET_DECIMALS_MAX) {
3955
3943
  throw new Error(`${cmdName}: ${labels.decimalsFlag} must be an integer in [0, 18] (got '${rawDecimals}').`);
3956
3944
  }
3957
3945
  decimals = parsed;
3958
3946
  }
3959
3947
  let symbol = resolved.symbol;
3960
3948
  if (rawSymbol !== void 0) {
3961
- if (rawSymbol.length === 0 || rawSymbol.length > 16) {
3949
+ if (rawSymbol.length < import_sdk10.ASSET_SYMBOL_MIN_LEN || rawSymbol.length > import_sdk10.ASSET_SYMBOL_MAX_LEN) {
3962
3950
  throw new Error(`${cmdName}: ${labels.symbolFlag} must be 1-16 chars (got length ${rawSymbol.length}).`);
3963
3951
  }
3964
3952
  symbol = rawSymbol;
@@ -3972,9 +3960,10 @@ var DELEGATION_CURRENCY_FLAGS = {
3972
3960
  };
3973
3961
 
3974
3962
  // src/commands/delegations.ts
3963
+ var import_sdk11 = require("@heyanon-arp/sdk");
3975
3964
  var import_chalk9 = __toESM(require("chalk"));
3976
3965
  init_api();
3977
- var ALLOWED_STATES = /* @__PURE__ */ new Set(["offered", "accepted", "declined", "canceled"]);
3966
+ var ALLOWED_STATES = /* @__PURE__ */ new Set([import_sdk11.DelegationStates.OFFERED, import_sdk11.DelegationStates.ACCEPTED, import_sdk11.DelegationStates.DECLINED, import_sdk11.DelegationStates.CANCELED]);
3978
3967
  function registerDelegationsCommand(root) {
3979
3968
  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(
3980
3969
  "--verbose",
@@ -4033,7 +4022,7 @@ function formatDelegationLine(d, selfDid, opts = {}) {
4033
4022
  const amount = formatAmount(d);
4034
4023
  const title = d.title ? import_chalk9.default.dim(`"${truncate2(d.title, 40)}"`) : import_chalk9.default.dim("(no title)");
4035
4024
  let declineSuffix = "";
4036
- if (d.state === "declined" && d.declineReason) {
4025
+ if (d.state === import_sdk11.DelegationStates.DECLINED && d.declineReason) {
4037
4026
  const detail = d.declineReasonDetail ? `: ${truncate2(d.declineReasonDetail, 40)}` : "";
4038
4027
  declineSuffix = ` ${import_chalk9.default.dim(`(reason: ${d.declineReason}${detail})`)}`;
4039
4028
  }
@@ -4041,8 +4030,6 @@ function formatDelegationLine(d, selfDid, opts = {}) {
4041
4030
  }
4042
4031
  function colorState(s) {
4043
4032
  switch (s) {
4044
- case "proposed":
4045
- return import_chalk9.default.yellow("proposed");
4046
4033
  // `offered` is the server's actual emitted state immediately
4047
4034
  // after `delegation offer`; `pending_lock_finalization` is the
4048
4035
  // transient state while the relayer submits create_lock. Both
@@ -4050,34 +4037,34 @@ function colorState(s) {
4050
4037
  // returns `undefined` and `formatDelegationLine` crashes with
4051
4038
  // `TypeError: Cannot read properties of undefined (reading
4052
4039
  // 'padEnd')` on any listing containing such a row.
4053
- case "offered":
4040
+ case import_sdk11.DelegationStates.OFFERED:
4054
4041
  return import_chalk9.default.yellow("offered");
4055
- case "pending_lock_finalization":
4042
+ case import_sdk11.DelegationStates.PENDING_LOCK_FINALIZATION:
4056
4043
  return import_chalk9.default.yellow("pending_lock");
4057
- case "accepted":
4044
+ case import_sdk11.DelegationStates.ACCEPTED:
4058
4045
  return import_chalk9.default.green("accepted");
4059
- // M6 lock-at-accept: the on-chain escrow lock is confirmed.
4060
- // Distinct branch so a LOCKED row renders without hitting the
4061
- // defensive fallback (which would otherwise echo the raw state).
4062
- case "locked":
4046
+ // The on-chain escrow lock is confirmed. Distinct branch so a
4047
+ // LOCKED row renders without hitting the defensive fallback
4048
+ // (which would otherwise echo the raw state).
4049
+ case import_sdk11.DelegationStates.LOCKED:
4063
4050
  return import_chalk9.default.green("locked");
4064
- case "declined":
4051
+ case import_sdk11.DelegationStates.DECLINED:
4065
4052
  return import_chalk9.default.red("declined");
4066
- case "canceled":
4053
+ case import_sdk11.DelegationStates.CANCELED:
4067
4054
  return import_chalk9.default.dim("canceled");
4068
- // Terminal escrow outcomes (V2): completed = payee paid;
4055
+ // Terminal escrow outcomes: completed = payee paid;
4069
4056
  // failed = create_lock never landed; refunded = funds returned
4070
4057
  // to the buyer (work expired / dispute closed);
4071
4058
  // dispute_resolved = operator ruled.
4072
- case "completed":
4059
+ case import_sdk11.DelegationStates.COMPLETED:
4073
4060
  return import_chalk9.default.green("completed");
4074
- case "failed":
4061
+ case import_sdk11.DelegationStates.FAILED:
4075
4062
  return import_chalk9.default.red("failed");
4076
- case "refunded":
4063
+ case import_sdk11.DelegationStates.REFUNDED:
4077
4064
  return import_chalk9.default.red("refunded");
4078
- case "disputing":
4065
+ case import_sdk11.DelegationStates.DISPUTING:
4079
4066
  return import_chalk9.default.yellow("disputing");
4080
- case "dispute_resolved":
4067
+ case import_sdk11.DelegationStates.DISPUTE_RESOLVED:
4081
4068
  return import_chalk9.default.red("dispute_resolved");
4082
4069
  default: {
4083
4070
  const _exhaustive = s;
@@ -4123,7 +4110,7 @@ function parseLimit2(raw) {
4123
4110
  }
4124
4111
 
4125
4112
  // src/commands/did-doc.ts
4126
- var import_sdk8 = require("@heyanon-arp/sdk");
4113
+ var import_sdk12 = require("@heyanon-arp/sdk");
4127
4114
  init_api();
4128
4115
  function registerDidDocCommand(root) {
4129
4116
  root.command("did-doc").description(
@@ -4132,7 +4119,7 @@ function registerDidDocCommand(root) {
4132
4119
  "--field <path>",
4133
4120
  "Extract a single value via a dotted path. Supports object property access (`id`), numeric array indexes (`verificationMethod.0`), and `#fragment` array selectors that match a verification-method or service entry by its `id` suffix (`verificationMethod.#settlement.publicKeyMultibase`). Two well-known shortcuts skip the path entirely AND strip the multibase prefix: `settlementPublicKey` / `identityPublicKey` emit the raw base58 pubkey ready for flags like `--recipient-pubkey`. Scalar values (string / number / boolean) emit raw to stdout for shell composition (`KEY=$(heyarp did-doc ... --field ...)`); objects / arrays emit as pretty-printed JSON. Mutually exclusive with `--json` since `--field` already controls the shape."
4134
4121
  ).action(async (did, opts) => {
4135
- if (!(0, import_sdk8.isValidDid)(did)) {
4122
+ if (!(0, import_sdk12.isValidDid)(did)) {
4136
4123
  throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
4137
4124
  }
4138
4125
  if (opts.json && opts.field !== void 0) {
@@ -4244,7 +4231,7 @@ function describeShape(value) {
4244
4231
  }
4245
4232
 
4246
4233
  // src/commands/doctor.ts
4247
- var import_sdk9 = require("@heyanon-arp/sdk");
4234
+ var import_sdk13 = require("@heyanon-arp/sdk");
4248
4235
  var import_chalk10 = __toESM(require("chalk"));
4249
4236
  init_api();
4250
4237
  function registerDoctorCommand(root) {
@@ -4254,7 +4241,7 @@ function registerDoctorCommand(root) {
4254
4241
  }
4255
4242
  var LISTENING_THRESHOLD_SECONDS = 15 * 60;
4256
4243
  async function runDoctor(did, opts) {
4257
- if (!(0, import_sdk9.isValidDid)(did)) {
4244
+ if (!(0, import_sdk13.isValidDid)(did)) {
4258
4245
  throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
4259
4246
  }
4260
4247
  const api = new ArpApiClient(opts.server);
@@ -4407,14 +4394,14 @@ function formatHints(event) {
4407
4394
  }
4408
4395
 
4409
4396
  // src/commands/escrow.ts
4410
- var import_sdk11 = require("@heyanon-arp/sdk");
4397
+ var import_sdk15 = require("@heyanon-arp/sdk");
4411
4398
  var import_utils2 = require("@noble/hashes/utils");
4412
4399
  var import_chalk12 = __toESM(require("chalk"));
4413
4400
  init_api();
4414
4401
 
4415
4402
  // src/commands/escrow-actions.ts
4416
4403
  var import_node_crypto = require("crypto");
4417
- var import_sdk10 = require("@heyanon-arp/sdk");
4404
+ var import_sdk14 = require("@heyanon-arp/sdk");
4418
4405
  var import_web33 = require("@solana/web3.js");
4419
4406
  init_api();
4420
4407
  var FEE_BUFFER_LAMPORTS = 10000000n;
@@ -4428,7 +4415,7 @@ async function setup(cmd, delegationIdArg, opts) {
4428
4415
  const delegationId = normaliseDelegationId(delegationIdArg);
4429
4416
  const fetched = await fetchLockAccount(conn, programId, delegationId);
4430
4417
  if (!fetched) {
4431
- const lockPda = deriveLockPda(programId, (0, import_sdk10.deriveLockId)(delegationId)).toBase58();
4418
+ const lockPda = deriveLockPda(programId, (0, import_sdk14.deriveLockId)(delegationId)).toBase58();
4432
4419
  throw new Error(
4433
4420
  `${cmd}: no on-chain Lock for delegation ${delegationId} (PDA ${lockPda} not found on ${rpcUrl}). Either the buyer hasn't funded yet (delegation fund \u2192 server relays create_lock), or you're pointing at the wrong cluster/program.`
4434
4421
  );
@@ -4453,7 +4440,7 @@ function nowSecs() {
4453
4440
  function deadlinePassed(ctx) {
4454
4441
  return ctx.lock.expiry > 0n && nowSecs() >= ctx.lock.expiry;
4455
4442
  }
4456
- var SYSTEM_PROGRAM_B58 = "11111111111111111111111111111111";
4443
+ var SYSTEM_PROGRAM_B58 = import_sdk14.SYSTEM_PROGRAM_ID_BASE58;
4457
4444
  function effectiveFeeRecipient(lock) {
4458
4445
  if (lock.feeRecipientAtLock !== SYSTEM_PROGRAM_B58) return new import_web33.PublicKey(lock.feeRecipientAtLock);
4459
4446
  if (lock.treasuryAtLock && lock.treasuryAtLock !== SYSTEM_PROGRAM_B58) return new import_web33.PublicKey(lock.treasuryAtLock);
@@ -4503,7 +4490,7 @@ function emit(ctx, action, signature, extra = {}) {
4503
4490
  lock_id: ctx.lock.lockId,
4504
4491
  state_before: ctx.lock.state,
4505
4492
  signature,
4506
- rpc_url: ctx.rpcUrl,
4493
+ rpc_url: redactRpcUrl(ctx.rpcUrl),
4507
4494
  ...extra
4508
4495
  };
4509
4496
  if (ctx.opts.json) {
@@ -4515,7 +4502,7 @@ function emit(ctx, action, signature, extra = {}) {
4515
4502
  }
4516
4503
  async function acceptHandler(delegationId, opts) {
4517
4504
  const ctx = await setup("escrow accept", delegationId, opts);
4518
- requireState(ctx, ["created"], "Accept is only possible before any other transition; if it shows in_progress you already accepted.");
4505
+ requireState(ctx, [import_sdk14.LockStates.CREATED], "Accept is only possible before any other transition; if it shows in_progress you already accepted.");
4519
4506
  requireSigner(ctx, "payee", ctx.lock.payee);
4520
4507
  const stake = ctx.lock.workerStakeAtLock;
4521
4508
  await preflightLamports(ctx, stake, `the worker stake (${stake} lamports, returned when the lock settles in your favour)`);
@@ -4525,7 +4512,7 @@ async function acceptHandler(delegationId, opts) {
4525
4512
  }
4526
4513
  async function submitWorkHandler(delegationId, opts) {
4527
4514
  const ctx = await setup("escrow submit-work", delegationId, opts);
4528
- requireState(ctx, ["in_progress"], "Submit needs an accepted lock ('created' \u2192 run `heyarp escrow accept` first; terminal \u2192 the cycle already ended).");
4515
+ requireState(ctx, [import_sdk14.LockStates.IN_PROGRESS], "Submit needs an accepted lock ('created' \u2192 run `heyarp escrow accept` first; terminal \u2192 the cycle already ended).");
4529
4516
  requireSigner(ctx, "payee", ctx.lock.payee);
4530
4517
  if (deadlinePassed(ctx)) {
4531
4518
  throw new Error(
@@ -4536,7 +4523,7 @@ async function submitWorkHandler(delegationId, opts) {
4536
4523
  let reviewDeadline = null;
4537
4524
  try {
4538
4525
  const fresh = await fetchLockAccount(ctx.conn, ctx.programId, ctx.delegationId);
4539
- if (fresh && fresh.lock.state === "submitted" && fresh.lock.expiry > 0n) {
4526
+ if (fresh && fresh.lock.state === import_sdk14.LockStates.SUBMITTED && fresh.lock.expiry > 0n) {
4540
4527
  reviewDeadline = new Date(Number(fresh.lock.expiry) * 1e3).toISOString();
4541
4528
  }
4542
4529
  } catch {
@@ -4549,22 +4536,25 @@ async function submitWorkHandler(delegationId, opts) {
4549
4536
  }
4550
4537
  async function claimHandler(delegationId, opts) {
4551
4538
  const ctx = await setup("escrow claim", delegationId, opts);
4552
- requireState(ctx, ["submitted"], "Claim needs submitted work ('in_progress' \u2192 the worker must `escrow submit-work` first; 'paid' \u2192 already claimed).");
4539
+ requireState(ctx, [import_sdk14.LockStates.SUBMITTED], "Claim needs submitted work ('in_progress' \u2192 the worker must `escrow submit-work` first; 'paid' \u2192 already claimed).");
4553
4540
  const me = ctx.keypair.publicKey.toBase58();
4554
4541
  let role;
4555
4542
  if (me === ctx.lock.payer) {
4556
- role = "buyer_approved";
4543
+ role = import_sdk14.EscrowReleaseMethods.BUYER_APPROVED;
4557
4544
  } else if (me === ctx.lock.payee) {
4558
4545
  if (!deadlinePassed(ctx)) {
4559
4546
  throw new Error(
4560
4547
  `escrow claim: the review window is still open (until ${expiryIso(ctx)}). As the worker you can self-claim only AFTER it lapses; before that the buyer must approve (or dispute).`
4561
4548
  );
4562
4549
  }
4563
- role = "review_timeout";
4550
+ role = import_sdk14.EscrowReleaseMethods.REVIEW_TIMEOUT;
4564
4551
  } else {
4565
4552
  throw new Error(`escrow claim: your settlement key ${me} is neither the payer (${ctx.lock.payer}) nor the payee (${ctx.lock.payee}) of this lock.`);
4566
4553
  }
4567
- progress(ctx.opts.json, role === "buyer_approved" ? "approving payment as the buyer \u2014 this RELEASES the escrow and is irreversible" : "self-claiming after review timeout");
4554
+ progress(
4555
+ ctx.opts.json,
4556
+ role === import_sdk14.EscrowReleaseMethods.BUYER_APPROVED ? "approving payment as the buyer \u2014 this RELEASES the escrow and is irreversible" : "self-claiming after review timeout"
4557
+ );
4568
4558
  const sig = await sendIx(
4569
4559
  ctx,
4570
4560
  buildClaimWorkPaymentIx({
@@ -4581,7 +4571,7 @@ async function claimHandler(delegationId, opts) {
4581
4571
  }
4582
4572
  async function cancelHandler(delegationId, opts) {
4583
4573
  const ctx = await setup("escrow cancel", delegationId, opts);
4584
- requireState(ctx, ["created"], "Cancel is the pre-accept exit only \u2014 once the worker accepted ('in_progress') the windows model governs.");
4574
+ requireState(ctx, [import_sdk14.LockStates.CREATED], "Cancel is the pre-accept exit only \u2014 once the worker accepted ('in_progress') the windows model governs.");
4585
4575
  requireSigner(ctx, "payer", ctx.lock.payer);
4586
4576
  const sig = await sendIx(
4587
4577
  ctx,
@@ -4596,7 +4586,7 @@ async function cancelHandler(delegationId, opts) {
4596
4586
  }
4597
4587
  async function claimExpiredHandler(delegationId, opts) {
4598
4588
  const ctx = await setup("escrow claim-expired", delegationId, opts);
4599
- requireState(ctx, ["in_progress"], "Claim-expired recovers an accepted-but-never-submitted lock; 'submitted' work goes through claim/dispute instead.");
4589
+ requireState(ctx, [import_sdk14.LockStates.IN_PROGRESS], "Claim-expired recovers an accepted-but-never-submitted lock; 'submitted' work goes through claim/dispute instead.");
4600
4590
  requireSigner(ctx, "payer", ctx.lock.payer);
4601
4591
  if (!deadlinePassed(ctx)) {
4602
4592
  throw new Error(`escrow claim-expired: the work window is still open (until ${expiryIso(ctx)}). The worker can still submit; the chain will reject this claim until then.`);
@@ -4614,7 +4604,7 @@ async function claimExpiredHandler(delegationId, opts) {
4614
4604
  }
4615
4605
  async function disputeOpenHandler(delegationId, opts) {
4616
4606
  const ctx = await setup("escrow dispute open", delegationId, opts);
4617
- requireState(ctx, ["submitted"], "Disputes open against SUBMITTED work, inside the review window.");
4607
+ requireState(ctx, [import_sdk14.LockStates.SUBMITTED], "Disputes open against SUBMITTED work, inside the review window.");
4618
4608
  requireSigner(ctx, "payer", ctx.lock.payer);
4619
4609
  if (deadlinePassed(ctx)) {
4620
4610
  throw new Error(
@@ -4629,7 +4619,7 @@ async function disputeOpenHandler(delegationId, opts) {
4629
4619
  }
4630
4620
  async function disputeCloseHandler(delegationId, opts) {
4631
4621
  const ctx = await setup("escrow dispute close", delegationId, opts);
4632
- requireState(ctx, ["disputing"], "Close only applies to an OPEN dispute that the operator never resolved.");
4622
+ requireState(ctx, [import_sdk14.LockStates.DISPUTING], "Close only applies to an OPEN dispute that the operator never resolved.");
4633
4623
  const me = ctx.keypair.publicKey.toBase58();
4634
4624
  if (me !== ctx.lock.payer && me !== ctx.lock.payee) {
4635
4625
  throw new Error(`escrow dispute close: your settlement key ${me} is neither party of this lock.`);
@@ -4654,7 +4644,7 @@ async function disputeCloseHandler(delegationId, opts) {
4654
4644
  }
4655
4645
  async function disputeResolveHandler(delegationId, opts) {
4656
4646
  const ctx = await setup("escrow dispute resolve", delegationId, opts);
4657
- requireState(ctx, ["disputing"], "Resolve only applies to an OPEN dispute, inside the dispute window.");
4647
+ requireState(ctx, [import_sdk14.LockStates.DISPUTING], "Resolve only applies to an OPEN dispute, inside the dispute window.");
4658
4648
  if (deadlinePassed(ctx)) {
4659
4649
  throw new Error(
4660
4650
  `escrow dispute resolve: the dispute window lapsed at ${expiryIso(ctx)} \u2014 the chain rejects late rulings (LockExpired); either party can now \`escrow dispute close\` instead.`
@@ -4695,7 +4685,12 @@ async function showHandler(delegationId, opts) {
4695
4685
  const normalised = normaliseDelegationId(delegationId);
4696
4686
  const fetched = await fetchLockAccount(conn, programId, normalised);
4697
4687
  if (!fetched) {
4698
- jsonOut({ delegation_id: normalised.slice("del_".length), lock_exists: false, lock_pda: deriveLockPda(programId, (0, import_sdk10.deriveLockId)(normalised)).toBase58(), rpc_url: rpcUrl });
4688
+ jsonOut({
4689
+ delegation_id: normalised.slice("del_".length),
4690
+ lock_exists: false,
4691
+ lock_pda: deriveLockPda(programId, (0, import_sdk14.deriveLockId)(normalised)).toBase58(),
4692
+ rpc_url: redactRpcUrl(rpcUrl)
4693
+ });
4699
4694
  return;
4700
4695
  }
4701
4696
  const l = fetched.lock;
@@ -4714,7 +4709,7 @@ async function showHandler(delegationId, opts) {
4714
4709
  fee_bps_at_lock: l.feeBpsAtLock,
4715
4710
  worker_stake_at_lock: l.workerStakeAtLock.toString(),
4716
4711
  operator_dispute_fee_at_lock: l.operatorDisputeFeeAtLock.toString(),
4717
- rpc_url: rpcUrl
4712
+ rpc_url: redactRpcUrl(rpcUrl)
4718
4713
  });
4719
4714
  }
4720
4715
  function sharedFlags(cmd) {
@@ -4770,7 +4765,7 @@ function registerEscrowCommands(root) {
4770
4765
  "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)."
4771
4766
  ).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").option(
4772
4767
  "--currency <s>",
4773
- `Asset identifier bound into the hash: shorthand (${import_sdk11.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string. Optional but must match the offer's --currency.`
4768
+ `Asset identifier bound into the hash: shorthand (${import_sdk15.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string. Optional but must match the offer's --currency.`
4774
4769
  ).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) => {
4775
4770
  await runDeriveConditionHash(opts);
4776
4771
  });
@@ -4849,7 +4844,7 @@ async function runDeriveConditionHash(opts) {
4849
4844
  progress(opts.json, import_chalk12.default.dim(`Server: ${api.serverUrl}`));
4850
4845
  progress(opts.json, import_chalk12.default.dim(`Signer: ${sender.did}`));
4851
4846
  const subset = projectDelegationForHash(cmdName, opts, delegationId);
4852
- const hashBytes = (0, import_sdk11.deriveDelegationConditionHash)(subset);
4847
+ const hashBytes = (0, import_sdk15.deriveDelegationConditionHash)(subset);
4853
4848
  const hex = (0, import_utils2.bytesToHex)(hashBytes);
4854
4849
  if (opts.json) {
4855
4850
  jsonOut({
@@ -4931,6 +4926,7 @@ async function runRecoverSequence(opts) {
4931
4926
  }
4932
4927
 
4933
4928
  // src/commands/events.ts
4929
+ var import_sdk16 = require("@heyanon-arp/sdk");
4934
4930
  var import_chalk13 = __toESM(require("chalk"));
4935
4931
  init_api();
4936
4932
  function registerEventsCommand(root) {
@@ -4949,7 +4945,7 @@ function registerEventsCommand(root) {
4949
4945
  false
4950
4946
  ).option(
4951
4947
  "--full-ids",
4952
- "Print eventId, DIDs and serverEventHash in full (no truncation). The eventId column in the default human row is truncated to `evt_<head>...<tail>` \u2014 pass --full-ids to read the full UUID for `heyarp envelope <evt-id>`. NOTE: `serverEventHash` is the hash-chain anchor, NOT the value `receipt propose / cosign` require \u2014 those want `requestHash` / `responseHash`, which are exposed by `heyarp receipts <rel-id> --full-ids`, or auto-derived via `receipt propose --auto-hashes --rel-id --request-id` / `receipt cosign --auto-hashes [--request-id]`.",
4948
+ "Print eventId, DIDs and serverEventHash in full (no truncation). The eventId column in the default human row is truncated to `evt_<head>...<tail>` \u2014 pass --full-ids to read the full UUID for `heyarp envelope <evt-id>`. NOTE: `serverEventHash` is the hash-chain anchor, NOT the value `receipt propose` requires \u2014 that wants `requestHash` / `responseHash`, which are exposed by `heyarp receipts <rel-id> --full-ids`, or auto-derived via `receipt propose --auto-hashes --rel-id --request-id`.",
4953
4949
  false
4954
4950
  ).option(
4955
4951
  "--success-only",
@@ -4981,8 +4977,8 @@ async function runEvents(relationshipId, opts) {
4981
4977
  }
4982
4978
  const query = { limit };
4983
4979
  if (since !== void 0) query.since = since;
4984
- if (opts.successOnly) query.readModelStatus = "materialized";
4985
- else if (opts.rejectedOnly) query.readModelStatus = "rejected";
4980
+ if (opts.successOnly) query.readModelStatus = import_sdk16.ReadModelStatuses.MATERIALIZED;
4981
+ else if (opts.rejectedOnly) query.readModelStatus = import_sdk16.ReadModelStatuses.REJECTED;
4986
4982
  const signer = makeSigner(sender);
4987
4983
  const events = await api.listEvents(relationshipId, signer, query);
4988
4984
  if (opts.json) {
@@ -5001,8 +4997,7 @@ async function runEvents(relationshipId, opts) {
5001
4997
  printVerbose(events, "Full event envelopes:", (ev) => ({
5002
4998
  primary: `#${ev.relationshipEventIndex} ${ev.type ?? "<unknown>"}`,
5003
4999
  // Surface BOTH the full eventId (used by `heyarp envelope
5004
- // <evt-id>`) and the full serverEventHash (used by
5005
- // `receipt propose` / `receipt cosign`).
5000
+ // <evt-id>`) and the full serverEventHash.
5006
5001
  secondary: `eventId=${ev.eventId} serverEventHash=${ev.serverEventHash}`
5007
5002
  }));
5008
5003
  }
@@ -5018,8 +5013,8 @@ function formatEventLine(ev, selfDid, opts = {}) {
5018
5013
  const hash = opts.fullIds ? ev.serverEventHash : hashHead(ev.serverEventHash);
5019
5014
  const extra = extraDetail(ev);
5020
5015
  const tail = extra ? ` ${import_chalk13.default.dim(`(${extra})`)}` : "";
5021
- const status = ev.readModelStatus ?? "materialized";
5022
- const statusGlyph = status === "rejected" ? `${import_chalk13.default.red("\u2717")} ` : " ";
5016
+ const status = ev.readModelStatus ?? import_sdk16.ReadModelStatuses.MATERIALIZED;
5017
+ const statusGlyph = status === import_sdk16.ReadModelStatuses.REJECTED ? `${import_chalk13.default.red("\u2717")} ` : " ";
5023
5018
  return `${statusGlyph}${idx} ${import_chalk13.default.cyan(eventId)} ${type} ${direction} ${import_chalk13.default.cyan(hash)}${tail}`;
5024
5019
  }
5025
5020
  function eventIdHead(eventId) {
@@ -5310,7 +5305,7 @@ var GUIDE_SECTIONS = [
5310
5305
  title: "Identity",
5311
5306
  body: [
5312
5307
  " \u2022 Each agent has a `did:arp:<base58btc>` DID + an Ed25519 identity key.",
5313
- " \u2022 Keys live in `~/.arp/agents.json` (or $HEYARP_HOME/agents.json).",
5308
+ " \u2022 Keys live in `~/.heyarp/agents.json` (or $HEYARP_HOME/agents.json).",
5314
5309
  " \u2022 State file = identity. If two agents share one home dir, EITHER can sign as",
5315
5310
  " the other. For multi-agent setups on one host, set HEYARP_HOME per agent:",
5316
5311
  " HEYARP_HOME=/tmp/agent-alice heyarp register \u2026",
@@ -5337,8 +5332,8 @@ var GUIDE_SECTIONS = [
5337
5332
  "",
5338
5333
  " (There is no separate terms-agreement step \u2014 the delegation offer",
5339
5334
  " carries the agreed terms inline and the condition_hash binds them.",
5340
- " M6 lock-at-accept: the escrow lock is funded AFTER acceptance via",
5341
- " `delegation fund`, so an un-accepted offer never strands funds.)",
5335
+ " The escrow lock is funded AFTER acceptance via `delegation fund`,",
5336
+ " so an un-accepted offer never strands funds.)",
5342
5337
  " Skipping any stage = the next stage rejects with a state-machine error.",
5343
5338
  " On-chain claim_work_payment is the closure: the buyer approves, or the",
5344
5339
  " worker self-claims after the review window expires."
@@ -5350,13 +5345,13 @@ var GUIDE_SECTIONS = [
5350
5345
  overview: true,
5351
5346
  title: "Escrow (how funds actually move)",
5352
5347
  body: [
5353
- " M6 lock-at-accept: the buyer funds escrow AFTER the worker accepts.",
5348
+ " The buyer funds escrow AFTER the worker accepts.",
5354
5349
  " `delegation offer` carries terms only (no lock). Once the worker",
5355
5350
  " accepts, `delegation fund` attaches the signed Solana `create_lock`",
5356
5351
  " tx blob; the worker waits for the lock to confirm (LOCKED) before",
5357
5352
  " starting work, knowing the cash is locked. An un-accepted offer has",
5358
5353
  " no lock to strand.",
5359
- " Payment consent is ON-CHAIN (V2): the payer sends claim_work_payment to",
5354
+ " Payment consent is ON-CHAIN: the payer sends claim_work_payment to",
5360
5355
  " approve, or the payee self-claims after the review window. Exit paths:",
5361
5356
  " \u2022 cancel_lock \u2014 buyer cancels while the lock is unaccepted",
5362
5357
  " \u2022 claim_expired_work \u2014 buyer reclaims if the work window lapses",
@@ -5557,7 +5552,7 @@ var GUIDE_SECTIONS = [
5557
5552
  " relationship : status <rel> --json \u2192 .relationshipState (NOT .state)",
5558
5553
  " delegation : status <rel> --json \u2192 .latestDelegation.state",
5559
5554
  " (or delegations <rel> --json \u2192 .[].state)",
5560
- " receipt : receipts <rel> --json \u2192 .[].state / .verdictFinal / .receiptEventHash",
5555
+ " receipt : receipts <rel> --json \u2192 .[].state / .verdictProposed / .receiptEventHash",
5561
5556
  " incoming events : inbox --json \u2192 .[].body.type / .eventId / .serverTimestamp /",
5562
5557
  " .senderDid (NOT .signer)",
5563
5558
  " worker pay key : did-doc <worker-did> --field settlementPublicKey",
@@ -6067,9 +6062,9 @@ async function runInbox(positionalDid, opts) {
6067
6062
  printVerbose(events, "Full event envelopes:", (ev) => ({
6068
6063
  primary: `#${ev.relationshipEventIndex} ${ev.type ?? "<unknown>"}`,
6069
6064
  // eventId is the cursor used by `--before-event-id`;
6070
- // serverEventHash is what `receipt propose` / `cosign`
6071
- // requires. Surface BOTH inline so a copy-paste workflow
6072
- // doesn't have to dig into the JSON below.
6065
+ // serverEventHash is what `receipt propose` requires.
6066
+ // Surface BOTH inline so a copy-paste workflow doesn't have
6067
+ // to dig into the JSON below.
6073
6068
  secondary: `eventId=${ev.eventId} serverEventHash=${ev.serverEventHash}`
6074
6069
  }));
6075
6070
  }
@@ -6194,7 +6189,7 @@ function parseLimit4(raw) {
6194
6189
  // src/commands/keys.ts
6195
6190
  var import_node_crypto2 = require("crypto");
6196
6191
  var import_node_fs7 = require("fs");
6197
- var import_sdk12 = require("@heyanon-arp/sdk");
6192
+ var import_sdk17 = require("@heyanon-arp/sdk");
6198
6193
  var import_chalk17 = __toESM(require("chalk"));
6199
6194
  function writeSecretFile(path, body) {
6200
6195
  const tmp = `${path}.tmp.${(0, import_node_crypto2.randomBytes)(8).toString("hex")}`;
@@ -6221,25 +6216,22 @@ function writeSecretFile(path, body) {
6221
6216
  }
6222
6217
  function registerKeysCommand(root) {
6223
6218
  const keys = root.command("keys").description("Local key utilities");
6224
- 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) => {
6225
- const identity = (0, import_sdk12.generateKeyPair)();
6226
- const settlement = (0, import_sdk12.generateKeyPair)();
6219
+ keys.command("gen").description("Generate a fresh identity + settlement keypair (no save by default)").action(() => {
6220
+ const identity = (0, import_sdk17.generateKeyPair)();
6221
+ const settlement = (0, import_sdk17.generateKeyPair)();
6227
6222
  const out = [
6228
6223
  import_chalk17.default.bold("Identity key (Ed25519)"),
6229
- ` public (base58btc): ${import_chalk17.default.cyan((0, import_sdk12.base58btcEncode)(identity.publicKey))}`,
6224
+ ` public (base58btc): ${import_chalk17.default.cyan((0, import_sdk17.base58btcEncode)(identity.publicKey))}`,
6230
6225
  ` secret (base64) : ${import_chalk17.default.yellow(Buffer.from(identity.secretKey).toString("base64"))}`,
6231
6226
  "",
6232
6227
  import_chalk17.default.bold("Settlement key (Ed25519)"),
6233
- ` public (base58btc): ${import_chalk17.default.cyan((0, import_sdk12.base58btcEncode)(settlement.publicKey))}`,
6228
+ ` public (base58btc): ${import_chalk17.default.cyan((0, import_sdk17.base58btcEncode)(settlement.publicKey))}`,
6234
6229
  ` secret (base64) : ${import_chalk17.default.yellow(Buffer.from(settlement.secretKey).toString("base64"))}`,
6235
6230
  "",
6236
6231
  import_chalk17.default.bold("Resulting DID"),
6237
- ` ${import_chalk17.default.cyan((0, import_sdk12.formatDid)(identity.publicKey))}`
6232
+ ` ${import_chalk17.default.cyan((0, import_sdk17.formatDid)(identity.publicKey))}`
6238
6233
  ];
6239
6234
  console.log(out.join("\n"));
6240
- if (opts.save) {
6241
- console.log(import_chalk17.default.yellow("\nNote: --save is not yet implemented. Capture the secret keys above before they scroll off-screen."));
6242
- }
6243
6235
  });
6244
6236
  keys.command("export <did>").description("Export an agent\u2019s key backup bundle (SECRETS) so you can recover it on another machine with `heyarp recover`. Writes to --out (mode 0600) or stdout.").option("--server <url>", "Override ARP server base URL").option("--out <file>", "Write the bundle to this file (mode 0600) instead of stdout").action((did, opts) => {
6245
6237
  const agent = loadAgentOrThrow(opts.server, did);
@@ -6256,10 +6248,10 @@ function registerKeysCommand(root) {
6256
6248
  });
6257
6249
  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) => {
6258
6250
  const seed = decodeSeed(secretKeyB64);
6259
- const pub = (0, import_sdk12.getPublicKey)(seed);
6260
- const did = (0, import_sdk12.formatDid)(pub);
6251
+ const pub = (0, import_sdk17.getPublicKey)(seed);
6252
+ const did = (0, import_sdk17.formatDid)(pub);
6261
6253
  console.log(`${import_chalk17.default.bold("DID")}: ${import_chalk17.default.cyan(did)}`);
6262
- console.log(`${import_chalk17.default.bold("Identity public key (base58btc)")}: ${import_chalk17.default.cyan((0, import_sdk12.base58btcEncode)(pub))}`);
6254
+ console.log(`${import_chalk17.default.bold("Identity public key (base58btc)")}: ${import_chalk17.default.cyan((0, import_sdk17.base58btcEncode)(pub))}`);
6263
6255
  });
6264
6256
  }
6265
6257
  function decodeSeed(b64) {
@@ -6278,8 +6270,12 @@ function decodeSeed(b64) {
6278
6270
  // src/commands/list.ts
6279
6271
  var import_chalk18 = __toESM(require("chalk"));
6280
6272
  function registerListCommand(root) {
6281
- root.command("list").description("List agents registered locally (~/.arp/agents.json)").action(() => {
6273
+ root.command("list").description("List agents registered locally (~/.heyarp/agents.json)").option("--json", "Machine-readable mode \u2014 emit the local agents as a single JSON array on stdout ({serverUrl, did, name, tags, registeredAt}[]).", false).action((opts) => {
6282
6274
  const rows = listAgents();
6275
+ if (opts.json) {
6276
+ jsonOut(rows.map(({ serverUrl, agent }) => ({ serverUrl, did: agent.did, name: agent.name ?? null, tags: agent.tags ?? [], registeredAt: agent.registeredAt })));
6277
+ return;
6278
+ }
6283
6279
  if (rows.length === 0) {
6284
6280
  console.log(import_chalk18.default.dim(`No local agents. State file: ${stateFilePath()}`));
6285
6281
  return;
@@ -6418,12 +6414,12 @@ function registerLogoutCommand(root) {
6418
6414
  }
6419
6415
 
6420
6416
  // src/commands/profile.ts
6421
- var import_sdk13 = require("@heyanon-arp/sdk");
6417
+ var import_sdk18 = require("@heyanon-arp/sdk");
6422
6418
  var import_chalk21 = __toESM(require("chalk"));
6423
6419
  init_api();
6424
6420
  function registerProfileCommand(root) {
6425
6421
  root.command("profile").description("Composed agent profile: public fields + reputation + liveness, in one read.").argument("<did>", "did:arp:<base58btc> identifier").option("--server <url>", "Override ARP server base URL").option("--json", "Emit the profile as JSON on stdout.", false).action(async (did, opts) => {
6426
- if (!(0, import_sdk13.isValidDid)(did)) {
6422
+ if (!(0, import_sdk18.isValidDid)(did)) {
6427
6423
  throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
6428
6424
  }
6429
6425
  const api = new ArpApiClient(opts.server);
@@ -6451,45 +6447,16 @@ function registerProfileCommand(root) {
6451
6447
  }
6452
6448
 
6453
6449
  // src/commands/receipt.ts
6454
- var import_sdk14 = require("@heyanon-arp/sdk");
6450
+ var import_sdk19 = require("@heyanon-arp/sdk");
6455
6451
  var import_shield2 = require("@heyanon-arp/shield");
6456
6452
  var import_chalk22 = __toESM(require("chalk"));
6457
6453
  init_api();
6458
6454
  function registerReceiptCommands(root) {
6459
- const cmd = root.command("receipt").description("Receipt envelopes \u2014 the payee proposes a delivery record (V2: payment consent is on-chain via claim_work_payment)");
6455
+ const cmd = root.command("receipt").description("Receipt envelopes \u2014 the payee proposes a delivery record (payment consent is on-chain via claim_work_payment)");
6460
6456
  registerPropose(cmd);
6461
6457
  }
6462
- var POST_COMMIT_ERROR_CODES2 = /* @__PURE__ */ new Set([
6463
- "RECEIPT_ALREADY_EXISTS",
6464
- "RECEIPT_DELEGATION_NOT_FOUND",
6465
- "RECEIPT_DELEGATION_NOT_ACTIVE",
6466
- "RECEIPT_RELATIONSHIP_MISMATCH",
6467
- "RECEIPT_ISSUER_IS_CALLER",
6468
- "RECEIPT_NOT_FOUND",
6469
- "RECEIPT_INVALID_STATE",
6470
- // M6 lock-at-accept: the receipt-propose handler's LOCKED gate emits
6471
- // `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
6472
- // the on-chain lock isn't confirmed yet (state
6473
- // `pending_lock_finalization`). It fires from the body handler AFTER
6474
- // the event row is committed — same lifecycle as
6475
- // `RECEIPT_DELEGATION_NOT_ACTIVE` — so the CLI must advance
6476
- // `lastSenderSequence`, otherwise a retry once the lock confirms
6477
- // reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
6478
- "DELEGATION_PENDING_LOCK",
6479
- // response_hash / request_hash / deliverable_hash content
6480
- // verification. Server commits the receipt envelope row BEFORE
6481
- // running the canonical-hash lookup, so a rejection here still
6482
- // consumes the sender sequence — must be in the allowlist or
6483
- // a retry after fixing the hashes would trip
6484
- // `ENV_SEQUENCE_BACKWARDS`.
6485
- "RECEIPT_RESPONSE_HASH_NOT_FOUND",
6486
- "RECEIPT_REQUEST_HASH_NOT_FOUND",
6487
- "RECEIPT_DELIVERABLE_HASH_MISMATCH"
6488
- ]);
6489
- var VERDICT_VALUES = ["accepted", "accepted_with_notes", "rejected"];
6490
- var SHA256_RE = /^sha256:[0-9a-f]{64}$/;
6491
6458
  function registerPropose(parent) {
6492
- parent.command("propose").description("Send a receipt envelope as the PAYEE. Row lands PROPOSED; caller cosigns to flip to COSIGNED.").argument("<recipient-did>", "Recipient agent DID (= caller / offerer of the parent delegation)").argument("<delegation-id>", "Parent delegation id (UUID, must be ACCEPTED)").argument("[request-hash]", "sha256:<64 hex> \u2014 SHA-256 of the work_request payload being settled. Omit when --auto-hashes is set.").argument("[response-hash]", "sha256:<64 hex> \u2014 SHA-256 of the work_response payload being settled. Omit when --auto-hashes is set.").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("--verdict <s>", "verdict_proposed (accepted | accepted_with_notes | rejected)", "accepted").option("--notes-hash <sha256>", "Optional sha256:<64 hex> notes hash").option("--deliverable-hash <sha256>", "Optional sha256:<64 hex> deliverable hash").option("--input-tokens <n>", "Optional usage.input_tokens").option("--output-tokens <n>", "Optional usage.output_tokens").option("--latency-ms <n>", "Optional usage.latency_ms").option("--model <name>", "Optional usage.model").option("--computed-amount <s>", "Optional usage.computed_amount").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option(
6459
+ parent.command("propose").description("Send a receipt envelope as the PAYEE. Row lands PROPOSED; the buyer approves payment on-chain via claim_work_payment.").argument("<recipient-did>", "Recipient agent DID (= caller / offerer of the parent delegation)").argument("<delegation-id>", "Parent delegation id (UUID, must be ACCEPTED)").argument("[request-hash]", "sha256:<64 hex> \u2014 SHA-256 of the work_request payload being settled. Omit when --auto-hashes is set.").argument("[response-hash]", "sha256:<64 hex> \u2014 SHA-256 of the work_response payload being settled. Omit when --auto-hashes is set.").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("--verdict <s>", "verdict_proposed (accepted | accepted_with_notes | rejected)", "accepted").option("--notes-hash <sha256>", "Optional sha256:<64 hex> notes hash").option("--deliverable-hash <sha256>", "Optional sha256:<64 hex> deliverable hash").option("--input-tokens <n>", "Optional usage.input_tokens").option("--output-tokens <n>", "Optional usage.output_tokens").option("--latency-ms <n>", "Optional usage.latency_ms").option("--model <name>", "Optional usage.model").option("--computed-amount <s>", "Optional usage.computed_amount").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option(
6493
6460
  "--auto-hashes",
6494
6461
  "Compute request_hash + response_hash automatically from the work-log row at (--rel-id, <delegation-id>, --request-id). Positional <request-hash>/<response-hash> become optional; if supplied they must match the computed values (consistency check). --rel-id and --request-id auto-resolve from local state when unambiguous \u2014 pass them explicitly only if the lookup finds multiple candidates.",
6495
6462
  false
@@ -6630,7 +6597,7 @@ async function assertSenderIsReceiptPayee(api, sender, relationshipId, delegatio
6630
6597
  }
6631
6598
  if (delegation.offererDid === sender.did) {
6632
6599
  throw new Error(
6633
- `receipt propose: ${sender.did} is the CALLER on delegation ${delegationId} (matches delegation.offererDid). Only the PAYEE \u2014 the counterparty who accepted the delegation offer \u2014 can issue a receipt; as the caller your role is to COSIGN the receipt the payee proposes. Wait for it via \`heyarp status ${relationshipId} --wait --until receipt.proposed\` then run \`heyarp receipt cosign ${relationshipId} ${delegationId} --auto-hashes [--request-id <id>] --settlement-purpose <p> --settlement-expires-at <unix> --payer-sig-from-file <path> --payee-sig-from-file <path>\`.`
6600
+ `receipt propose: ${sender.did} is the CALLER on delegation ${delegationId} (matches delegation.offererDid). Only the PAYEE \u2014 the counterparty who accepted the delegation offer \u2014 can issue a receipt; as the caller your role is to approve payment ON-CHAIN via claim_work_payment once the payee proposes the receipt. Wait for it via \`heyarp status ${relationshipId} --wait --until receipt.proposed\`.`
6634
6601
  );
6635
6602
  }
6636
6603
  return delegation;
@@ -6687,7 +6654,7 @@ async function computeWorkLogHashes(api, sender, relationshipId, delegationId, r
6687
6654
  `receipt propose --auto-hashes: no work-log row found for (relationshipId=${relationshipId}, delegationId=${delegationId}, requestId=${requestId}). Did the work_request envelope land yet?`
6688
6655
  );
6689
6656
  }
6690
- if (workLog.state !== "responded") {
6657
+ if (workLog.state !== import_sdk19.WorkLogStates.RESPONDED) {
6691
6658
  throw new Error(
6692
6659
  `receipt propose --auto-hashes: work-log row ${requestId} is in state '${workLog.state}', not 'responded'. The payee must \`work respond\` before a receipt can be proposed.`
6693
6660
  );
@@ -6703,27 +6670,27 @@ async function computeWorkLogHashes(api, sender, relationshipId, delegationId, r
6703
6670
  };
6704
6671
  const responseBody = workLog.responseOutput !== void 0 ? { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, output: workLog.responseOutput } } : { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, error: workLog.responseError } };
6705
6672
  return {
6706
- requestHash: (0, import_sdk14.canonicalSha256Hex)(requestBody),
6707
- responseHash: (0, import_sdk14.canonicalSha256Hex)(responseBody)
6673
+ requestHash: (0, import_sdk19.canonicalSha256Hex)(requestBody),
6674
+ responseHash: (0, import_sdk19.canonicalSha256Hex)(responseBody)
6708
6675
  };
6709
6676
  }
6710
6677
  async function sendReceiptEnvelope(args) {
6711
6678
  const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
6712
6679
  const protectedBlock = {
6713
- protocol_version: "arp/0.1",
6714
- purpose: import_sdk14.Purpose.ENVELOPE,
6715
- message_id: (0, import_sdk14.uuidV4)(),
6680
+ protocol_version: import_sdk19.CURRENT_PROTOCOL_VERSION,
6681
+ purpose: import_sdk19.Purpose.ENVELOPE,
6682
+ message_id: (0, import_sdk19.uuidV4)(),
6716
6683
  sender_did: args.sender.did,
6717
6684
  recipient_did: args.recipientDid,
6718
6685
  relationship_id: null,
6719
6686
  sender_sequence: nextSequence,
6720
- sender_nonce: (0, import_sdk14.senderNonce)(),
6721
- timestamp: (0, import_sdk14.rfc3339)(),
6722
- expires_at: (0, import_sdk14.expiresAt)(args.ttlSeconds),
6687
+ sender_nonce: (0, import_sdk19.senderNonce)(),
6688
+ timestamp: (0, import_sdk19.rfc3339)(),
6689
+ expires_at: (0, import_sdk19.expiresAt)(args.ttlSeconds),
6723
6690
  delivery_id: null
6724
6691
  };
6725
6692
  const signer = makeSigner(args.sender);
6726
- const envelope = (0, import_sdk14.signEnvelope)({
6693
+ const envelope = (0, import_sdk19.signEnvelope)({
6727
6694
  protected: protectedBlock,
6728
6695
  body: args.body,
6729
6696
  identitySecretKey: signer.identitySecretKey,
@@ -6738,7 +6705,7 @@ async function sendReceiptEnvelope(args) {
6738
6705
  updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
6739
6706
  return result;
6740
6707
  } catch (err) {
6741
- if (err instanceof ApiError && POST_COMMIT_ERROR_CODES2.has(err.payload.code)) {
6708
+ if (err instanceof ApiError && (0, import_sdk19.isPostCommitErrorCode)(err.payload.code)) {
6742
6709
  updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
6743
6710
  }
6744
6711
  throw err;
@@ -6753,9 +6720,9 @@ function printIngestResult2(result) {
6753
6720
  console.log(`${import_chalk22.default.bold("Server event hash")}: ${import_chalk22.default.cyan(result.serverEventHash)}`);
6754
6721
  }
6755
6722
  function parseVerdict(cmdName, raw) {
6756
- if (raw === void 0 || raw === "") return "accepted";
6757
- if (!VERDICT_VALUES.includes(raw)) {
6758
- throw new Error(`${cmdName}: --verdict must be one of ${VERDICT_VALUES.join(" | ")} (got '${raw}')`);
6723
+ if (raw === void 0 || raw === "") return import_sdk19.ReceiptVerdicts.ACCEPTED;
6724
+ if (!import_sdk19.RECEIPT_VERDICTS.includes(raw)) {
6725
+ throw new Error(`${cmdName}: --verdict must be one of ${import_sdk19.RECEIPT_VERDICTS.join(" | ")} (got '${raw}')`);
6759
6726
  }
6760
6727
  return raw;
6761
6728
  }
@@ -6792,7 +6759,7 @@ function requireUuid3(cmdName, raw, label) {
6792
6759
  requireUuid(cmdName, raw, label);
6793
6760
  }
6794
6761
  function requireSha256(cmdName, raw, label) {
6795
- if (!SHA256_RE.test(raw)) {
6762
+ if (!(0, import_sdk19.isSha256Hex)(raw)) {
6796
6763
  throw new Error(`${cmdName}: ${label} must match 'sha256:<64 lowercase hex>' (got '${raw}')`);
6797
6764
  }
6798
6765
  }
@@ -6803,21 +6770,17 @@ function requireDid2(cmdName, did, label) {
6803
6770
  }
6804
6771
 
6805
6772
  // src/commands/receipts.ts
6773
+ var import_sdk20 = require("@heyanon-arp/sdk");
6806
6774
  var import_chalk23 = __toESM(require("chalk"));
6807
6775
  init_api();
6808
- var ALLOWED_STATES2 = /* @__PURE__ */ new Set(["proposed", "cosigned"]);
6809
6776
  function registerReceiptsCommand(root) {
6810
6777
  root.command("receipts").description(
6811
6778
  "List receipts for a relationship (one row per (delegationId, requestHash, responseHash), oldest-first). Receipt rows expose the chain hash as `receiptEventHash` (NOT `serverEventHash` \u2014 that field is the wider envelope identifier on `heyarp events` rows). Scripts using `--json | jq .receiptEventHash` get the value; `jq .serverEventHash` silently returns null because the row doesn't carry that key."
6812
- ).argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by exact state (proposed|cosigned)").option("--delegation-id <uuid>", "Narrow to receipts under a specific delegation id").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(
6779
+ ).argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--delegation-id <uuid>", "Narrow to receipts under a specific delegation id").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(
6813
6780
  "--verbose",
6814
- 'After the one-line summaries, print a framed "Full receipt payloads (N rows)" block \u2014 each row labelled with full delegationId + requestHash + responseHash (the values `receipt cosign` requires) and dumped as JSON. Mutually exclusive with --json.',
6815
- false
6816
- ).option("--json", "Machine-readable mode \u2014 emit a single JSON array of receipt rows, no chalk, no summary. Pipe-safe. Mutually exclusive with --verbose.", false).option(
6817
- "--full-ids",
6818
- "Print delegationId / requestHash / responseHash / DIDs in full (no truncation). REQUIRED for piping into `receipt cosign` which needs the full sha256s.",
6781
+ 'After the one-line summaries, print a framed "Full receipt payloads (N rows)" block \u2014 each row labelled with full delegationId + requestHash + responseHash and dumped as JSON. Mutually exclusive with --json.',
6819
6782
  false
6820
- ).action(async (relationshipId, opts) => {
6783
+ ).option("--json", "Machine-readable mode \u2014 emit a single JSON array of receipt rows, no chalk, no summary. Pipe-safe. Mutually exclusive with --verbose.", false).option("--full-ids", "Print delegationId / requestHash / responseHash / DIDs in full (no truncation).", false).action(async (relationshipId, opts) => {
6821
6784
  await runReceipts(relationshipId, opts);
6822
6785
  });
6823
6786
  }
@@ -6828,7 +6791,6 @@ async function runReceipts(relationshipId, opts) {
6828
6791
  );
6829
6792
  }
6830
6793
  const limit = parseLimit5(opts.limit);
6831
- const state = parseState2(opts.state);
6832
6794
  const api = new ArpApiClient(opts.server);
6833
6795
  const sender = resolveSenderAgent("receipts", opts.server, opts.fromDid);
6834
6796
  if (!opts.json) {
@@ -6837,7 +6799,6 @@ async function runReceipts(relationshipId, opts) {
6837
6799
  console.log(import_chalk23.default.dim(`Relationship: ${relationshipId}`));
6838
6800
  }
6839
6801
  const query = { limit };
6840
- if (state) query.state = state;
6841
6802
  if (opts.delegationId) query.delegationId = opts.delegationId;
6842
6803
  if (opts.after) query.after = opts.after;
6843
6804
  const signer = makeSigner(sender);
@@ -6856,11 +6817,10 @@ async function runReceipts(relationshipId, opts) {
6856
6817
  }
6857
6818
  if (opts.verbose) {
6858
6819
  printVerbose(rows, "Full receipt payloads:", (r) => ({
6859
- primary: `state=${r.state} verdict=${r.verdictFinal ?? r.verdictProposed}`,
6860
- // `receipt cosign` requires the FULL `requestHash` and
6861
- // `responseHash` plus `delegationId`. Putting all three on
6862
- // one inline label keeps the cosign flow grep-able without
6863
- // dropping into `--json | jq`.
6820
+ primary: `verdict=${r.verdictProposed}`,
6821
+ // Surface the FULL `requestHash` + `responseHash` + `delegationId`
6822
+ // on one inline label so the receipt-correlation flow stays
6823
+ // grep-able without dropping into `--json | jq`.
6864
6824
  secondary: `delegationId=${r.delegationId} requestHash=${r.requestHash} responseHash=${r.responseHash}`
6865
6825
  }));
6866
6826
  }
@@ -6872,34 +6832,21 @@ function formatReceiptLine(r, selfDid, opts = {}) {
6872
6832
  const delegationPart = opts.fullIds ? r.delegationId : idHead2(r.delegationId);
6873
6833
  const requestHashPart = opts.fullIds ? r.requestHash : hashHead3(r.requestHash);
6874
6834
  const id = import_chalk23.default.bold(`${delegationPart}/${requestHashPart}`);
6875
- const state = colorState2(r.state).padEnd(stateColumnWidth2());
6876
6835
  const callerHead = opts.fullIds ? r.callerDid : didHead3(r.callerDid);
6877
6836
  const payeeHead = opts.fullIds ? r.payeeDid : didHead3(r.payeeDid);
6878
6837
  const direction = r.payeeDid === selfDid ? `${import_chalk23.default.bold("me")}(payee) \u2192 ${import_chalk23.default.dim(callerHead)}` : `${import_chalk23.default.dim(payeeHead)}(payee) \u2192 ${import_chalk23.default.bold("me")}`;
6879
6838
  const verdict = formatVerdict(r);
6880
6839
  const responseTail = opts.fullIds ? `
6881
6840
  ${import_chalk23.default.dim("responseHash:")} ${import_chalk23.default.cyan(r.responseHash)}` : "";
6882
- return `${id} ${state} ${direction} ${verdict}${responseTail}`;
6883
- }
6884
- function colorState2(s) {
6885
- switch (s) {
6886
- case "proposed":
6887
- return import_chalk23.default.yellow("proposed");
6888
- case "cosigned":
6889
- return import_chalk23.default.green("cosigned");
6890
- }
6891
- }
6892
- function stateColumnWidth2() {
6893
- return 8;
6841
+ return `${id} ${direction} ${verdict}${responseTail}`;
6894
6842
  }
6895
6843
  function formatVerdict(r) {
6896
- const final = r.verdictFinal ?? r.verdictProposed;
6897
- switch (final) {
6898
- case "accepted":
6844
+ switch (r.verdictProposed) {
6845
+ case import_sdk20.ReceiptVerdicts.ACCEPTED:
6899
6846
  return import_chalk23.default.green("accepted");
6900
- case "accepted_with_notes":
6847
+ case import_sdk20.ReceiptVerdicts.ACCEPTED_WITH_NOTES:
6901
6848
  return import_chalk23.default.yellow("accepted_with_notes");
6902
- case "rejected":
6849
+ case import_sdk20.ReceiptVerdicts.REJECTED:
6903
6850
  return import_chalk23.default.red("rejected");
6904
6851
  }
6905
6852
  }
@@ -6915,13 +6862,6 @@ function didHead3(did) {
6915
6862
  if (did.length <= 20) return did;
6916
6863
  return `${did.slice(0, 20)}...`;
6917
6864
  }
6918
- function parseState2(raw) {
6919
- if (raw === void 0) return void 0;
6920
- if (!ALLOWED_STATES2.has(raw)) {
6921
- throw new Error(`receipts: --state must be one of proposed|cosigned (got '${raw}')`);
6922
- }
6923
- return raw;
6924
- }
6925
6865
  function parseLimit5(raw) {
6926
6866
  if (raw === void 0) return 20;
6927
6867
  const n = Number(raw);
@@ -6933,7 +6873,7 @@ function parseLimit5(raw) {
6933
6873
 
6934
6874
  // src/commands/recover.ts
6935
6875
  var import_node_fs8 = require("fs");
6936
- var import_sdk15 = require("@heyanon-arp/sdk");
6876
+ var import_sdk21 = require("@heyanon-arp/sdk");
6937
6877
  var import_chalk24 = __toESM(require("chalk"));
6938
6878
  init_api();
6939
6879
  function registerRecoverCommand(root) {
@@ -6954,18 +6894,18 @@ async function runRecover(opts) {
6954
6894
  }
6955
6895
  const bundle = validateKeyBundle(raw);
6956
6896
  const identitySecret = new Uint8Array(Buffer.from(bundle.identitySecretKeyB64, "base64"));
6957
- const identityPub = (0, import_sdk15.base58btcDecode)(bundle.identityPublicKeyB58);
6958
- const derivedDid = (0, import_sdk15.formatDid)(identityPub);
6897
+ const identityPub = (0, import_sdk21.base58btcDecode)(bundle.identityPublicKeyB58);
6898
+ const derivedDid = (0, import_sdk21.formatDid)(identityPub);
6959
6899
  if (derivedDid !== bundle.did) {
6960
6900
  throw new Error(`recover: key file is inconsistent \u2014 its DID (${bundle.did}) does not match its identity public key (${derivedDid})`);
6961
6901
  }
6962
6902
  const probe2 = new TextEncoder().encode("heyarp-recover-keycheck");
6963
- if (!(0, import_sdk15.verify)((0, import_sdk15.sign)(probe2, identitySecret), probe2, identityPub)) {
6903
+ if (!(0, import_sdk21.verify)((0, import_sdk21.sign)(probe2, identitySecret), probe2, identityPub)) {
6964
6904
  throw new Error("recover: the identity secret key does not match the public key in this bundle");
6965
6905
  }
6966
6906
  const settlementSecret = new Uint8Array(Buffer.from(bundle.settlementSecretKeyB64, "base64"));
6967
- const settlementPub = (0, import_sdk15.base58btcDecode)(bundle.settlementPublicKeyB58);
6968
- if (!(0, import_sdk15.verify)((0, import_sdk15.sign)(probe2, settlementSecret), probe2, settlementPub)) {
6907
+ const settlementPub = (0, import_sdk21.base58btcDecode)(bundle.settlementPublicKeyB58);
6908
+ if (!(0, import_sdk21.verify)((0, import_sdk21.sign)(probe2, settlementSecret), probe2, settlementPub)) {
6969
6909
  throw new Error("recover: the settlement secret key does not match the settlement public key in this bundle");
6970
6910
  }
6971
6911
  if (bundle.ownerWallet && bundle.ownerWallet !== credential.wallet) {
@@ -6987,8 +6927,10 @@ async function runRecover(opts) {
6987
6927
  lastSenderSequence = seq.lastSenderSequence;
6988
6928
  } catch (err) {
6989
6929
  process.stderr.write(
6990
- import_chalk24.default.yellow(`\u26A0 could not read the sender sequence from the server (${err.message}). If sends are rejected as backwards, run \`heyarp escrow recover-sequence --apply\`.
6991
- `)
6930
+ import_chalk24.default.yellow(
6931
+ `\u26A0 could not read the sender sequence from the server (${err.message}). If sends are rejected as backwards, run \`heyarp escrow recover-sequence --apply\`.
6932
+ `
6933
+ )
6992
6934
  );
6993
6935
  }
6994
6936
  const state = {
@@ -7019,7 +6961,7 @@ async function runRecover(opts) {
7019
6961
  // src/commands/register.ts
7020
6962
  var import_node_crypto4 = require("crypto");
7021
6963
  var import_node_fs9 = require("fs");
7022
- var import_sdk16 = require("@heyanon-arp/sdk");
6964
+ var import_sdk22 = require("@heyanon-arp/sdk");
7023
6965
  var import_chalk25 = __toESM(require("chalk"));
7024
6966
  var import_prompts2 = __toESM(require("prompts"));
7025
6967
  init_api();
@@ -7040,13 +6982,12 @@ function registerRegisterCommand(root) {
7040
6982
  // logged by CI runners (e.g. GitHub Actions echoes the
7041
6983
  // full command). Safer alternatives:
7042
6984
  // • interactive prompt (default) — never written anywhere
7043
- // • `HEYARP_PASSWORD` env var (V1.5 — tracked) reads
7044
- // from env so the secret stays out of argv even in
7045
- // scripts
6985
+ // • `HEYARP_PASSWORD` env var (tracked) reads from env so
6986
+ // the secret stays out of argv even in scripts
7046
6987
  // Treat `--password <s>` as a one-off local-dev affordance;
7047
6988
  // do NOT use it in CI pipelines without a secret-redacting
7048
6989
  // runner.
7049
- "Owner password \u2014 MUST be at least 8 characters. REQUIRED when --yes is set; otherwise the CLI prompts for it. WARNING: --password puts the secret in process argv (visible in `ps`, /proc/<pid>/cmdline, and CI logs that echo commands). Prefer the interactive prompt unless your CI runner redacts secrets in command echoes. V1.5: HEYARP_PASSWORD env var support tracked."
6990
+ "Owner password \u2014 MUST be at least 8 characters. REQUIRED when --yes is set; otherwise the CLI prompts for it. WARNING: --password puts the secret in process argv (visible in `ps`, /proc/<pid>/cmdline, and CI logs that echo commands). Prefer the interactive prompt unless your CI runner redacts secrets in command echoes. HEYARP_PASSWORD env var support is tracked."
7050
6991
  ).option(
7051
6992
  "--yes",
7052
6993
  "Strict non-interactive: fail if any required field is still missing after merging flags. With --yes, --password must be supplied explicitly (>= 8 chars).",
@@ -7057,7 +6998,6 @@ function registerRegisterCommand(root) {
7057
6998
  // success prints. Output shape:
7058
6999
  // {did, settlementPublicKeyB58, identityPublicKeyB58,
7059
7000
  // localStatePath, didDocument}
7060
- // (V1-alpha: `accountId` parked from the output.)
7061
7001
  // REQUIRES --yes (interactive prompts would corrupt the
7062
7002
  // single-doc contract).
7063
7003
  "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).",
@@ -7083,41 +7023,39 @@ async function runRegister(opts, deps = defaultRegisterDeps) {
7083
7023
  warnIfOrphanHomesPresent();
7084
7024
  }
7085
7025
  const keys = opts.fromKeys ? loadKeysFromFile(opts.fromKeys) : freshKeys();
7086
- const did = (0, import_sdk16.formatDid)(keys.identityPublicKey);
7026
+ const did = (0, import_sdk22.formatDid)(keys.identityPublicKey);
7087
7027
  if (!opts.json) console.log(import_chalk25.default.dim(`DID will be: ${did}`));
7088
7028
  const answers = await mergeAnswers(opts);
7089
7029
  const scryptSalt = (0, import_node_crypto4.randomBytes)(16);
7090
7030
  if (!opts.json) console.log(import_chalk25.default.dim("Deriving scrypt key, this may take a moment..."));
7091
- const scryptKey = (0, import_sdk16.deriveScryptKey)(answers.password, new Uint8Array(scryptSalt));
7031
+ const scryptKey = (0, import_sdk22.deriveScryptKey)(answers.password, new Uint8Array(scryptSalt));
7092
7032
  const challenge = await api.issueChallenge("register");
7093
7033
  const challengeBytes = base64UrlNoPadDecode(challenge.challengeB64);
7094
7034
  if (challengeBytes.length !== 32) {
7095
7035
  throw new Error(`Server returned a ${challengeBytes.length}-byte challenge; expected 32`);
7096
7036
  }
7097
- const challengeSig = (0, import_sdk16.signChallenge)(challengeBytes, keys.identitySecretKey);
7098
- const identityPublicKeyB58 = (0, import_sdk16.base58btcEncode)(keys.identityPublicKey);
7037
+ const challengeSig = (0, import_sdk22.signChallenge)(challengeBytes, keys.identitySecretKey);
7038
+ const identityPublicKeyB58 = (0, import_sdk22.base58btcEncode)(keys.identityPublicKey);
7099
7039
  await api.submitChallengeResponse({
7100
7040
  challengeId: challenge.challengeId,
7101
7041
  identityPublicKey: identityPublicKeyB58,
7102
7042
  signature: Buffer.from(challengeSig).toString("base64")
7103
7043
  });
7104
- const settlementPublicKeyB58 = (0, import_sdk16.base58btcEncode)(keys.settlementPublicKey);
7105
- const scryptSaltId = (0, import_sdk16.uuidV4)();
7044
+ const settlementPublicKeyB58 = (0, import_sdk22.base58btcEncode)(keys.settlementPublicKey);
7045
+ const scryptSaltId = (0, import_sdk22.uuidV4)();
7106
7046
  const payload = {
7107
- purpose: "ARP-KEY-LINK-v1",
7047
+ purpose: import_sdk22.Purpose.KEY_LINK,
7108
7048
  agent_did: did,
7109
7049
  identity_public_key: identityPublicKeyB58,
7110
7050
  settlement_public_key: settlementPublicKeyB58,
7111
- owner_signing_method: "scrypt_password_proof",
7051
+ owner_signing_method: import_sdk22.OWNER_SIGNING_METHODS[0],
7112
7052
  link_method: "manual",
7113
- created_at: (0, import_sdk16.rfc3339)(),
7114
- nonce: (0, import_sdk16.senderNonce)()
7053
+ created_at: (0, import_sdk22.rfc3339)(),
7054
+ nonce: (0, import_sdk22.senderNonce)()
7115
7055
  };
7116
- const attestation = (0, import_sdk16.signKeyLinkAttestation)({ payload, scryptKey, scryptSaltId });
7056
+ const attestation = (0, import_sdk22.signKeyLinkAttestation)({ payload, scryptKey, scryptSaltId });
7117
7057
  const body = {
7118
7058
  challengeId: challenge.challengeId,
7119
- // V1-alpha: accountId parked — server's DTO no longer accepts it.
7120
- // accountId: answers.accountId,
7121
7059
  identityPublicKey: identityPublicKeyB58,
7122
7060
  settlementPublicKey: settlementPublicKeyB58,
7123
7061
  ownerAttestation: {
@@ -7158,7 +7096,7 @@ async function runRegister(opts, deps = defaultRegisterDeps) {
7158
7096
  try {
7159
7097
  result = await api.register(body, credential.token);
7160
7098
  } catch (err) {
7161
- if (err instanceof ApiError && (err.payload.code === "AUTH_TOKEN_INVALID" || err.payload.code === "AUTH_TOKEN_REQUIRED")) {
7099
+ if (err instanceof ApiError && (err.payload.code === import_sdk22.CliAuthTokenErrorCodes.INVALID || err.payload.code === import_sdk22.CliAuthTokenErrorCodes.REQUIRED)) {
7162
7100
  throw new Error(
7163
7101
  `register: the stored login token for ${api.serverUrl} was rejected (${err.payload.message}) \u2014 run 'heyarp login' again. Your keys are saved locally; re-running register with --from-keys is safe.`
7164
7102
  );
@@ -7210,20 +7148,11 @@ Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
7210
7148
  }
7211
7149
  }
7212
7150
  async function mergeAnswers(opts) {
7213
- const need = opts.yes ? {
7214
- password: false,
7215
- name: false,
7216
- description: false,
7217
- tagsCsv: false
7218
- /* accountId parked V1-alpha */
7219
- } : {
7151
+ const need = opts.yes ? { password: false, name: false, description: false, tagsCsv: false } : {
7220
7152
  password: opts.password === void 0,
7221
7153
  name: opts.name === void 0,
7222
7154
  description: opts.description === void 0,
7223
7155
  tagsCsv: opts.tag === void 0 || opts.tag.length === 0
7224
- // V1-alpha: accountId parked. Uncomment + restore the prompt
7225
- // below when launchpad↔ARP join lands.
7226
- // accountId: opts.accountId === undefined,
7227
7156
  };
7228
7157
  if (opts.yes) {
7229
7158
  const missing = [];
@@ -7275,7 +7204,6 @@ async function mergeAnswers(opts) {
7275
7204
  password: opts.password ?? prompted.password,
7276
7205
  name: opts.name ?? prompted.name,
7277
7206
  description: opts.description ?? prompted.description ?? "",
7278
- // accountId, // V1-alpha parked — see RegisterAnswers above.
7279
7207
  tags
7280
7208
  };
7281
7209
  }
@@ -7311,7 +7239,7 @@ function warnIfAgentsAlreadyRegistered(serverOverride) {
7311
7239
  const existing = listAgents().filter((row) => row.serverUrl === targetServer);
7312
7240
  if (existing.length === 0) return;
7313
7241
  const list = existing.map((row) => ` \u2022 ${import_chalk25.default.cyan(row.agent.did)}${row.agent.name ? import_chalk25.default.dim(` (${row.agent.name})`) : ""}`).join("\n");
7314
- console.log(import_chalk25.default.yellow("\n\u26A0 ~/.arp/agents.json already has agent(s) for this server:"));
7242
+ console.log(import_chalk25.default.yellow("\n\u26A0 ~/.heyarp/agents.json already has agent(s) for this server:"));
7315
7243
  console.log(list);
7316
7244
  console.log(
7317
7245
  import_chalk25.default.dim(
@@ -7324,8 +7252,8 @@ function parseTagsCsv(raw) {
7324
7252
  return raw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
7325
7253
  }
7326
7254
  function freshKeys() {
7327
- const identity = (0, import_sdk16.generateKeyPair)();
7328
- const settlement = (0, import_sdk16.generateKeyPair)();
7255
+ const identity = (0, import_sdk22.generateKeyPair)();
7256
+ const settlement = (0, import_sdk22.generateKeyPair)();
7329
7257
  return {
7330
7258
  identityPublicKey: identity.publicKey,
7331
7259
  identitySecretKey: identity.secretKey,
@@ -7351,9 +7279,9 @@ function loadKeysFromFile(path) {
7351
7279
  if (identitySecret.length !== 32) throw new Error("--from-keys: identitySecretKeyB64 is not a 32-byte Ed25519 seed");
7352
7280
  if (settlementSecret.length !== 32) throw new Error("--from-keys: settlementSecretKeyB64 is not a 32-byte Ed25519 seed");
7353
7281
  return {
7354
- identityPublicKey: (0, import_sdk16.getPublicKey)(identitySecret),
7282
+ identityPublicKey: (0, import_sdk22.getPublicKey)(identitySecret),
7355
7283
  identitySecretKey: identitySecret,
7356
- settlementPublicKey: (0, import_sdk16.getPublicKey)(settlementSecret),
7284
+ settlementPublicKey: (0, import_sdk22.getPublicKey)(settlementSecret),
7357
7285
  settlementSecretKey: settlementSecret
7358
7286
  };
7359
7287
  }
@@ -7369,19 +7297,29 @@ function assertJsonRequiresYes(opts) {
7369
7297
  }
7370
7298
 
7371
7299
  // src/commands/relationships.ts
7300
+ var import_sdk23 = require("@heyanon-arp/sdk");
7372
7301
  var import_chalk26 = __toESM(require("chalk"));
7373
7302
  init_api();
7374
- var ALLOWED_STATES3 = /* @__PURE__ */ new Set(["pending", "active", "paused", "closed"]);
7303
+ var ALLOWED_STATES2 = new Set(import_sdk23.RELATIONSHIP_STATE_NAMES);
7375
7304
  function registerRelationshipsCommand(root) {
7376
7305
  root.command("relationships").description(
7377
7306
  "List relationships <did> belongs to (live + closed pairs, ordered by lastEventAt desc). DID can be passed positionally OR via --from-did; if both are omitted the resolver picks the sole local agent for the server (see `heyarp config set server` + multi-DID disambiguation rules)."
7378
- ).argument("[did]", "Agent DID (did:arp:...) \u2014 must have local state. Optional when --from-did is given OR exactly one agent is registered for the resolved server.").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by relationship state (pending|active|paused|closed)").option("--limit <n>", "Max relationships to return (1..100)", "20").option("--verbose", "Print the full JSON for each relationship in addition to the table", false).option("--from-did <did>", "Alias for the positional <did> argument \u2014 accepted for consistency with other signed commands. Must match the positional if both are given.").action(async (did, opts) => {
7307
+ ).argument("[did]", "Agent DID (did:arp:...) \u2014 must have local state. Optional when --from-did is given OR exactly one agent is registered for the resolved server.").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by relationship state (pending|active|paused|closed)").option("--limit <n>", "Max relationships to return (1..100)", "20").option("--verbose", "Print the full JSON for each relationship in addition to the table. Mutually exclusive with --json.", false).option(
7308
+ "--json",
7309
+ "Machine-readable mode \u2014 emit the relationships as a single JSON array on stdout (RelationshipPublic[]). Prelude + table move to stderr. Mutually exclusive with --verbose.",
7310
+ false
7311
+ ).option("--from-did <did>", "Alias for the positional <did> argument \u2014 accepted for consistency with other signed commands. Must match the positional if both are given.").action(async (did, opts) => {
7379
7312
  await runRelationships(did, opts);
7380
7313
  });
7381
7314
  }
7382
7315
  async function runRelationships(positionalDid, opts) {
7316
+ if (opts.verbose && opts.json) {
7317
+ throw new Error(
7318
+ "relationships: --verbose and --json are mutually exclusive. --json emits the rows as a clean array; --verbose adds per-row dumps that would break `--json | jq`."
7319
+ );
7320
+ }
7383
7321
  const limit = parseLimit6(opts.limit);
7384
- const state = parseState3(opts.state);
7322
+ const state = parseState2(opts.state);
7385
7323
  if (positionalDid !== void 0 && opts.fromDid !== void 0 && positionalDid !== opts.fromDid) {
7386
7324
  throw new Error(`relationships: positional <did> (${positionalDid}) and --from-did (${opts.fromDid}) disagree \u2014 pass only one`);
7387
7325
  }
@@ -7389,12 +7327,16 @@ async function runRelationships(positionalDid, opts) {
7389
7327
  const local = explicitDid !== void 0 ? loadAgentOrThrow(opts.server, explicitDid) : resolveSenderAgent("relationships", opts.server, void 0);
7390
7328
  const did = local.did;
7391
7329
  const api = new ArpApiClient(opts.server);
7392
- console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
7393
- console.log(import_chalk26.default.dim(`Signer: ${local.did}`));
7330
+ progress(opts.json, import_chalk26.default.dim(`Server: ${api.serverUrl}`));
7331
+ progress(opts.json, import_chalk26.default.dim(`Signer: ${local.did}`));
7394
7332
  const query = { limit };
7395
7333
  if (state) query.state = state;
7396
7334
  const signer = makeSigner(local);
7397
7335
  const rows = await api.listRelationships(did, signer, query);
7336
+ if (opts.json) {
7337
+ jsonOut(rows);
7338
+ return;
7339
+ }
7398
7340
  if (rows.length === 0) {
7399
7341
  console.log(import_chalk26.default.dim("\n(no relationships)"));
7400
7342
  return;
@@ -7422,9 +7364,9 @@ function otherPair(r, selfDid) {
7422
7364
  if (r.pairDidB === selfDid) return r.pairDidA;
7423
7365
  return `${r.pairDidA} \u2194 ${r.pairDidB}`;
7424
7366
  }
7425
- function parseState3(raw) {
7367
+ function parseState2(raw) {
7426
7368
  if (raw === void 0) return void 0;
7427
- if (!ALLOWED_STATES3.has(raw)) {
7369
+ if (!ALLOWED_STATES2.has(raw)) {
7428
7370
  throw new Error(`relationships: --state must be one of pending|active|paused|closed (got '${raw}')`);
7429
7371
  }
7430
7372
  return raw;
@@ -7439,12 +7381,12 @@ function parseLimit6(raw) {
7439
7381
  }
7440
7382
 
7441
7383
  // src/commands/reputation.ts
7442
- var import_sdk17 = require("@heyanon-arp/sdk");
7384
+ var import_sdk24 = require("@heyanon-arp/sdk");
7443
7385
  var import_chalk27 = __toESM(require("chalk"));
7444
7386
  init_api();
7445
7387
  function registerReputationCommand(root) {
7446
7388
  root.command("reputation").description("Show an agent's informational reputation (composite + component scores + raw counters). Public, no auth. NOT a money/eligibility gate.").argument("<did>", "did:arp:<base58btc> identifier").option("--server <url>", "Override ARP server base URL").option("--json", "Emit the reputation as JSON on stdout.", false).action(async (did, opts) => {
7447
- if (!(0, import_sdk17.isValidDid)(did)) {
7389
+ if (!(0, import_sdk24.isValidDid)(did)) {
7448
7390
  throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
7449
7391
  }
7450
7392
  const api = new ArpApiClient(opts.server);
@@ -7486,44 +7428,53 @@ function bar(score) {
7486
7428
  }
7487
7429
 
7488
7430
  // src/commands/send-handshake.ts
7489
- var import_sdk18 = require("@heyanon-arp/sdk");
7431
+ var import_sdk25 = require("@heyanon-arp/sdk");
7490
7432
  var import_chalk28 = __toESM(require("chalk"));
7491
7433
  init_api();
7492
7434
  function registerSendHandshakeCommand(root) {
7493
- root.command("send-handshake").description("Send a handshake envelope to <recipient-did>. Server creates the relationship row on first contact.").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("--greeting <s>", "Optional greeting text included in body.content").option("--intent <s>", "Optional intent text included in body.content").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).action(async (recipientDid, opts) => {
7435
+ root.command("send-handshake").description("Send a handshake envelope to <recipient-did>. Server creates the relationship row on first contact.").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("--greeting <s>", "Optional greeting text included in body.content").option("--intent <s>", "Optional intent text included in body.content").option("--ttl <seconds>", `Envelope TTL in seconds (max ${import_sdk25.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
7436
+ "--json",
7437
+ "Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, eventId, relationshipId, relationshipEventIndex, serverTimestamp, signedMessageHash, serverEventHash, prevServerEventHash, senderSequence}). The Server/Sender/Recipient prelude moves to stderr so `--json | jq` stays byte-pure. Mutually exclusive with --verbose.",
7438
+ false
7439
+ ).action(async (recipientDid, opts) => {
7494
7440
  await runSendHandshake(recipientDid, opts);
7495
7441
  });
7496
7442
  }
7497
7443
  async function runSendHandshake(recipientDid, opts) {
7498
- if (!isDid(recipientDid)) {
7499
- throw new Error(`send-handshake: <recipient-did> must look like 'did:arp:...' (got '${recipientDid}')`);
7444
+ if (!(0, import_sdk25.isValidDid)(recipientDid)) {
7445
+ throw new Error(`send-handshake: <recipient-did> must be a valid did:arp identifier (base58btc-encoded 32-byte Ed25519 pubkey) (got '${recipientDid}')`);
7446
+ }
7447
+ if (opts.verbose && opts.json) {
7448
+ throw new Error(
7449
+ "send-handshake: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds envelope + response dumps that would break `--json | jq`."
7450
+ );
7500
7451
  }
7501
7452
  const ttlSeconds = parseTtl3(opts.ttl);
7502
7453
  const api = new ArpApiClient(opts.server);
7503
- console.log(import_chalk28.default.dim(`Server: ${api.serverUrl}`));
7454
+ progress(opts.json, import_chalk28.default.dim(`Server: ${api.serverUrl}`));
7504
7455
  const sender = resolveSenderAgent("send-handshake", opts.server, opts.fromDid);
7505
- console.log(import_chalk28.default.dim(`Sender: ${sender.did}`));
7506
- console.log(import_chalk28.default.dim(`Recipient: ${recipientDid}`));
7456
+ progress(opts.json, import_chalk28.default.dim(`Sender: ${sender.did}`));
7457
+ progress(opts.json, import_chalk28.default.dim(`Recipient: ${recipientDid}`));
7507
7458
  const content = {};
7508
7459
  if (opts.greeting) content.greeting = opts.greeting;
7509
7460
  if (opts.intent) content.intent = opts.intent;
7510
7461
  const body = { type: "handshake", content };
7511
7462
  const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
7512
7463
  const protectedBlock = {
7513
- protocol_version: "arp/0.1",
7514
- purpose: import_sdk18.Purpose.ENVELOPE,
7515
- message_id: (0, import_sdk18.uuidV4)(),
7464
+ protocol_version: import_sdk25.CURRENT_PROTOCOL_VERSION,
7465
+ purpose: import_sdk25.Purpose.ENVELOPE,
7466
+ message_id: (0, import_sdk25.uuidV4)(),
7516
7467
  sender_did: sender.did,
7517
7468
  recipient_did: recipientDid,
7518
7469
  relationship_id: null,
7519
7470
  sender_sequence: nextSequence,
7520
- sender_nonce: (0, import_sdk18.senderNonce)(),
7521
- timestamp: (0, import_sdk18.rfc3339)(),
7522
- expires_at: (0, import_sdk18.expiresAt)(ttlSeconds),
7471
+ sender_nonce: (0, import_sdk25.senderNonce)(),
7472
+ timestamp: (0, import_sdk25.rfc3339)(),
7473
+ expires_at: (0, import_sdk25.expiresAt)(ttlSeconds),
7523
7474
  delivery_id: null
7524
7475
  };
7525
7476
  const signer = makeSigner(sender);
7526
- const envelope = (0, import_sdk18.signEnvelope)({
7477
+ const envelope = (0, import_sdk25.signEnvelope)({
7527
7478
  protected: protectedBlock,
7528
7479
  body,
7529
7480
  identitySecretKey: signer.identitySecretKey
@@ -7534,6 +7485,21 @@ async function runSendHandshake(recipientDid, opts) {
7534
7485
  }
7535
7486
  const result = await api.ingest(envelope);
7536
7487
  updateAgentLocal(opts.server, sender.did, { lastSenderSequence: nextSequence });
7488
+ if (opts.json) {
7489
+ jsonOut({
7490
+ ok: true,
7491
+ action: "handshake",
7492
+ eventId: result.eventId,
7493
+ relationshipId: result.relationshipId,
7494
+ relationshipEventIndex: result.relationshipEventIndex,
7495
+ serverTimestamp: result.serverTimestamp,
7496
+ signedMessageHash: result.signedMessageHash,
7497
+ serverEventHash: result.serverEventHash,
7498
+ prevServerEventHash: result.prevServerEventHash ?? null,
7499
+ senderSequence: nextSequence
7500
+ });
7501
+ return;
7502
+ }
7537
7503
  console.log(import_chalk28.default.green("\nDelivered."));
7538
7504
  console.log(`${import_chalk28.default.bold("Event id")}: ${import_chalk28.default.cyan(result.eventId)}`);
7539
7505
  console.log(`${import_chalk28.default.bold("Relationship id")}: ${import_chalk28.default.cyan(result.relationshipId)}`);
@@ -7561,15 +7527,12 @@ function parseTtl3(raw) {
7561
7527
  }
7562
7528
  return n;
7563
7529
  }
7564
- function isDid(s) {
7565
- return typeof s === "string" && s.startsWith("did:arp:") && s.length > "did:arp:".length;
7566
- }
7567
7530
 
7568
7531
  // src/commands/send-handshake-response.ts
7569
- var import_sdk19 = require("@heyanon-arp/sdk");
7532
+ var import_sdk26 = require("@heyanon-arp/sdk");
7570
7533
  var import_chalk29 = __toESM(require("chalk"));
7571
7534
  init_api();
7572
- var ALLOWED_DECISIONS = /* @__PURE__ */ new Set(["accept", "decline"]);
7535
+ var ALLOWED_DECISIONS = new Set(import_sdk26.HANDSHAKE_DECISIONS);
7573
7536
  function registerSendHandshakeResponseCommand(root) {
7574
7537
  root.command("send-handshake-response").description("Accept or decline an inbound handshake from <recipient-did>. Server reuses the existing relationship row.").argument("<recipient-did>", "Recipient agent DID (did:arp:...) \u2014 the original handshake sender").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("--decision <s>", "REQUIRED: accept or decline").option("--notes <s>", "Optional notes included in body.content").option(
7575
7538
  "--reason <code>",
@@ -7577,8 +7540,8 @@ function registerSendHandshakeResponseCommand(root) {
7577
7540
  // We don't use commander's requiredOption because it
7578
7541
  // would fire for the accept path too; validate manually
7579
7542
  // after decision is parsed.
7580
- `When --decision=decline: required reason code (one of: ${import_sdk19.DECLINE_REASONS.join(", ")}). Carried in body.content.reason.`
7581
- ).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(
7543
+ `When --decision=decline: required reason code (one of: ${import_sdk26.DECLINE_REASONS.join(", ")}). Carried in body.content.reason.`
7544
+ ).option("--reason-detail <s>", "Optional free-text elaboration alongside --reason (max 512 chars).").option("--ttl <seconds>", `Envelope TTL in seconds (max ${import_sdk26.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
7582
7545
  "--json",
7583
7546
  "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.",
7584
7547
  false
@@ -7596,13 +7559,13 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7596
7559
  "send-handshake-response: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds envelope + response dumps that would break `--json | jq`."
7597
7560
  );
7598
7561
  }
7599
- if (!isDid2(recipientDid)) {
7600
- throw new Error(`send-handshake-response: <recipient-did> must look like 'did:arp:...' (got '${recipientDid}')`);
7562
+ if (!(0, import_sdk26.isValidDid)(recipientDid)) {
7563
+ throw new Error(`send-handshake-response: <recipient-did> must be a valid did:arp identifier (base58btc-encoded 32-byte Ed25519 pubkey) (got '${recipientDid}')`);
7601
7564
  }
7602
7565
  const decision = parseDecision(opts.decision);
7603
7566
  const ttlSeconds = parseTtl4(opts.ttl);
7604
7567
  let declinePayload = null;
7605
- if (decision === "decline") {
7568
+ if (decision === import_sdk26.HandshakeDecisions.DECLINE) {
7606
7569
  const reason = parseDeclineReason("send-handshake-response", opts.reason);
7607
7570
  const detail = parseReasonDetail("send-handshake-response", opts.reasonDetail);
7608
7571
  declinePayload = detail ? { reason, reasonDetail: detail } : { reason };
@@ -7628,6 +7591,7 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7628
7591
  if (opts.json) {
7629
7592
  jsonOut({
7630
7593
  ok: true,
7594
+ action: "handshake_response",
7631
7595
  idempotent: true,
7632
7596
  decision,
7633
7597
  eventId: previousResponseFromMe.eventId,
@@ -7670,19 +7634,19 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7670
7634
  const body = { type: "handshake_response", content };
7671
7635
  const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
7672
7636
  const protectedBlock = {
7673
- protocol_version: "arp/0.1",
7674
- purpose: import_sdk19.Purpose.ENVELOPE,
7675
- message_id: (0, import_sdk19.uuidV4)(),
7637
+ protocol_version: import_sdk26.CURRENT_PROTOCOL_VERSION,
7638
+ purpose: import_sdk26.Purpose.ENVELOPE,
7639
+ message_id: (0, import_sdk26.uuidV4)(),
7676
7640
  sender_did: sender.did,
7677
7641
  recipient_did: recipientDid,
7678
7642
  relationship_id: null,
7679
7643
  sender_sequence: nextSequence,
7680
- sender_nonce: (0, import_sdk19.senderNonce)(),
7681
- timestamp: (0, import_sdk19.rfc3339)(),
7682
- expires_at: (0, import_sdk19.expiresAt)(ttlSeconds),
7644
+ sender_nonce: (0, import_sdk26.senderNonce)(),
7645
+ timestamp: (0, import_sdk26.rfc3339)(),
7646
+ expires_at: (0, import_sdk26.expiresAt)(ttlSeconds),
7683
7647
  delivery_id: null
7684
7648
  };
7685
- const envelope = (0, import_sdk19.signEnvelope)({
7649
+ const envelope = (0, import_sdk26.signEnvelope)({
7686
7650
  protected: protectedBlock,
7687
7651
  body,
7688
7652
  identitySecretKey: signer.identitySecretKey
@@ -7696,6 +7660,7 @@ async function runSendHandshakeResponse(recipientDid, opts) {
7696
7660
  if (opts.json) {
7697
7661
  jsonOut({
7698
7662
  ok: true,
7663
+ action: "handshake_response",
7699
7664
  decision,
7700
7665
  eventId: result.eventId,
7701
7666
  relationshipId: result.relationshipId,
@@ -7744,13 +7709,10 @@ function parseTtl4(raw) {
7744
7709
  }
7745
7710
  return n;
7746
7711
  }
7747
- function isDid2(s) {
7748
- return typeof s === "string" && s.startsWith("did:arp:") && s.length > "did:arp:".length;
7749
- }
7750
7712
  function classifyIdempotencyOutcome(decision, existing) {
7751
7713
  if (!existing) return { kind: "proceed" };
7752
- if (existing.state === "active") {
7753
- if (decision === "decline") {
7714
+ if (existing.state === import_sdk26.RelationshipStates.ACTIVE) {
7715
+ if (decision === import_sdk26.HandshakeDecisions.DECLINE) {
7754
7716
  return {
7755
7717
  kind: "error",
7756
7718
  message: `send-handshake-response: relationship ${existing.relationshipId} is already 'active' from a previous ACCEPT. A decline cannot reverse that \u2014 the active relationship is now under FSM control. If you want to terminate, close the relationship explicitly; pass --force to ignore this check and send the decline (which the server will reject with DOM_INVALID_TRANSITION).`
@@ -7758,7 +7720,7 @@ function classifyIdempotencyOutcome(decision, existing) {
7758
7720
  }
7759
7721
  return { kind: "short-circuit" };
7760
7722
  }
7761
- if (existing.state === "closed") {
7723
+ if (existing.state === import_sdk26.RelationshipStates.CLOSED) {
7762
7724
  return {
7763
7725
  kind: "error",
7764
7726
  message: `send-handshake-response: relationship ${existing.relationshipId} is CLOSED. Cannot respond to handshake on a terminated relationship \u2014 start a fresh handshake instead.`
@@ -7883,8 +7845,7 @@ function registerWhoamiCommand(root) {
7883
7845
  // `--payer-settlement-pubkey` / on-chain ops without a
7884
7846
  // server round-trip. `--local` short-circuits the signed
7885
7847
  // GET, prints just the resolved DID + pubkeys from local
7886
- // state. (V1-alpha: accountId parked, no longer in --local
7887
- // output.)
7848
+ // state.
7888
7849
  "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.",
7889
7850
  false
7890
7851
  ).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) => {
@@ -7897,10 +7858,6 @@ function registerWhoamiCommand(root) {
7897
7858
  did: local.did,
7898
7859
  settlementPublicKeyB58: local.settlementPublicKeyB58,
7899
7860
  identityPublicKeyB58: local.identityPublicKeyB58,
7900
- // V1-alpha: accountId parked — local-state type no
7901
- // longer carries the field. Uncomment when launchpad↔ARP
7902
- // join lands.
7903
- // accountId: local.accountId,
7904
7861
  name: local.name
7905
7862
  };
7906
7863
  if (opts.local) {
@@ -7953,7 +7910,7 @@ function registerWhoamiCommand(root) {
7953
7910
  }
7954
7911
 
7955
7912
  // src/commands/work.ts
7956
- var import_sdk20 = require("@heyanon-arp/sdk");
7913
+ var import_sdk27 = require("@heyanon-arp/sdk");
7957
7914
  var import_chalk32 = __toESM(require("chalk"));
7958
7915
  init_api();
7959
7916
  function registerWorkCommands(root) {
@@ -7961,25 +7918,6 @@ function registerWorkCommands(root) {
7961
7918
  registerRequest(cmd);
7962
7919
  registerRespond(cmd);
7963
7920
  }
7964
- var POST_COMMIT_ERROR_CODES3 = /* @__PURE__ */ new Set([
7965
- "WORK_DELEGATION_NOT_FOUND",
7966
- "WORK_DELEGATION_NOT_ACTIVE",
7967
- "WORK_RELATIONSHIP_MISMATCH",
7968
- "WORK_REQUESTER_NOT_OFFERER",
7969
- "WORK_REQUEST_ALREADY_EXISTS",
7970
- "WORK_REQUEST_NOT_FOUND",
7971
- "WORK_RESPONDER_IS_CALLER",
7972
- "WORK_INVALID_STATE",
7973
- // M6 lock-at-accept: the work handler's LOCKED gate emits
7974
- // `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
7975
- // the on-chain lock isn't confirmed yet (state
7976
- // `pending_lock_finalization`). It fires from the body handler AFTER
7977
- // the event row is committed — same lifecycle as
7978
- // `WORK_DELEGATION_NOT_ACTIVE` — so the CLI must advance
7979
- // `lastSenderSequence`, otherwise a retry once the lock confirms
7980
- // reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
7981
- "DELEGATION_PENDING_LOCK"
7982
- ]);
7983
7921
  function registerRequest(parent) {
7984
7922
  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(
7985
7923
  "--params <json>",
@@ -7987,11 +7925,20 @@ function registerRequest(parent) {
7987
7925
  ).option(
7988
7926
  "--params-file <path>",
7989
7927
  "Read the JSON payload from a file. Mutually exclusive with --params. Use this for any payload large or quote-heavy enough that `--params '{...}'` would fight your shell."
7990
- ).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).action(async (recipientDid, delegationId, opts) => {
7928
+ ).option("--ttl <seconds>", `Envelope TTL in seconds (max ${import_sdk27.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
7929
+ "--json",
7930
+ 'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"work_request", requestId, delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + reply hints move to stderr; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
7931
+ false
7932
+ ).action(async (recipientDid, delegationId, opts) => {
7991
7933
  await runRequest(recipientDid, delegationId, opts);
7992
7934
  });
7993
7935
  }
7994
7936
  async function runRequest(recipientDid, delegationId, opts) {
7937
+ if (opts.verbose && opts.json) {
7938
+ throw new Error(
7939
+ "work request: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
7940
+ );
7941
+ }
7995
7942
  requireDid3("work request", recipientDid, "<recipient-did>");
7996
7943
  delegationId = requireUuidNormalised3("work request", delegationId, "<delegation-id>");
7997
7944
  const ttlSeconds = parseTtl5("work request", opts.ttl);
@@ -8005,27 +7952,51 @@ async function runRequest(recipientDid, delegationId, opts) {
8005
7952
  params
8006
7953
  };
8007
7954
  const body = { type: "work_request", content };
8008
- console.log(import_chalk32.default.dim(`Server: ${api.serverUrl}`));
8009
- console.log(import_chalk32.default.dim(`Sender: ${sender.did}`));
8010
- console.log(import_chalk32.default.dim(`Recipient: ${recipientDid}`));
8011
- console.log(import_chalk32.default.dim(`Delegation: ${delegationId}`));
8012
- console.log(import_chalk32.default.dim(`Request id: ${requestId}`));
7955
+ progress(opts.json, import_chalk32.default.dim(`Server: ${api.serverUrl}`));
7956
+ progress(opts.json, import_chalk32.default.dim(`Sender: ${sender.did}`));
7957
+ progress(opts.json, import_chalk32.default.dim(`Recipient: ${recipientDid}`));
7958
+ progress(opts.json, import_chalk32.default.dim(`Delegation: ${delegationId}`));
7959
+ progress(opts.json, import_chalk32.default.dim(`Request id: ${requestId}`));
8013
7960
  const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
8014
- printIngestResult3(result);
8015
- console.log(import_chalk32.default.dim(`
7961
+ if (opts.json) {
7962
+ jsonOut({
7963
+ ok: true,
7964
+ action: "work_request",
7965
+ requestId,
7966
+ delegationId,
7967
+ eventId: result.eventId,
7968
+ relationshipId: result.relationshipId,
7969
+ relationshipEventIndex: result.relationshipEventIndex,
7970
+ serverTimestamp: result.serverTimestamp,
7971
+ serverEventHash: result.serverEventHash,
7972
+ prevServerEventHash: result.prevServerEventHash ?? null
7973
+ });
7974
+ } else {
7975
+ printIngestResult3(result);
7976
+ console.log(import_chalk32.default.dim(`
8016
7977
  The payee can reply with:`));
8017
- console.log(import_chalk32.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --output '<json>'`));
8018
- console.log(import_chalk32.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --error CODE:message`));
7978
+ console.log(import_chalk32.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --output '<json>'`));
7979
+ console.log(import_chalk32.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --error CODE:message`));
7980
+ }
8019
7981
  }
8020
7982
  function registerRespond(parent) {
8021
7983
  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(
8022
7984
  "--output-file <path>",
8023
7985
  "Read the success payload from a file. Mutually exclusive with --output and --error. Use this for any payload large or quote-heavy enough to fight your shell."
8024
- ).option("--error <code:message>", "Failure payload as `CODE:message`. Mutually exclusive with --output / --output-file.").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).action(async (relationshipId, delegationId, requestId, opts) => {
7986
+ ).option("--error <code:message>", "Failure payload as `CODE:message`. Mutually exclusive with --output / --output-file.").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(
7987
+ "--json",
7988
+ 'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"work_response", requestId, delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude moves to stderr; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
7989
+ false
7990
+ ).action(async (relationshipId, delegationId, requestId, opts) => {
8025
7991
  await runRespond(relationshipId, delegationId, requestId, opts);
8026
7992
  });
8027
7993
  }
8028
7994
  async function runRespond(relationshipId, delegationId, requestId, opts) {
7995
+ if (opts.verbose && opts.json) {
7996
+ throw new Error(
7997
+ "work respond: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
7998
+ );
7999
+ }
8029
8000
  relationshipId = requireUuidNormalised3("work respond", relationshipId, "<relationship-id>");
8030
8001
  delegationId = requireUuidNormalised3("work respond", delegationId, "<delegation-id>");
8031
8002
  const ttlSeconds = parseTtl5("work respond", opts.ttl);
@@ -8040,33 +8011,48 @@ async function runRespond(relationshipId, delegationId, requestId, opts) {
8040
8011
  ...responsePayload
8041
8012
  };
8042
8013
  const body = { type: "work_response", content };
8043
- console.log(import_chalk32.default.dim(`Server: ${api.serverUrl}`));
8044
- console.log(import_chalk32.default.dim(`Sender: ${sender.did}`));
8045
- console.log(import_chalk32.default.dim(`Recipient: ${recipientDid}`));
8046
- console.log(import_chalk32.default.dim(`Relationship: ${relationshipId}`));
8047
- console.log(import_chalk32.default.dim(`Delegation: ${delegationId}`));
8048
- console.log(import_chalk32.default.dim(`Request id: ${requestId}`));
8049
- console.log(import_chalk32.default.dim(`Outcome: ${responsePayload.output ? "success" : "error"}`));
8014
+ progress(opts.json, import_chalk32.default.dim(`Server: ${api.serverUrl}`));
8015
+ progress(opts.json, import_chalk32.default.dim(`Sender: ${sender.did}`));
8016
+ progress(opts.json, import_chalk32.default.dim(`Recipient: ${recipientDid}`));
8017
+ progress(opts.json, import_chalk32.default.dim(`Relationship: ${relationshipId}`));
8018
+ progress(opts.json, import_chalk32.default.dim(`Delegation: ${delegationId}`));
8019
+ progress(opts.json, import_chalk32.default.dim(`Request id: ${requestId}`));
8020
+ progress(opts.json, import_chalk32.default.dim(`Outcome: ${responsePayload.output ? "success" : "error"}`));
8050
8021
  const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
8051
- printIngestResult3(result);
8022
+ if (opts.json) {
8023
+ jsonOut({
8024
+ ok: true,
8025
+ action: "work_response",
8026
+ requestId,
8027
+ delegationId,
8028
+ eventId: result.eventId,
8029
+ relationshipId: result.relationshipId,
8030
+ relationshipEventIndex: result.relationshipEventIndex,
8031
+ serverTimestamp: result.serverTimestamp,
8032
+ serverEventHash: result.serverEventHash,
8033
+ prevServerEventHash: result.prevServerEventHash ?? null
8034
+ });
8035
+ } else {
8036
+ printIngestResult3(result);
8037
+ }
8052
8038
  }
8053
8039
  async function sendWorkEnvelope(args) {
8054
8040
  const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
8055
8041
  const protectedBlock = {
8056
- protocol_version: "arp/0.1",
8057
- purpose: import_sdk20.Purpose.ENVELOPE,
8058
- message_id: (0, import_sdk20.uuidV4)(),
8042
+ protocol_version: import_sdk27.CURRENT_PROTOCOL_VERSION,
8043
+ purpose: import_sdk27.Purpose.ENVELOPE,
8044
+ message_id: (0, import_sdk27.uuidV4)(),
8059
8045
  sender_did: args.sender.did,
8060
8046
  recipient_did: args.recipientDid,
8061
8047
  relationship_id: null,
8062
8048
  sender_sequence: nextSequence,
8063
- sender_nonce: (0, import_sdk20.senderNonce)(),
8064
- timestamp: (0, import_sdk20.rfc3339)(),
8065
- expires_at: (0, import_sdk20.expiresAt)(args.ttlSeconds),
8049
+ sender_nonce: (0, import_sdk27.senderNonce)(),
8050
+ timestamp: (0, import_sdk27.rfc3339)(),
8051
+ expires_at: (0, import_sdk27.expiresAt)(args.ttlSeconds),
8066
8052
  delivery_id: null
8067
8053
  };
8068
8054
  const signer = makeSigner(args.sender);
8069
- const envelope = (0, import_sdk20.signEnvelope)({
8055
+ const envelope = (0, import_sdk27.signEnvelope)({
8070
8056
  protected: protectedBlock,
8071
8057
  body: args.body,
8072
8058
  identitySecretKey: signer.identitySecretKey
@@ -8080,7 +8066,7 @@ async function sendWorkEnvelope(args) {
8080
8066
  updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
8081
8067
  return result;
8082
8068
  } catch (err) {
8083
- if (err instanceof ApiError && POST_COMMIT_ERROR_CODES3.has(err.payload.code)) {
8069
+ if (err instanceof ApiError && (0, import_sdk27.isPostCommitErrorCode)(err.payload.code)) {
8084
8070
  updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
8085
8071
  }
8086
8072
  throw err;
@@ -8092,7 +8078,7 @@ async function resolveResponseRecipient(cmdName, api, signer, args) {
8092
8078
  const page = await api.listWorkLogs(args.relationshipId, signer, { delegationId: args.delegationId, limit: 100, after });
8093
8079
  const row = page.find((w) => w.requestId === args.requestId);
8094
8080
  if (row) {
8095
- if (row.state !== "requested") {
8081
+ if (row.state !== import_sdk27.WorkLogStates.REQUESTED) {
8096
8082
  throw new Error(
8097
8083
  `${cmdName}: work_request ${args.requestId} for delegation ${args.delegationId} is already in state '${row.state}' \u2014 only requests in state 'requested' can be responded to.`
8098
8084
  );
@@ -8209,7 +8195,7 @@ function parseTtl5(cmdName, raw) {
8209
8195
  }
8210
8196
  function parseRequestId(cmdName, raw) {
8211
8197
  if (raw === void 0 || raw === "") {
8212
- return (0, import_sdk20.uuidV4)();
8198
+ return (0, import_sdk27.uuidV4)();
8213
8199
  }
8214
8200
  if (raw.length === 0) {
8215
8201
  throw new Error(`${cmdName}: --request-id must be a non-empty string`);
@@ -8230,9 +8216,10 @@ function requireDid3(cmdName, did, label) {
8230
8216
  }
8231
8217
 
8232
8218
  // src/commands/work-list.ts
8219
+ var import_sdk28 = require("@heyanon-arp/sdk");
8233
8220
  var import_chalk33 = __toESM(require("chalk"));
8234
8221
  init_api();
8235
- var ALLOWED_STATES4 = /* @__PURE__ */ new Set(["requested", "responded"]);
8222
+ var ALLOWED_STATES3 = new Set(import_sdk28.WORK_LOG_STATES);
8236
8223
  function registerWorkListCommand(root) {
8237
8224
  root.command("work-list").description("List work-log rows for a relationship (one row per (delegationId, requestId), oldest-first)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by exact state (requested|responded)").option("--delegation-id <uuid>", "Narrow to work-logs operating under a specific delegation id").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(
8238
8225
  "--verbose",
@@ -8253,7 +8240,7 @@ async function runWorkList(relationshipId, opts) {
8253
8240
  );
8254
8241
  }
8255
8242
  const limit = parseLimit7(opts.limit);
8256
- const state = parseState4(opts.state);
8243
+ const state = parseState3(opts.state);
8257
8244
  const api = new ArpApiClient(opts.server);
8258
8245
  const sender = resolveSenderAgent("work-list", opts.server, opts.fromDid);
8259
8246
  if (!opts.json) {
@@ -8296,26 +8283,26 @@ function formatWorkLogLine(w, selfDid, opts = {}) {
8296
8283
  const delegationPart = opts.fullIds ? w.delegationId : idHead3(w.delegationId);
8297
8284
  const requestPart = opts.fullIds ? w.requestId : truncate3(w.requestId, 16);
8298
8285
  const id = import_chalk33.default.bold(`${delegationPart}/${requestPart}`);
8299
- const state = colorState3(w.state).padEnd(stateColumnWidth3());
8286
+ const state = colorState2(w.state).padEnd(stateColumnWidth2());
8300
8287
  const peerCallerHead = opts.fullIds ? w.callerDid : didHead5(w.callerDid);
8301
8288
  const peerPayeeHead = opts.fullIds ? w.payeeDid : didHead5(w.payeeDid);
8302
8289
  const direction = w.callerDid === selfDid ? `${import_chalk33.default.bold("me")} \u2192 ${import_chalk33.default.dim(peerPayeeHead)}` : `${import_chalk33.default.dim(peerCallerHead)} \u2192 ${import_chalk33.default.bold("me")}`;
8303
8290
  const outcome = formatOutcome(w);
8304
8291
  return `${id} ${state} ${direction} ${outcome}`;
8305
8292
  }
8306
- function colorState3(s) {
8293
+ function colorState2(s) {
8307
8294
  switch (s) {
8308
- case "requested":
8295
+ case import_sdk28.WorkLogStates.REQUESTED:
8309
8296
  return import_chalk33.default.yellow("requested");
8310
- case "responded":
8297
+ case import_sdk28.WorkLogStates.RESPONDED:
8311
8298
  return import_chalk33.default.green("responded");
8312
8299
  }
8313
8300
  }
8314
- function stateColumnWidth3() {
8301
+ function stateColumnWidth2() {
8315
8302
  return 9;
8316
8303
  }
8317
8304
  function formatOutcome(w) {
8318
- if (w.state === "requested") return import_chalk33.default.dim("(in flight)");
8305
+ if (w.state === import_sdk28.WorkLogStates.REQUESTED) return import_chalk33.default.dim("(in flight)");
8319
8306
  if (w.responseError) return import_chalk33.default.red(`error ${w.responseError.code}: ${truncate3(w.responseError.message, 32)}`);
8320
8307
  if (w.responseOutput) return import_chalk33.default.cyan("ok");
8321
8308
  return import_chalk33.default.dim("(empty response)");
@@ -8332,9 +8319,9 @@ function truncate3(s, max) {
8332
8319
  if (s.length <= max) return s;
8333
8320
  return `${s.slice(0, max - 3)}...`;
8334
8321
  }
8335
- function parseState4(raw) {
8322
+ function parseState3(raw) {
8336
8323
  if (raw === void 0) return void 0;
8337
- if (!ALLOWED_STATES4.has(raw)) {
8324
+ if (!ALLOWED_STATES3.has(raw)) {
8338
8325
  throw new Error(`work-list: --state must be one of requested|responded (got '${raw}')`);
8339
8326
  }
8340
8327
  return raw;
@@ -8348,6 +8335,43 @@ function parseLimit7(raw) {
8348
8335
  return n;
8349
8336
  }
8350
8337
 
8338
+ // src/option-hint.ts
8339
+ function unknownOptionHint(program, err) {
8340
+ try {
8341
+ if (err.code !== "commander.unknownOption" || !err.message) return null;
8342
+ const m = /unknown option '([^']+)'/.exec(err.message);
8343
+ if (!m) return null;
8344
+ const raw = m[1];
8345
+ const flag = raw.split("=")[0];
8346
+ const owners = [];
8347
+ const walk = (cmd, path) => {
8348
+ for (const opt of cmd.options) {
8349
+ if (opt.long === flag || opt.short === flag) owners.push(path.join(" "));
8350
+ }
8351
+ for (const sub of cmd.commands) walk(sub, [...path, sub.name()]);
8352
+ };
8353
+ for (const sub of program.commands) walk(sub, [sub.name()]);
8354
+ if (owners.length === 0) return null;
8355
+ owners.sort((a, b) => a.split(" ").length - b.split(" ").length);
8356
+ const argv = process.argv.slice(2);
8357
+ const commandNames = /* @__PURE__ */ new Set();
8358
+ for (const c of program.commands) {
8359
+ commandNames.add(c.name());
8360
+ for (const a of c.aliases()) commandNames.add(a);
8361
+ }
8362
+ const flagIdx = argv.findIndex((t) => t === raw || t.split("=")[0] === flag);
8363
+ const cmdIdx = argv.findIndex((t) => commandNames.has(t));
8364
+ const placedBeforeCommand = cmdIdx === -1 || flagIdx !== -1 && flagIdx < cmdIdx;
8365
+ if (placedBeforeCommand) {
8366
+ return `'${flag}' is a command option, not a global one \u2014 put it AFTER the command (e.g. \`heyarp ${owners[0]} ${flag} \u2026\`), not before it.`;
8367
+ }
8368
+ const list = owners.slice(0, 4).join(", ") + (owners.length > 4 ? ", \u2026" : "");
8369
+ return `'${flag}' isn't valid for that command \u2014 it's an option of: ${list}.`;
8370
+ } catch {
8371
+ return null;
8372
+ }
8373
+ }
8374
+
8351
8375
  // src/cli.ts
8352
8376
  async function checkForUpdates() {
8353
8377
  if (package_default.private === true) return;
@@ -8362,7 +8386,7 @@ async function checkForUpdates() {
8362
8386
  async function main() {
8363
8387
  void checkForUpdates();
8364
8388
  const program = new import_commander.Command();
8365
- program.name("heyarp").description("ARP \u2014 Agent Relationship Protocol CLI (talks to apps/arp-server)").version(package_default.version).option("--trace", "Surface stack traces and error details on failure.", false);
8389
+ program.name("heyarp").description("ARP \u2014 Agent Relationship Protocol CLI (talks to apps/arp-server)").version(package_default.version).usage("[global-options] <command> [options]").option("--trace", "Surface stack traces and error details on failure.", false);
8366
8390
  program.exitOverride();
8367
8391
  program.configureOutput({
8368
8392
  writeErr: (str) => {
@@ -8370,6 +8394,7 @@ async function main() {
8370
8394
  process.stderr.write(str);
8371
8395
  }
8372
8396
  });
8397
+ program.addHelpText("after", () => optionPlacementHelpNote());
8373
8398
  program.addHelpText("after", () => onboardingHelpFooter());
8374
8399
  registerConfigCommand(program);
8375
8400
  registerGuideCommand(program);
@@ -8417,10 +8442,13 @@ async function main() {
8417
8442
  const json = process.argv.includes("--json");
8418
8443
  const verbose = process.argv.includes("--trace");
8419
8444
  const exitCode = typeof cerr.exitCode === "number" && cerr.exitCode !== 0 ? cerr.exitCode : 1;
8445
+ const hint = unknownOptionHint(program, cerr);
8420
8446
  if (isCommanderError && !json) {
8447
+ if (hint) process.stderr.write(`hint: ${hint}
8448
+ `);
8421
8449
  process.exit(exitCode);
8422
8450
  }
8423
- emitError(err, { json, verbose });
8451
+ emitError(err, { json, verbose, hint: hint ?? void 0 });
8424
8452
  process.exit(exitCode);
8425
8453
  }
8426
8454
  }