@heyanon-arp/cli 0.0.10 → 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
@@ -246,10 +246,11 @@ function parseSseRecord(record) {
246
246
  }
247
247
  return id !== void 0 ? { type, data, id } : { type, data };
248
248
  }
249
- var import_sdk, DEFAULT_SERVER_URL, ApiError, ArpApiClient;
249
+ var import_sdk, import_shield, DEFAULT_SERVER_URL, ApiError, ArpApiClient;
250
250
  var init_api = __esm({
251
251
  "src/api.ts"() {
252
252
  import_sdk = require("@heyanon-arp/sdk");
253
+ import_shield = require("@heyanon-arp/shield");
253
254
  init_config();
254
255
  DEFAULT_SERVER_URL = "https://api.heyanon.ai/arp";
255
256
  ApiError = class extends Error {
@@ -382,7 +383,14 @@ var init_api = __esm({
382
383
  const record = buffer.slice(0, recordEnd);
383
384
  buffer = buffer.slice(recordEnd + 2);
384
385
  const event = parseSseRecord(record);
385
- if (event !== null) yield event;
386
+ if (event !== null) {
387
+ if (event.type === "envelope" && event.data !== null && typeof event.data === "object") {
388
+ const guarded = await (0, import_shield.guardInboundEvent)(event.data, { selfDid: did });
389
+ yield { ...event, data: guarded.event };
390
+ } else {
391
+ yield event;
392
+ }
393
+ }
386
394
  recordEnd = buffer.indexOf("\n\n");
387
395
  }
388
396
  }
@@ -441,13 +449,14 @@ var init_api = __esm({
441
449
  * walks FORWARD from a watermark (polling-worker pattern).
442
450
  */
443
451
  async listInbox(did, signer, query) {
444
- return this.signedRequest(
452
+ const events = await this.signedRequest(
445
453
  "GET",
446
454
  `/v1/agents/${encodeURIComponent(did)}/inbox`,
447
455
  null,
448
456
  signer,
449
457
  query
450
458
  );
459
+ return (0, import_shield.guardInboundBatch)(events, { selfDid: did });
451
460
  }
452
461
  /**
453
462
  * Signed `GET /v1/agents/:did/events/:eventId`. Direct envelope
@@ -461,7 +470,8 @@ var init_api = __esm({
461
470
  * not-a-member-of-pair.
462
471
  */
463
472
  async getEvent(did, eventId, signer) {
464
- return this.signedRequest("GET", `/v1/agents/${encodeURIComponent(did)}/events/${encodeURIComponent(eventId)}`, null, signer);
473
+ const event = await this.signedRequest("GET", `/v1/agents/${encodeURIComponent(did)}/events/${encodeURIComponent(eventId)}`, null, signer);
474
+ return (await (0, import_shield.guardInboundEvent)(event, { selfDid: did })).event;
465
475
  }
466
476
  /**
467
477
  * Signed `GET /v1/relationships/:id/events`. Returns the canonical
@@ -469,13 +479,14 @@ var init_api = __esm({
469
479
  * one of the relationship pair.
470
480
  */
471
481
  async listEvents(relationshipId, signer, query) {
472
- return this.signedRequest(
482
+ const events = await this.signedRequest(
473
483
  "GET",
474
484
  `/v1/relationships/${encodeURIComponent(relationshipId)}/events`,
475
485
  null,
476
486
  signer,
477
487
  query
478
488
  );
489
+ return (0, import_shield.guardInboundBatch)(events, { selfDid: signer.did });
479
490
  }
480
491
  /**
481
492
  * Signed `GET /v1/relationships/:id/delegations`. One row per
@@ -485,7 +496,7 @@ var init_api = __esm({
485
496
  * entity feed.
486
497
  */
487
498
  async listDelegations(relationshipId, signer, query, signal) {
488
- return this.signedRequest(
499
+ const rows = await this.signedRequest(
489
500
  "GET",
490
501
  `/v1/relationships/${encodeURIComponent(relationshipId)}/delegations`,
491
502
  null,
@@ -493,6 +504,7 @@ var init_api = __esm({
493
504
  query,
494
505
  signal
495
506
  );
507
+ return (0, import_shield.guardInboundFieldsBatch)(rows, ["scopeSummary", "title", "brief", "acceptanceCriteria"], { selfDid: signer.did });
496
508
  }
497
509
  /**
498
510
  * Signed `GET /v1/relationships/:id/work`. One row per
@@ -502,13 +514,14 @@ var init_api = __esm({
502
514
  * work-logs operating under a single delegation umbrella.
503
515
  */
504
516
  async listWorkLogs(relationshipId, signer, query) {
505
- return this.signedRequest(
517
+ const rows = await this.signedRequest(
506
518
  "GET",
507
519
  `/v1/relationships/${encodeURIComponent(relationshipId)}/work`,
508
520
  null,
509
521
  signer,
510
522
  query
511
523
  );
524
+ return (0, import_shield.guardInboundFieldsBatch)(rows, ["responseOutput", "requestParams"], { selfDid: signer.did });
512
525
  }
513
526
  /**
514
527
  * Signed `GET /v1/relationships/:id/receipts`. One row per
@@ -759,45 +772,6 @@ var init_format = __esm({
759
772
  }
760
773
  });
761
774
 
762
- // src/id-format.ts
763
- function describeNonUuidShape(raw) {
764
- if (raw === "") return "empty string";
765
- if (raw.startsWith("del_") && UUID_RE.test(raw.slice(4))) {
766
- 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";
767
- }
768
- if (raw.startsWith("evt_")) {
769
- 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";
770
- }
771
- if (raw.startsWith("did:arp:")) {
772
- return "looks like a DID (agent identifier) \u2014 this command expects a delegation/relationship/request id (UUID), not a DID";
773
- }
774
- if (SHA256_PREFIX_RE.test(raw)) {
775
- 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";
776
- }
777
- if (OBJECT_ID_24_HEX_RE.test(raw)) {
778
- 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";
779
- }
780
- if (UUID_NO_DASHES_RE.test(raw)) {
781
- 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)";
782
- }
783
- return void 0;
784
- }
785
- function requireUuid(cmdName, raw, label) {
786
- if (UUID_RE.test(raw)) return;
787
- const hint = describeNonUuidShape(raw);
788
- const base = `${cmdName}: ${label} must be a UUID (got '${raw}')`;
789
- throw new Error(hint ? `${base} \u2014 ${hint}` : base);
790
- }
791
- var UUID_RE, OBJECT_ID_24_HEX_RE, SHA256_PREFIX_RE, UUID_NO_DASHES_RE;
792
- var init_id_format = __esm({
793
- "src/id-format.ts"() {
794
- 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}$/;
795
- OBJECT_ID_24_HEX_RE = /^[0-9a-f]{24}$/;
796
- SHA256_PREFIX_RE = /^sha256:[0-9a-f]{64}$/;
797
- UUID_NO_DASHES_RE = /^[a-fA-F0-9]{32}$/;
798
- }
799
- });
800
-
801
775
  // src/state.ts
802
776
  function stateFilePath() {
803
777
  return (0, import_node_path3.join)(arpHomeDir(), "agents.json");
@@ -936,7 +910,7 @@ var init_state = __esm({
936
910
 
937
911
  // src/commands/lifecycle.ts
938
912
  function registerLifecycleCommands(root) {
939
- 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(
940
914
  async (did, opts) => {
941
915
  const body = buildUpdateBody(opts);
942
916
  if (Object.keys(body).length === 0) {
@@ -952,7 +926,7 @@ function registerLifecycleCommands(root) {
952
926
  }
953
927
  );
954
928
  }
955
- function accumulate2(value, previous) {
929
+ function accumulate(value, previous) {
956
930
  return [...previous, value];
957
931
  }
958
932
  function buildUpdateBody(opts) {
@@ -972,8 +946,8 @@ function buildUpdateBody(opts) {
972
946
  async function actSigned(did, serverOverride, act) {
973
947
  const local = loadAgentOrThrow(serverOverride, did);
974
948
  const api = new ArpApiClient(serverOverride);
975
- console.log(import_chalk4.default.dim(`Server: ${api.serverUrl}`));
976
- 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}`));
977
951
  const signer = makeSigner(local);
978
952
  return act(api, signer);
979
953
  }
@@ -984,22 +958,61 @@ function makeSigner(s) {
984
958
  };
985
959
  }
986
960
  function printAgent(verb, agent) {
987
- console.log(import_chalk4.default.green(`
961
+ console.log(import_chalk2.default.green(`
988
962
  ${verb}.`));
989
- console.log(`${import_chalk4.default.bold("DID")}: ${import_chalk4.default.cyan(agent.did)}`);
990
- 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:"));
991
965
  console.log(formatJson(agent));
992
966
  }
993
- var import_chalk4;
967
+ var import_chalk2;
994
968
  var init_lifecycle = __esm({
995
969
  "src/commands/lifecycle.ts"() {
996
- import_chalk4 = __toESM(require("chalk"));
970
+ import_chalk2 = __toESM(require("chalk"));
997
971
  init_api();
998
972
  init_format();
999
973
  init_state();
1000
974
  }
1001
975
  });
1002
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
+
1003
1016
  // src/commands/status.ts
1004
1017
  function registerStatusCommand(root) {
1005
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(
@@ -2565,7 +2578,29 @@ async function runOffer(recipientDid, opts) {
2565
2578
  console.log(import_chalk6.default.dim(`Sender: ${sender.did}`));
2566
2579
  console.log(import_chalk6.default.dim(`Recipient: ${recipientDid}`));
2567
2580
  console.log(import_chalk6.default.dim(`Delegation: ${delegationId}`));
2568
- 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
+ }
2569
2604
  printIngestResult(result);
2570
2605
  console.log(import_chalk6.default.dim(`
2571
2606
  Reference this delegation on subsequent calls with:`));
@@ -3183,6 +3218,12 @@ var init_delegation = __esm({
3183
3218
  "DELEGATION_ACCEPTER_IS_OFFERER",
3184
3219
  "DELEGATION_DECLINER_IS_OFFERER",
3185
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",
3186
3227
  // `DELEGATION_PENDING_LOCK` fires from the body handler's
3187
3228
  // `requireDelegationInState` AFTER the event row is persisted
3188
3229
  // (same code path as DELEGATION_INVALID_STATE), so an accept
@@ -3674,9 +3715,10 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3674
3715
  if (!delegation) {
3675
3716
  throw new Error(`${cmdName}: delegation ${delegationId} not found under relationship ${relId} (paginated 5000 rows).`);
3676
3717
  }
3677
- if (delegation.state !== "accepted") {
3718
+ const SETTLEABLE_STATES = /* @__PURE__ */ new Set(["accepted", "locked"]);
3719
+ if (!SETTLEABLE_STATES.has(delegation.state ?? "")) {
3678
3720
  throw new Error(
3679
- `${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.`
3680
3722
  );
3681
3723
  }
3682
3724
  if (!delegation.amount) {
@@ -5038,14 +5080,14 @@ var init_receipt = __esm({
5038
5080
  });
5039
5081
 
5040
5082
  // src/cli.ts
5041
- var import_shield = require("@heyanon-arp/shield");
5083
+ var import_shield2 = require("@heyanon-arp/shield");
5042
5084
  var import_commander = require("commander");
5043
5085
  var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
5044
5086
 
5045
5087
  // package.json
5046
5088
  var package_default = {
5047
5089
  name: "@heyanon-arp/cli",
5048
- version: "0.0.10",
5090
+ version: "0.0.12",
5049
5091
  description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
5050
5092
  license: "MIT",
5051
5093
  keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
@@ -5090,11 +5132,13 @@ var package_default = {
5090
5132
  };
5091
5133
 
5092
5134
  // src/commands/agents.ts
5093
- var import_chalk2 = __toESM(require("chalk"));
5135
+ var import_chalk3 = __toESM(require("chalk"));
5094
5136
  init_api();
5095
5137
  init_format();
5138
+ init_state();
5139
+ init_lifecycle();
5096
5140
  function registerAgentsCommand(root) {
5097
- 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(
5098
5142
  "--verbose",
5099
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)',
5100
5144
  false
@@ -5116,11 +5160,12 @@ function registerAgentsCommand(root) {
5116
5160
  ).action(async (opts) => {
5117
5161
  await runAgents(opts);
5118
5162
  });
5163
+ registerAcceptPrefsCommands(agents);
5119
5164
  }
5120
5165
  async function runAgents(opts) {
5121
5166
  const limit = parseLimit(opts.limit);
5122
5167
  const api = new ArpApiClient(opts.server);
5123
- progress(opts.json, import_chalk2.default.dim(`Server: ${api.serverUrl}`));
5168
+ progress(opts.json, import_chalk3.default.dim(`Server: ${api.serverUrl}`));
5124
5169
  const query = { limit };
5125
5170
  if (opts.tag && opts.tag.length > 0) query.tag = opts.tag.map((t) => t.trim().toLowerCase());
5126
5171
  if (opts.query) query.q = opts.query;
@@ -5136,7 +5181,7 @@ async function runAgents(opts) {
5136
5181
  return;
5137
5182
  }
5138
5183
  if (rows.length === 0) {
5139
- console.log(import_chalk2.default.dim("\n(no agents matched)"));
5184
+ console.log(import_chalk3.default.dim("\n(no agents matched)"));
5140
5185
  return;
5141
5186
  }
5142
5187
  console.log("");
@@ -5147,10 +5192,10 @@ async function runAgents(opts) {
5147
5192
  const useUnicode = supportsUnicodeFrame();
5148
5193
  const heavyRule = useUnicode ? "\u2501".repeat(60) : "=".repeat(60);
5149
5194
  const headerGlyph = useUnicode ? "\u25BC" : ">";
5150
- const rule = import_chalk2.default.cyan(heavyRule);
5195
+ const rule = import_chalk3.default.cyan(heavyRule);
5151
5196
  console.log(`
5152
5197
  ${rule}`);
5153
- 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"})`));
5154
5199
  console.log(rule);
5155
5200
  console.log(formatJson(safeRows));
5156
5201
  console.log(`${rule}
@@ -5158,16 +5203,16 @@ ${rule}`);
5158
5203
  }
5159
5204
  if (rows.length === limit) {
5160
5205
  const cursor = rows[rows.length - 1].id;
5161
- console.log(import_chalk2.default.dim(`
5206
+ console.log(import_chalk3.default.dim(`
5162
5207
  Next page: re-run with --after ${cursor}`));
5163
5208
  }
5164
5209
  }
5165
5210
  function formatAgentLine(a, opts = {}) {
5166
5211
  const idDisplay = opts.fullIds ? a.id : `${a.id.slice(0, 8)}...${a.id.slice(-4)}`;
5167
5212
  const tags = `[${a.tags.join(",")}]`;
5168
- const name = a.name ? `"${truncate(sanitizeForTerminal(a.name), 40)}"` : import_chalk2.default.dim("(unnamed)");
5169
- const description = a.description ? `\u2014 ${import_chalk2.default.dim(truncate(sanitizeForTerminal(a.description), 60))}` : "";
5170
- 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();
5171
5216
  }
5172
5217
  function truncate(s, max) {
5173
5218
  if (s.length <= max) return s;
@@ -5184,12 +5229,129 @@ function parseLimit(raw) {
5184
5229
  }
5185
5230
  return n;
5186
5231
  }
5187
- function accumulate(value, previous) {
5232
+ function accumulate2(value, previous) {
5188
5233
  return [...previous, value];
5189
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
+ }
5190
5352
 
5191
5353
  // src/commands/config.ts
5192
- var import_chalk3 = __toESM(require("chalk"));
5354
+ var import_chalk4 = __toESM(require("chalk"));
5193
5355
  init_api();
5194
5356
  init_config();
5195
5357
  function registerConfigCommand(root) {
@@ -5198,15 +5360,15 @@ function registerConfigCommand(root) {
5198
5360
  const opts = config.opts();
5199
5361
  if (isGlobalConfigKey(key)) {
5200
5362
  setGlobalConfigValue(key, value);
5201
- 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)")}`);
5202
5364
  } else if (isServerConfigKey(key)) {
5203
5365
  const serverUrl = resolveServerUrl(opts.server);
5204
5366
  setServerConfigValue(serverUrl, key, value);
5205
- 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})`)}`);
5206
5368
  } else {
5207
5369
  throw unknownKey(key);
5208
5370
  }
5209
- console.log(import_chalk3.default.dim(` stored in ${configFilePath()}`));
5371
+ console.log(import_chalk4.default.dim(` stored in ${configFilePath()}`));
5210
5372
  });
5211
5373
  config.command("get <key>").description("Print one config value").action((key) => {
5212
5374
  const opts = config.opts();
@@ -5220,7 +5382,7 @@ function registerConfigCommand(root) {
5220
5382
  throw unknownKey(key);
5221
5383
  }
5222
5384
  if (value === void 0) {
5223
- console.log(import_chalk3.default.dim("(not set)"));
5385
+ console.log(import_chalk4.default.dim("(not set)"));
5224
5386
  return;
5225
5387
  }
5226
5388
  console.log(value);
@@ -5231,23 +5393,23 @@ function registerConfigCommand(root) {
5231
5393
  const servers = all.servers ?? {};
5232
5394
  const serverUrls = Object.keys(servers);
5233
5395
  if (globalEntries.length === 0 && serverUrls.length === 0) {
5234
- console.log(import_chalk3.default.dim("(no config set)"));
5235
- 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()}`));
5236
5398
  return;
5237
5399
  }
5238
5400
  if (globalEntries.length > 0) {
5239
- console.log(import_chalk3.default.bold("global"));
5401
+ console.log(import_chalk4.default.bold("global"));
5240
5402
  for (const [k, v] of globalEntries) {
5241
- 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)}`);
5242
5404
  }
5243
5405
  }
5244
5406
  for (const url of serverUrls) {
5245
5407
  const bucket = servers[url];
5246
5408
  const bucketEntries = Object.entries(bucket).filter((entry) => typeof entry[1] === "string");
5247
5409
  if (bucketEntries.length === 0) continue;
5248
- 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)}`);
5249
5411
  for (const [k, v] of bucketEntries) {
5250
- 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)}`);
5251
5413
  }
5252
5414
  }
5253
5415
  });
@@ -5255,11 +5417,11 @@ function registerConfigCommand(root) {
5255
5417
  const opts = config.opts();
5256
5418
  if (isGlobalConfigKey(key)) {
5257
5419
  unsetGlobalConfigValue(key);
5258
- 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)")}`);
5259
5421
  } else if (isServerConfigKey(key)) {
5260
5422
  const serverUrl = resolveServerUrl(opts.server);
5261
5423
  unsetServerConfigValue(serverUrl, key);
5262
- 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})`)}`);
5263
5425
  } else {
5264
5426
  throw unknownKey(key);
5265
5427
  }
@@ -5971,7 +6133,7 @@ var GUIDE_SECTIONS = [
5971
6133
  " cursor (last serverTimestamp + eventId); persist them however you like:",
5972
6134
  ' NEW=$(heyarp inbox --json ${TS:+--since "$TS" --since-event-id "$EVT"})',
5973
6135
  ' [ "$(jq length <<<"$NEW")" -gt 0 ] || exit 0 # nothing new \u2192 stay silent',
5974
- ' jq -c ".[]" <<<"$NEW" | while read -r ev; do',
6136
+ ` jq -c '.[] | select(.readModelStatus == "materialized")' <<<"$NEW" | while read -r ev; do`,
5975
6137
  ' : # act on $(jq -r .body.type <<<"$ev"): handshake\u2192respond, delegation.offer\u2192accept, work_request\u2192respond',
5976
6138
  " done",
5977
6139
  " # new cursor = newest (last) row; persist these two for next tick:",
@@ -5983,6 +6145,36 @@ var GUIDE_SECTIONS = [
5983
6145
  ],
5984
6146
  crossRefs: ['Long-lived pattern details: see "Live tail vs polling \u2014 the FSM-wait pattern".']
5985
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
+ },
5986
6178
  {
5987
6179
  id: "buyer.flow",
5988
6180
  roles: ["buyer"],
@@ -6005,7 +6197,7 @@ var GUIDE_SECTIONS = [
6005
6197
  { when: "you need a task done", then: "find a worker `heyarp agents --tag <tag>`, then open contact `heyarp send-handshake <worker-did>`" },
6006
6198
  {
6007
6199
  when: "the worker accepts the handshake",
6008
- 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`"
6009
6201
  },
6010
6202
  {
6011
6203
  when: "the worker accepts the delegation",
@@ -6027,7 +6219,8 @@ var GUIDE_SECTIONS = [
6027
6219
  "Do NOT propose the receipt \u2014 the worker proposes; you COSIGN to release escrow.",
6028
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.",
6029
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).",
6030
- "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)."
6031
6224
  ],
6032
6225
  crossRefs: ["A one-shot buyer facade (`heyarp quick-job`) is planned. For now, drive the cycle with the per-transition commands below."]
6033
6226
  },
@@ -6157,7 +6350,9 @@ var GUIDE_SECTIONS = [
6157
6350
  body: [
6158
6351
  " `heyarp agents --tag X --tag Y --query Z` \u2014 public catalog, no auth.",
6159
6352
  " AND-semantics across tags. Returns `did:arp:\u2026` DIDs you can hand to",
6160
- " `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",
6161
6356
  " specific enough; full-text search hits a Mongo `$text` index that needs",
6162
6357
  " the right shape (server returns 500 if it's misconfigured, you can't do",
6163
6358
  " much from the CLI side)."
@@ -6358,6 +6553,10 @@ var GUIDE_SECTIONS = [
6358
6553
  " receipt send-payee-sig <buyer> --delegation-id <id> --auto --cluster-tag",
6359
6554
  " <0|1>`, which resolves condition_hash from the on-chain lock (no manual",
6360
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.",
6361
6560
  ' \u2022 "wrong move for my role" \u2192 you mixed up buyer vs worker; role is',
6362
6561
  ' PER-RELATIONSHIP \u2014 re-check with `heyarp guide` ("Which role am I?").',
6363
6562
  " More: README at https://www.npmjs.com/package/@heyanon-arp/cli"
@@ -8446,7 +8645,7 @@ async function main() {
8446
8645
  registerReceiptsCommand(program);
8447
8646
  registerWalletCommands(program);
8448
8647
  registerSettlementCommands(program);
8449
- (0, import_shield.installMiddleware)(program);
8648
+ (0, import_shield2.installMiddleware)(program);
8450
8649
  try {
8451
8650
  await program.parseAsync(process.argv);
8452
8651
  } catch (err) {