@heyanon-arp/cli 0.0.11 → 0.0.12

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
@@ -496,7 +496,7 @@ var init_api = __esm({
496
496
  * entity feed.
497
497
  */
498
498
  async listDelegations(relationshipId, signer, query, signal) {
499
- return this.signedRequest(
499
+ const rows = await this.signedRequest(
500
500
  "GET",
501
501
  `/v1/relationships/${encodeURIComponent(relationshipId)}/delegations`,
502
502
  null,
@@ -504,6 +504,7 @@ var init_api = __esm({
504
504
  query,
505
505
  signal
506
506
  );
507
+ return (0, import_shield.guardInboundFieldsBatch)(rows, ["scopeSummary", "title", "brief", "acceptanceCriteria"], { selfDid: signer.did });
507
508
  }
508
509
  /**
509
510
  * Signed `GET /v1/relationships/:id/work`. One row per
@@ -513,13 +514,14 @@ var init_api = __esm({
513
514
  * work-logs operating under a single delegation umbrella.
514
515
  */
515
516
  async listWorkLogs(relationshipId, signer, query) {
516
- return this.signedRequest(
517
+ const rows = await this.signedRequest(
517
518
  "GET",
518
519
  `/v1/relationships/${encodeURIComponent(relationshipId)}/work`,
519
520
  null,
520
521
  signer,
521
522
  query
522
523
  );
524
+ return (0, import_shield.guardInboundFieldsBatch)(rows, ["responseOutput", "requestParams"], { selfDid: signer.did });
523
525
  }
524
526
  /**
525
527
  * Signed `GET /v1/relationships/:id/receipts`. One row per
@@ -770,45 +772,6 @@ var init_format = __esm({
770
772
  }
771
773
  });
772
774
 
773
- // src/id-format.ts
774
- function describeNonUuidShape(raw) {
775
- if (raw === "") return "empty string";
776
- if (raw.startsWith("del_") && UUID_RE.test(raw.slice(4))) {
777
- return "looks like a delegation id with the canonical `del_` prefix. If this field expects a delegation_id, drop the `del_` prefix and pass the 36-char body. If this field expects a DIFFERENT id type (relationship_id / request_id), the `del_`-prefixed value belongs to the WRONG entity \u2014 look up the right row and copy its id";
778
- }
779
- if (raw.startsWith("evt_")) {
780
- return "looks like an event id (evt_<uuid>) \u2014 this command expects a delegation/relationship/request id (UUID), which is a DIFFERENT column. Look up the row that emitted this event (`heyarp events <rel> --json`) and copy the appropriate id field";
781
- }
782
- if (raw.startsWith("did:arp:")) {
783
- return "looks like a DID (agent identifier) \u2014 this command expects a delegation/relationship/request id (UUID), not a DID";
784
- }
785
- if (SHA256_PREFIX_RE.test(raw)) {
786
- return "looks like a sha256:<hash> \u2014 this command expects a UUID, not a hash. sha256: ids show up in receipt_event_hash, request_hash, response_hash";
787
- }
788
- if (OBJECT_ID_24_HEX_RE.test(raw)) {
789
- return "looks like a Mongo ObjectId (24-hex row id surfaced by some server read endpoints) rather than the expected UUID. Mongo ObjectIds and delegation ids are DIFFERENT formats";
790
- }
791
- if (UUID_NO_DASHES_RE.test(raw)) {
792
- return "looks like a UUID with no dashes \u2014 canonical UUIDs need the 8-4-4-4-12 dash pattern (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)";
793
- }
794
- return void 0;
795
- }
796
- function requireUuid(cmdName, raw, label) {
797
- if (UUID_RE.test(raw)) return;
798
- const hint = describeNonUuidShape(raw);
799
- const base = `${cmdName}: ${label} must be a UUID (got '${raw}')`;
800
- throw new Error(hint ? `${base} \u2014 ${hint}` : base);
801
- }
802
- var UUID_RE, OBJECT_ID_24_HEX_RE, SHA256_PREFIX_RE, UUID_NO_DASHES_RE;
803
- var init_id_format = __esm({
804
- "src/id-format.ts"() {
805
- 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}$/;
806
- OBJECT_ID_24_HEX_RE = /^[0-9a-f]{24}$/;
807
- SHA256_PREFIX_RE = /^sha256:[0-9a-f]{64}$/;
808
- UUID_NO_DASHES_RE = /^[a-fA-F0-9]{32}$/;
809
- }
810
- });
811
-
812
775
  // src/state.ts
813
776
  function stateFilePath() {
814
777
  return (0, import_node_path3.join)(arpHomeDir(), "agents.json");
@@ -947,7 +910,7 @@ var init_state = __esm({
947
910
 
948
911
  // src/commands/lifecycle.ts
949
912
  function registerLifecycleCommands(root) {
950
- root.command("update").description("Update an agent profile (name / description / tags). At least one flag is required.").argument("<did>").option("--server <url>", "Override ARP server base URL").option("--name <s>", "New display name").option("--description <s>", "New description").option("--tag <s>", "Capability tag \u2014 REPLACES the existing list. Repeatable: --tag translation --tag fr", accumulate2, []).option("--clear-tags", "Drop all tags (cannot be combined with --tag)", false).action(
913
+ 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(
951
914
  async (did, opts) => {
952
915
  const body = buildUpdateBody(opts);
953
916
  if (Object.keys(body).length === 0) {
@@ -963,7 +926,7 @@ function registerLifecycleCommands(root) {
963
926
  }
964
927
  );
965
928
  }
966
- function accumulate2(value, previous) {
929
+ function accumulate(value, previous) {
967
930
  return [...previous, value];
968
931
  }
969
932
  function buildUpdateBody(opts) {
@@ -983,8 +946,8 @@ function buildUpdateBody(opts) {
983
946
  async function actSigned(did, serverOverride, act) {
984
947
  const local = loadAgentOrThrow(serverOverride, did);
985
948
  const api = new ArpApiClient(serverOverride);
986
- console.log(import_chalk4.default.dim(`Server: ${api.serverUrl}`));
987
- console.log(import_chalk4.default.dim(`Signer: ${local.did}`));
949
+ console.log(import_chalk2.default.dim(`Server: ${api.serverUrl}`));
950
+ console.log(import_chalk2.default.dim(`Signer: ${local.did}`));
988
951
  const signer = makeSigner(local);
989
952
  return act(api, signer);
990
953
  }
@@ -995,22 +958,61 @@ function makeSigner(s) {
995
958
  };
996
959
  }
997
960
  function printAgent(verb, agent) {
998
- console.log(import_chalk4.default.green(`
961
+ console.log(import_chalk2.default.green(`
999
962
  ${verb}.`));
1000
- console.log(`${import_chalk4.default.bold("DID")}: ${import_chalk4.default.cyan(agent.did)}`);
1001
- console.log(import_chalk4.default.bold("\nAgent profile:"));
963
+ console.log(`${import_chalk2.default.bold("DID")}: ${import_chalk2.default.cyan(agent.did)}`);
964
+ console.log(import_chalk2.default.bold("\nAgent profile:"));
1002
965
  console.log(formatJson(agent));
1003
966
  }
1004
- var import_chalk4;
967
+ var import_chalk2;
1005
968
  var init_lifecycle = __esm({
1006
969
  "src/commands/lifecycle.ts"() {
1007
- import_chalk4 = __toESM(require("chalk"));
970
+ import_chalk2 = __toESM(require("chalk"));
1008
971
  init_api();
1009
972
  init_format();
1010
973
  init_state();
1011
974
  }
1012
975
  });
1013
976
 
977
+ // src/id-format.ts
978
+ function describeNonUuidShape(raw) {
979
+ if (raw === "") return "empty string";
980
+ if (raw.startsWith("del_") && UUID_RE.test(raw.slice(4))) {
981
+ return "looks like a delegation id with the canonical `del_` prefix. If this field expects a delegation_id, drop the `del_` prefix and pass the 36-char body. If this field expects a DIFFERENT id type (relationship_id / request_id), the `del_`-prefixed value belongs to the WRONG entity \u2014 look up the right row and copy its id";
982
+ }
983
+ if (raw.startsWith("evt_")) {
984
+ return "looks like an event id (evt_<uuid>) \u2014 this command expects a delegation/relationship/request id (UUID), which is a DIFFERENT column. Look up the row that emitted this event (`heyarp events <rel> --json`) and copy the appropriate id field";
985
+ }
986
+ if (raw.startsWith("did:arp:")) {
987
+ return "looks like a DID (agent identifier) \u2014 this command expects a delegation/relationship/request id (UUID), not a DID";
988
+ }
989
+ if (SHA256_PREFIX_RE.test(raw)) {
990
+ return "looks like a sha256:<hash> \u2014 this command expects a UUID, not a hash. sha256: ids show up in receipt_event_hash, request_hash, response_hash";
991
+ }
992
+ if (OBJECT_ID_24_HEX_RE.test(raw)) {
993
+ return "looks like a Mongo ObjectId (24-hex row id surfaced by some server read endpoints) rather than the expected UUID. Mongo ObjectIds and delegation ids are DIFFERENT formats";
994
+ }
995
+ if (UUID_NO_DASHES_RE.test(raw)) {
996
+ return "looks like a UUID with no dashes \u2014 canonical UUIDs need the 8-4-4-4-12 dash pattern (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)";
997
+ }
998
+ return void 0;
999
+ }
1000
+ function requireUuid(cmdName, raw, label) {
1001
+ if (UUID_RE.test(raw)) return;
1002
+ const hint = describeNonUuidShape(raw);
1003
+ const base = `${cmdName}: ${label} must be a UUID (got '${raw}')`;
1004
+ throw new Error(hint ? `${base} \u2014 ${hint}` : base);
1005
+ }
1006
+ var UUID_RE, OBJECT_ID_24_HEX_RE, SHA256_PREFIX_RE, UUID_NO_DASHES_RE;
1007
+ var init_id_format = __esm({
1008
+ "src/id-format.ts"() {
1009
+ 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}$/;
1010
+ OBJECT_ID_24_HEX_RE = /^[0-9a-f]{24}$/;
1011
+ SHA256_PREFIX_RE = /^sha256:[0-9a-f]{64}$/;
1012
+ UUID_NO_DASHES_RE = /^[a-fA-F0-9]{32}$/;
1013
+ }
1014
+ });
1015
+
1014
1016
  // src/commands/status.ts
1015
1017
  function registerStatusCommand(root) {
1016
1018
  root.command("status").description("Where am I in the work cycle? FSM state + next-action hint for ONE relationship (signed reads)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Signer DID \u2014 required only if multiple agents are registered against this server").option("--json", "Machine-readable: single JSON object with the composed summary. Pipe-safe.", false).option(
@@ -2576,7 +2578,29 @@ async function runOffer(recipientDid, opts) {
2576
2578
  console.log(import_chalk6.default.dim(`Sender: ${sender.did}`));
2577
2579
  console.log(import_chalk6.default.dim(`Recipient: ${recipientDid}`));
2578
2580
  console.log(import_chalk6.default.dim(`Delegation: ${delegationId}`));
2579
- const result = await sendDelegationEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
2581
+ let result;
2582
+ try {
2583
+ result = await sendDelegationEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
2584
+ } catch (err) {
2585
+ if (err instanceof ApiError && err.payload.code === "DELEGATION_PRICING_MISMATCH") {
2586
+ const d = err.payload.details;
2587
+ console.error(import_chalk6.default.yellow(`
2588
+ The recipient's published accept-prefs rejected this offer${d?.reason ? ` (mismatch: ${d.reason})` : ""}.`));
2589
+ if (d !== void 0) {
2590
+ console.error(import_chalk6.default.dim(` accepted: ${JSON.stringify(d.accepted)}`));
2591
+ console.error(import_chalk6.default.dim(` offered: ${JSON.stringify(d.offered)}`));
2592
+ }
2593
+ console.error(
2594
+ import_chalk6.default.dim(
2595
+ `
2596
+ Check what the recipient accepts BEFORE offering:
2597
+ heyarp agents accept-prefs show ${recipientDid}
2598
+ then re-run with matching --pricing-model / --currency / --amount.`
2599
+ )
2600
+ );
2601
+ }
2602
+ throw err;
2603
+ }
2580
2604
  printIngestResult(result);
2581
2605
  console.log(import_chalk6.default.dim(`
2582
2606
  Reference this delegation on subsequent calls with:`));
@@ -3194,6 +3218,12 @@ var init_delegation = __esm({
3194
3218
  "DELEGATION_ACCEPTER_IS_OFFERER",
3195
3219
  "DELEGATION_DECLINER_IS_OFFERER",
3196
3220
  "DELEGATION_CANCELER_NOT_OFFERER",
3221
+ // PricingPolicy: the offer's terms fell outside the RECIPIENT's
3222
+ // published accept-prefs. Fires in `handleOffer` AFTER the event
3223
+ // row commit (pre-materialization on the server), so the sequence
3224
+ // was consumed — advance it or the corrected re-offer trips
3225
+ // ENV_SEQUENCE_BACKWARDS.
3226
+ "DELEGATION_PRICING_MISMATCH",
3197
3227
  // `DELEGATION_PENDING_LOCK` fires from the body handler's
3198
3228
  // `requireDelegationInState` AFTER the event row is persisted
3199
3229
  // (same code path as DELEGATION_INVALID_STATE), so an accept
@@ -3685,9 +3715,10 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3685
3715
  if (!delegation) {
3686
3716
  throw new Error(`${cmdName}: delegation ${delegationId} not found under relationship ${relId} (paginated 5000 rows).`);
3687
3717
  }
3688
- if (delegation.state !== "accepted") {
3718
+ const SETTLEABLE_STATES = /* @__PURE__ */ new Set(["accepted", "locked"]);
3719
+ if (!SETTLEABLE_STATES.has(delegation.state ?? "")) {
3689
3720
  throw new Error(
3690
- `${cmdName}: delegation ${delegationId} is in state '${delegation.state ?? "unknown"}' \u2014 only an 'accepted' delegation is settleable (its escrow lock is still LOCKED). A non-accepted delegation's lock has been released / refunded / canceled, so the server would reject the settlement signature post-commit (SETTLEMENT_SIG_LOCK_INVALID_STATE). Nothing to settle.`
3721
+ `${cmdName}: delegation ${delegationId} is in state '${delegation.state ?? "unknown"}' \u2014 only an 'accepted' or 'locked' delegation is settleable (its escrow lock is still LOCKED). A non-settleable state's lock has been released / refunded / canceled, so the server would reject the settlement signature post-commit (SETTLEMENT_SIG_LOCK_INVALID_STATE). Nothing to settle.`
3691
3722
  );
3692
3723
  }
3693
3724
  if (!delegation.amount) {
@@ -5056,7 +5087,7 @@ var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
5056
5087
  // package.json
5057
5088
  var package_default = {
5058
5089
  name: "@heyanon-arp/cli",
5059
- version: "0.0.11",
5090
+ version: "0.0.12",
5060
5091
  description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
5061
5092
  license: "MIT",
5062
5093
  keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
@@ -5101,11 +5132,13 @@ var package_default = {
5101
5132
  };
5102
5133
 
5103
5134
  // src/commands/agents.ts
5104
- var import_chalk2 = __toESM(require("chalk"));
5135
+ var import_chalk3 = __toESM(require("chalk"));
5105
5136
  init_api();
5106
5137
  init_format();
5138
+ init_state();
5139
+ init_lifecycle();
5107
5140
  function registerAgentsCommand(root) {
5108
- root.command("agents").description("Discover published agents on the server (public catalog) \u2014 filter by tag / free-text").option("--server <url>", "Override ARP server base URL").option("--tag <s>", "Filter by capability tag \u2014 repeatable; AND-semantics across tags", accumulate, []).option("--query <s>", "Full-text search over name + description").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(
5141
+ const agents = root.command("agents").description("Discover published agents on the server (public catalog) \u2014 filter by tag / free-text").option("--server <url>", "Override ARP server base URL").option("--tag <s>", "Filter by capability tag \u2014 repeatable; AND-semantics across tags", accumulate2, []).option("--query <s>", "Full-text search over name + description").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(
5109
5142
  "--verbose",
5110
5143
  'After the one-line summaries, print a framed "Full agent payloads (N rows)" block containing the JSON array of all rows (with owner-string sanitisation for terminal safety)',
5111
5144
  false
@@ -5127,11 +5160,12 @@ function registerAgentsCommand(root) {
5127
5160
  ).action(async (opts) => {
5128
5161
  await runAgents(opts);
5129
5162
  });
5163
+ registerAcceptPrefsCommands(agents);
5130
5164
  }
5131
5165
  async function runAgents(opts) {
5132
5166
  const limit = parseLimit(opts.limit);
5133
5167
  const api = new ArpApiClient(opts.server);
5134
- progress(opts.json, import_chalk2.default.dim(`Server: ${api.serverUrl}`));
5168
+ progress(opts.json, import_chalk3.default.dim(`Server: ${api.serverUrl}`));
5135
5169
  const query = { limit };
5136
5170
  if (opts.tag && opts.tag.length > 0) query.tag = opts.tag.map((t) => t.trim().toLowerCase());
5137
5171
  if (opts.query) query.q = opts.query;
@@ -5147,7 +5181,7 @@ async function runAgents(opts) {
5147
5181
  return;
5148
5182
  }
5149
5183
  if (rows.length === 0) {
5150
- console.log(import_chalk2.default.dim("\n(no agents matched)"));
5184
+ console.log(import_chalk3.default.dim("\n(no agents matched)"));
5151
5185
  return;
5152
5186
  }
5153
5187
  console.log("");
@@ -5158,10 +5192,10 @@ async function runAgents(opts) {
5158
5192
  const useUnicode = supportsUnicodeFrame();
5159
5193
  const heavyRule = useUnicode ? "\u2501".repeat(60) : "=".repeat(60);
5160
5194
  const headerGlyph = useUnicode ? "\u25BC" : ">";
5161
- const rule = import_chalk2.default.cyan(heavyRule);
5195
+ const rule = import_chalk3.default.cyan(heavyRule);
5162
5196
  console.log(`
5163
5197
  ${rule}`);
5164
- console.log(import_chalk2.default.bold.cyan(`${headerGlyph} Full agent payloads (${safeRows.length} row${safeRows.length === 1 ? "" : "s"})`));
5198
+ console.log(import_chalk3.default.bold.cyan(`${headerGlyph} Full agent payloads (${safeRows.length} row${safeRows.length === 1 ? "" : "s"})`));
5165
5199
  console.log(rule);
5166
5200
  console.log(formatJson(safeRows));
5167
5201
  console.log(`${rule}
@@ -5169,16 +5203,16 @@ ${rule}`);
5169
5203
  }
5170
5204
  if (rows.length === limit) {
5171
5205
  const cursor = rows[rows.length - 1].id;
5172
- console.log(import_chalk2.default.dim(`
5206
+ console.log(import_chalk3.default.dim(`
5173
5207
  Next page: re-run with --after ${cursor}`));
5174
5208
  }
5175
5209
  }
5176
5210
  function formatAgentLine(a, opts = {}) {
5177
5211
  const idDisplay = opts.fullIds ? a.id : `${a.id.slice(0, 8)}...${a.id.slice(-4)}`;
5178
5212
  const tags = `[${a.tags.join(",")}]`;
5179
- const name = a.name ? `"${truncate(sanitizeForTerminal(a.name), 40)}"` : import_chalk2.default.dim("(unnamed)");
5180
- const description = a.description ? `\u2014 ${import_chalk2.default.dim(truncate(sanitizeForTerminal(a.description), 60))}` : "";
5181
- return `${import_chalk2.default.dim(idDisplay)} ${import_chalk2.default.cyan(a.did)} ${import_chalk2.default.magenta(tags)} ${name} ${description}`.trim();
5213
+ const name = a.name ? `"${truncate(sanitizeForTerminal(a.name), 40)}"` : import_chalk3.default.dim("(unnamed)");
5214
+ const description = a.description ? `\u2014 ${import_chalk3.default.dim(truncate(sanitizeForTerminal(a.description), 60))}` : "";
5215
+ return `${import_chalk3.default.dim(idDisplay)} ${import_chalk3.default.cyan(a.did)} ${import_chalk3.default.magenta(tags)} ${name} ${description}`.trim();
5182
5216
  }
5183
5217
  function truncate(s, max) {
5184
5218
  if (s.length <= max) return s;
@@ -5195,12 +5229,129 @@ function parseLimit(raw) {
5195
5229
  }
5196
5230
  return n;
5197
5231
  }
5198
- function accumulate(value, previous) {
5232
+ function accumulate2(value, previous) {
5199
5233
  return [...previous, value];
5200
5234
  }
5235
+ var PRICING_MODELS = ["flat", "usage_based"];
5236
+ function registerAcceptPrefsCommands(agents) {
5237
+ const prefs = agents.command("accept-prefs").description("Worker-side accept-preferences (PricingPolicy): what delegation offers the server should reject on your behalf");
5238
+ prefs.command("set").description("Publish accept-prefs for an agent you own \u2014 REPLACES the whole object on every call").argument("<did>", "Agent DID (must have a local key \u2014 owner-only)").option("--server <url>", "Override ARP server base URL").option("--pricing-model <m>", "Accepted pricing model: flat | usage_based. Repeatable; omit to accept any model.", accumulate2, []).option(
5239
+ "--currency <spec>",
5240
+ `Accepted currency: "<caip19-asset-id>[,<min>[,<max>]]" \u2014 optional inclusive amount bounds as decimal strings in that currency's units (empty segment skips: "asset,,500" = max only). Repeatable; omit to accept any currency.`,
5241
+ accumulate2,
5242
+ []
5243
+ ).option(
5244
+ "--from-json <json>",
5245
+ 'Full AcceptPrefs JSON object (mutually exclusive with --pricing-model / --currency). Shape: {"pricingModels":[...],"currencies":[{"assetId":"...","minAmount":"...","maxAmount":"..."}]}'
5246
+ ).action(async (did, _opts, cmd) => {
5247
+ const opts = cmd.optsWithGlobals();
5248
+ const body = buildAcceptPrefs(opts);
5249
+ const local = loadAgentOrThrow(opts.server, did);
5250
+ const api = new ArpApiClient(opts.server);
5251
+ console.log(import_chalk3.default.dim(`Server: ${api.serverUrl}`));
5252
+ console.log(import_chalk3.default.dim(`Signer: ${local.did}`));
5253
+ const agent = await api.updateAgent(did, { acceptPrefs: body }, makeSigner(local));
5254
+ console.log(import_chalk3.default.green("\nAccept-prefs published."));
5255
+ printAcceptPrefs(agent.acceptPrefs);
5256
+ console.log(import_chalk3.default.dim("\nIncompatible delegation offers are now rejected server-side with DELEGATION_PRICING_MISMATCH."));
5257
+ });
5258
+ prefs.command("show").description(
5259
+ "Read an agent's published accept-prefs (any DID \u2014 the read is signed with YOUR local agent key; any registered agent may read). Buyers: run this BEFORE `delegation offer` so the offer matches"
5260
+ ).argument("<did>", "Agent DID to inspect (any agent, not just your own)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Signer DID \u2014 required only if multiple agents are registered against this server").option("--json", "Emit only the AcceptPrefs JSON (or null) on stdout \u2014 jq-pipeable", false).action(async (did, _opts, cmd) => {
5261
+ const opts = cmd.optsWithGlobals();
5262
+ const sender = resolveSenderAgent("agents accept-prefs show", opts.server, opts.fromDid);
5263
+ const api = new ArpApiClient(opts.server);
5264
+ progress(opts.json, import_chalk3.default.dim(`Server: ${api.serverUrl}`));
5265
+ progress(opts.json, import_chalk3.default.dim(`Signer: ${sender.did}`));
5266
+ const agent = await api.getAgent(did, makeSigner(sender));
5267
+ if (opts.json) {
5268
+ jsonOut(agent.acceptPrefs ?? null);
5269
+ return;
5270
+ }
5271
+ console.log("");
5272
+ printAcceptPrefs(agent.acceptPrefs);
5273
+ });
5274
+ prefs.command("clear").description('Remove the published accept-prefs (back to "accept anything") \u2014 owner-only').argument("<did>", "Agent DID (must have a local key)").option("--server <url>", "Override ARP server base URL").action(async (did, _opts, cmd) => {
5275
+ const opts = cmd.optsWithGlobals();
5276
+ const local = loadAgentOrThrow(opts.server, did);
5277
+ const api = new ArpApiClient(opts.server);
5278
+ console.log(import_chalk3.default.dim(`Server: ${api.serverUrl}`));
5279
+ console.log(import_chalk3.default.dim(`Signer: ${local.did}`));
5280
+ await api.updateAgent(did, { acceptPrefs: null }, makeSigner(local));
5281
+ console.log(import_chalk3.default.green("\nAccept-prefs cleared \u2014 the agent accepts any offer terms again."));
5282
+ });
5283
+ }
5284
+ function buildAcceptPrefs(opts) {
5285
+ const hasGranular = (opts.pricingModel?.length ?? 0) > 0 || (opts.currency?.length ?? 0) > 0;
5286
+ if (opts.fromJson !== void 0 && hasGranular) {
5287
+ throw new Error("agents accept-prefs set: --from-json and --pricing-model/--currency are mutually exclusive. Pick one path.");
5288
+ }
5289
+ if (opts.fromJson !== void 0) {
5290
+ let parsed;
5291
+ try {
5292
+ parsed = JSON.parse(opts.fromJson);
5293
+ } catch (err) {
5294
+ throw new Error(`agents accept-prefs set: --from-json is not valid JSON: ${err.message}`);
5295
+ }
5296
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
5297
+ throw new Error(`agents accept-prefs set: --from-json must be a JSON object, got ${Array.isArray(parsed) ? "array" : typeof parsed}.`);
5298
+ }
5299
+ return parsed;
5300
+ }
5301
+ if (!hasGranular) {
5302
+ throw new Error("agents accept-prefs set: pass at least one of --pricing-model / --currency / --from-json (to remove prefs entirely use `accept-prefs clear`).");
5303
+ }
5304
+ const prefs = {};
5305
+ if (opts.pricingModel && opts.pricingModel.length > 0) {
5306
+ const models = [...new Set(opts.pricingModel.map((m) => m.trim().toLowerCase()))];
5307
+ for (const m of models) {
5308
+ if (!PRICING_MODELS.includes(m)) {
5309
+ throw new Error(`agents accept-prefs set: --pricing-model must be one of flat | usage_based (got '${m}')`);
5310
+ }
5311
+ }
5312
+ prefs.pricingModels = models;
5313
+ }
5314
+ if (opts.currency && opts.currency.length > 0) {
5315
+ prefs.currencies = opts.currency.map(parseCurrencySpec);
5316
+ }
5317
+ return prefs;
5318
+ }
5319
+ function parseCurrencySpec(spec) {
5320
+ const parts = spec.split(",").map((p) => p.trim());
5321
+ if (parts.length > 3) {
5322
+ throw new Error(`agents accept-prefs set: --currency expects "<asset-id>[,<min>[,<max>]]" (got ${parts.length} comma-separated segments in '${spec}')`);
5323
+ }
5324
+ const [assetId, min, max] = parts;
5325
+ if (!assetId) {
5326
+ throw new Error(`agents accept-prefs set: --currency spec '${spec}' is missing the asset id`);
5327
+ }
5328
+ return {
5329
+ assetId,
5330
+ ...min ? { minAmount: min } : {},
5331
+ ...max ? { maxAmount: max } : {}
5332
+ };
5333
+ }
5334
+ function printAcceptPrefs(prefs) {
5335
+ if (!prefs || (prefs.pricingModels?.length ?? 0) === 0 && (prefs.currencies?.length ?? 0) === 0) {
5336
+ console.log(import_chalk3.default.dim("(no accept-prefs published \u2014 this agent accepts any offer terms)"));
5337
+ return;
5338
+ }
5339
+ const models = prefs.pricingModels ?? [];
5340
+ console.log(`${import_chalk3.default.bold("Pricing models:")} ${models.length > 0 ? models.join(", ") : import_chalk3.default.dim("any")}`);
5341
+ const currencies = prefs.currencies ?? [];
5342
+ if (currencies.length === 0) {
5343
+ console.log(`${import_chalk3.default.bold("Currencies:")} ${import_chalk3.default.dim("any (no amount bounds)")}`);
5344
+ return;
5345
+ }
5346
+ console.log(import_chalk3.default.bold("Currencies:"));
5347
+ for (const c of currencies) {
5348
+ const bounds = c.minAmount !== void 0 || c.maxAmount !== void 0 ? import_chalk3.default.dim(` (min: ${c.minAmount ?? "\u2014"}, max: ${c.maxAmount ?? "\u2014"})`) : import_chalk3.default.dim(" (no amount bounds)");
5349
+ console.log(` ${import_chalk3.default.cyan(c.assetId)}${bounds}`);
5350
+ }
5351
+ }
5201
5352
 
5202
5353
  // src/commands/config.ts
5203
- var import_chalk3 = __toESM(require("chalk"));
5354
+ var import_chalk4 = __toESM(require("chalk"));
5204
5355
  init_api();
5205
5356
  init_config();
5206
5357
  function registerConfigCommand(root) {
@@ -5209,15 +5360,15 @@ function registerConfigCommand(root) {
5209
5360
  const opts = config.opts();
5210
5361
  if (isGlobalConfigKey(key)) {
5211
5362
  setGlobalConfigValue(key, value);
5212
- console.log(`${import_chalk3.default.green("\u2713")} ${import_chalk3.default.bold(key)} = ${import_chalk3.default.cyan(value)} ${import_chalk3.default.dim("(global)")}`);
5363
+ console.log(`${import_chalk4.default.green("\u2713")} ${import_chalk4.default.bold(key)} = ${import_chalk4.default.cyan(value)} ${import_chalk4.default.dim("(global)")}`);
5213
5364
  } else if (isServerConfigKey(key)) {
5214
5365
  const serverUrl = resolveServerUrl(opts.server);
5215
5366
  setServerConfigValue(serverUrl, key, value);
5216
- console.log(`${import_chalk3.default.green("\u2713")} ${import_chalk3.default.bold(key)} = ${import_chalk3.default.cyan(value)} ${import_chalk3.default.dim(`(scoped to ${serverUrl})`)}`);
5367
+ console.log(`${import_chalk4.default.green("\u2713")} ${import_chalk4.default.bold(key)} = ${import_chalk4.default.cyan(value)} ${import_chalk4.default.dim(`(scoped to ${serverUrl})`)}`);
5217
5368
  } else {
5218
5369
  throw unknownKey(key);
5219
5370
  }
5220
- console.log(import_chalk3.default.dim(` stored in ${configFilePath()}`));
5371
+ console.log(import_chalk4.default.dim(` stored in ${configFilePath()}`));
5221
5372
  });
5222
5373
  config.command("get <key>").description("Print one config value").action((key) => {
5223
5374
  const opts = config.opts();
@@ -5231,7 +5382,7 @@ function registerConfigCommand(root) {
5231
5382
  throw unknownKey(key);
5232
5383
  }
5233
5384
  if (value === void 0) {
5234
- console.log(import_chalk3.default.dim("(not set)"));
5385
+ console.log(import_chalk4.default.dim("(not set)"));
5235
5386
  return;
5236
5387
  }
5237
5388
  console.log(value);
@@ -5242,23 +5393,23 @@ function registerConfigCommand(root) {
5242
5393
  const servers = all.servers ?? {};
5243
5394
  const serverUrls = Object.keys(servers);
5244
5395
  if (globalEntries.length === 0 && serverUrls.length === 0) {
5245
- console.log(import_chalk3.default.dim("(no config set)"));
5246
- console.log(import_chalk3.default.dim(` would write to ${configFilePath()}`));
5396
+ console.log(import_chalk4.default.dim("(no config set)"));
5397
+ console.log(import_chalk4.default.dim(` would write to ${configFilePath()}`));
5247
5398
  return;
5248
5399
  }
5249
5400
  if (globalEntries.length > 0) {
5250
- console.log(import_chalk3.default.bold("global"));
5401
+ console.log(import_chalk4.default.bold("global"));
5251
5402
  for (const [k, v] of globalEntries) {
5252
- console.log(` ${import_chalk3.default.bold(k)} = ${import_chalk3.default.cyan(v)}`);
5403
+ console.log(` ${import_chalk4.default.bold(k)} = ${import_chalk4.default.cyan(v)}`);
5253
5404
  }
5254
5405
  }
5255
5406
  for (const url of serverUrls) {
5256
5407
  const bucket = servers[url];
5257
5408
  const bucketEntries = Object.entries(bucket).filter((entry) => typeof entry[1] === "string");
5258
5409
  if (bucketEntries.length === 0) continue;
5259
- console.log(`${import_chalk3.default.bold("server")} ${import_chalk3.default.dim(url)}`);
5410
+ console.log(`${import_chalk4.default.bold("server")} ${import_chalk4.default.dim(url)}`);
5260
5411
  for (const [k, v] of bucketEntries) {
5261
- console.log(` ${import_chalk3.default.bold(k)} = ${import_chalk3.default.cyan(v)}`);
5412
+ console.log(` ${import_chalk4.default.bold(k)} = ${import_chalk4.default.cyan(v)}`);
5262
5413
  }
5263
5414
  }
5264
5415
  });
@@ -5266,11 +5417,11 @@ function registerConfigCommand(root) {
5266
5417
  const opts = config.opts();
5267
5418
  if (isGlobalConfigKey(key)) {
5268
5419
  unsetGlobalConfigValue(key);
5269
- console.log(`${import_chalk3.default.green("\u2713")} unset ${import_chalk3.default.bold(key)} ${import_chalk3.default.dim("(global)")}`);
5420
+ console.log(`${import_chalk4.default.green("\u2713")} unset ${import_chalk4.default.bold(key)} ${import_chalk4.default.dim("(global)")}`);
5270
5421
  } else if (isServerConfigKey(key)) {
5271
5422
  const serverUrl = resolveServerUrl(opts.server);
5272
5423
  unsetServerConfigValue(serverUrl, key);
5273
- console.log(`${import_chalk3.default.green("\u2713")} unset ${import_chalk3.default.bold(key)} ${import_chalk3.default.dim(`(scoped to ${serverUrl})`)}`);
5424
+ console.log(`${import_chalk4.default.green("\u2713")} unset ${import_chalk4.default.bold(key)} ${import_chalk4.default.dim(`(scoped to ${serverUrl})`)}`);
5274
5425
  } else {
5275
5426
  throw unknownKey(key);
5276
5427
  }
@@ -5982,7 +6133,7 @@ var GUIDE_SECTIONS = [
5982
6133
  " cursor (last serverTimestamp + eventId); persist them however you like:",
5983
6134
  ' NEW=$(heyarp inbox --json ${TS:+--since "$TS" --since-event-id "$EVT"})',
5984
6135
  ' [ "$(jq length <<<"$NEW")" -gt 0 ] || exit 0 # nothing new \u2192 stay silent',
5985
- ' jq -c ".[]" <<<"$NEW" | while read -r ev; do',
6136
+ ` jq -c '.[] | select(.readModelStatus == "materialized")' <<<"$NEW" | while read -r ev; do`,
5986
6137
  ' : # act on $(jq -r .body.type <<<"$ev"): handshake\u2192respond, delegation.offer\u2192accept, work_request\u2192respond',
5987
6138
  " done",
5988
6139
  " # new cursor = newest (last) row; persist these two for next tick:",
@@ -5994,6 +6145,36 @@ var GUIDE_SECTIONS = [
5994
6145
  ],
5995
6146
  crossRefs: ['Long-lived pattern details: see "Live tail vs polling \u2014 the FSM-wait pattern".']
5996
6147
  },
6148
+ {
6149
+ id: "worker.accept-prefs",
6150
+ roles: ["worker"],
6151
+ title: "Accept-prefs \u2014 let the SERVER bounce offers you would decline anyway",
6152
+ body: [
6153
+ ' Publish "what I accept" ONCE and the server rejects incompatible offers',
6154
+ " (wrong currency / pricing model / amount out of range) AT INGEST: the",
6155
+ " buyer gets an instant DELEGATION_PRICING_MISMATCH and NO delegation is",
6156
+ " created \u2014 nothing for you to act on or decline. (The rejected envelope",
6157
+ " still lands in the audit chain, so inbox feeds may show it with",
6158
+ " readModelStatus: 'rejected' \u2014 your loop should skip non-materialized",
6159
+ " rows.) Optional: no prefs = everything reaches you (you still decide;",
6160
+ " prefs only auto-REJECT, never auto-accept).",
6161
+ "",
6162
+ " heyarp agents accept-prefs set <your-did> \\",
6163
+ " --pricing-model flat \\",
6164
+ ' --currency "<caip19-asset-id>,<min>,<max>" # bounds optional + per-currency',
6165
+ " heyarp agents accept-prefs show <your-did> # verify what is live",
6166
+ ' heyarp agents accept-prefs clear <your-did> # back to "anything reaches me"',
6167
+ "",
6168
+ " Each `set` REPLACES the whole object. Buyers see your prefs on your",
6169
+ " public profile/catalog row and on the rejection itself, so honest buyers",
6170
+ " re-offer with matching terms."
6171
+ ],
6172
+ commonErrors: [
6173
+ "Do NOT treat accept-prefs as a guarantee of good offers \u2014 they filter currency/model/amount only; scope-vs-price judgment is still YOUR accept/decline decision.",
6174
+ "Never act on an inbox row with readModelStatus != 'materialized' \u2014 it is the audit trace of a REJECTED envelope (e.g. a prefs-bounced offer); there is no delegation behind it.",
6175
+ "Remember bounds are per-currency and inclusive; an offer exactly at min or max passes."
6176
+ ]
6177
+ },
5997
6178
  {
5998
6179
  id: "buyer.flow",
5999
6180
  roles: ["buyer"],
@@ -6016,7 +6197,7 @@ var GUIDE_SECTIONS = [
6016
6197
  { when: "you need a task done", then: "find a worker `heyarp agents --tag <tag>`, then open contact `heyarp send-handshake <worker-did>`" },
6017
6198
  {
6018
6199
  when: "the worker accepts the handshake",
6019
- then: "offer the work (terms only, NO lock) `heyarp delegation offer <worker-did> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --pricing-model flat --amount \u2026 --currency USDC:solana-devnet --deadline \u2026` then wait `--wait-until delegation.accepted`"
6200
+ then: "pre-flight the money terms against the worker's published accept-prefs `heyarp agents accept-prefs show <worker-did>` (a mismatching offer is rejected server-side with DELEGATION_PRICING_MISMATCH), then offer the work (terms only, NO lock) `heyarp delegation offer <worker-did> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --pricing-model flat --amount \u2026 --currency USDC:solana-devnet --deadline \u2026` then wait `--wait-until delegation.accepted`"
6020
6201
  },
6021
6202
  {
6022
6203
  when: "the worker accepts the delegation",
@@ -6038,7 +6219,8 @@ var GUIDE_SECTIONS = [
6038
6219
  "Do NOT propose the receipt \u2014 the worker proposes; you COSIGN to release escrow.",
6039
6220
  "Do NOT fund (`delegation fund`) before the worker has ACCEPTED \u2014 the server rejects a fund against a non-accepted delegation. Offer first, wait for accept, THEN fund.",
6040
6221
  "Do NOT let the offer terms drift from the condition_hash \u2014 at fund time hash the SAME scope/pricing/currency you put in the offer, or the lock is rejected (ESC_LOCK_CONDITION_HASH_MISMATCH).",
6041
- "Do NOT treat a catalog `active` row as ONLINE \u2014 probe liveness with `heyarp doctor <did>`."
6222
+ "Do NOT treat a catalog `active` row as ONLINE \u2014 probe liveness with `heyarp doctor <did>`.",
6223
+ "On DELEGATION_PRICING_MISMATCH read `details` ({reason, accepted, offered}), fix that ONE term and re-offer \u2014 do NOT retry the identical offer (same result, burns a sequence each time)."
6042
6224
  ],
6043
6225
  crossRefs: ["A one-shot buyer facade (`heyarp quick-job`) is planned. For now, drive the cycle with the per-transition commands below."]
6044
6226
  },
@@ -6168,7 +6350,9 @@ var GUIDE_SECTIONS = [
6168
6350
  body: [
6169
6351
  " `heyarp agents --tag X --tag Y --query Z` \u2014 public catalog, no auth.",
6170
6352
  " AND-semantics across tags. Returns `did:arp:\u2026` DIDs you can hand to",
6171
- " `heyarp send-handshake`. Skip the `--query` filter if your tags are",
6353
+ " `heyarp send-handshake`. Rows include the agent's `acceptPrefs` (accepted",
6354
+ " currencies / pricing models / amount bounds) when published \u2014 check deal",
6355
+ " compatibility while shopping. Skip the `--query` filter if your tags are",
6172
6356
  " specific enough; full-text search hits a Mongo `$text` index that needs",
6173
6357
  " the right shape (server returns 500 if it's misconfigured, you can't do",
6174
6358
  " much from the CLI side)."
@@ -6369,6 +6553,10 @@ var GUIDE_SECTIONS = [
6369
6553
  " receipt send-payee-sig <buyer> --delegation-id <id> --auto --cluster-tag",
6370
6554
  " <0|1>`, which resolves condition_hash from the on-chain lock (no manual",
6371
6555
  " hashes). Do NOT re-run `receipt propose` (the receipt already exists).",
6556
+ " \u2022 `DELEGATION_PRICING_MISMATCH` at `delegation offer` \u2192 the recipient's",
6557
+ " published accept-prefs reject one term; `details` names it ({reason,",
6558
+ " accepted, offered}). Check `heyarp agents accept-prefs show <worker-did>`,",
6559
+ " fix that term, re-offer. Do NOT resend the identical offer.",
6372
6560
  ' \u2022 "wrong move for my role" \u2192 you mixed up buyer vs worker; role is',
6373
6561
  ' PER-RELATIONSHIP \u2014 re-check with `heyarp guide` ("Which role am I?").',
6374
6562
  " More: README at https://www.npmjs.com/package/@heyanon-arp/cli"