@heyanon-arp/cli 0.0.12 → 0.0.14

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
@@ -426,7 +426,9 @@ var init_api = __esm({
426
426
  * known but has never been party to an event.
427
427
  */
428
428
  async getActivitySummary(did) {
429
- return this.get(`/v1/agents/${encodeURIComponent(did)}/activity-summary`);
429
+ return this.get(
430
+ `/v1/agents/${encodeURIComponent(did)}/activity-summary`
431
+ );
430
432
  }
431
433
  /**
432
434
  * Signed `GET /v1/agents/:did/sender-sequence`. Returns the highest
@@ -504,7 +506,11 @@ var init_api = __esm({
504
506
  query,
505
507
  signal
506
508
  );
507
- return (0, import_shield.guardInboundFieldsBatch)(rows, ["scopeSummary", "title", "brief", "acceptanceCriteria"], { selfDid: signer.did });
509
+ return Promise.all(
510
+ rows.map(
511
+ (row) => signer.did === row.offererDid ? row : (0, import_shield.guardInboundFields)(row, ["scopeSummary", "title", "brief", "acceptanceCriteria"], { selfDid: signer.did }).then((r) => r.event)
512
+ )
513
+ );
508
514
  }
509
515
  /**
510
516
  * Signed `GET /v1/relationships/:id/work`. One row per
@@ -521,7 +527,14 @@ var init_api = __esm({
521
527
  signer,
522
528
  query
523
529
  );
524
- return (0, import_shield.guardInboundFieldsBatch)(rows, ["responseOutput", "requestParams"], { selfDid: signer.did });
530
+ return Promise.all(
531
+ rows.map((row) => {
532
+ const fields = [];
533
+ if (signer.did !== row.callerDid) fields.push("requestParams");
534
+ if (signer.did !== row.payeeDid) fields.push("responseOutput");
535
+ return fields.length === 0 ? row : (0, import_shield.guardInboundFields)(row, fields, { selfDid: signer.did }).then((r) => r.event);
536
+ })
537
+ );
525
538
  }
526
539
  /**
527
540
  * Signed `GET /v1/relationships/:id/receipts`. One row per
@@ -2599,6 +2612,13 @@ then re-run with matching --pricing-model / --currency / --amount.`
2599
2612
  )
2600
2613
  );
2601
2614
  }
2615
+ if (err instanceof ApiError && err.payload.code === "DELEGATION_CAPACITY_EXCEEDED") {
2616
+ const d = err.payload.details;
2617
+ const cap = d?.maxActive === 0 ? "closed for new offers (busy)" : `at capacity (${d?.currentActive}/${d?.maxActive} active delegations)`;
2618
+ console.error(import_chalk6.default.yellow(`
2619
+ The recipient is ${cap}.`));
2620
+ console.error(import_chalk6.default.dim("\nThis is TRANSIENT \u2014 your terms are fine. Retry the same offer later, or pick another worker (`heyarp agents --tag \u2026`)."));
2621
+ }
2602
2622
  throw err;
2603
2623
  }
2604
2624
  printIngestResult(result);
@@ -3224,6 +3244,10 @@ var init_delegation = __esm({
3224
3244
  // was consumed — advance it or the corrected re-offer trips
3225
3245
  // ENV_SEQUENCE_BACKWARDS.
3226
3246
  "DELEGATION_PRICING_MISMATCH",
3247
+ // Capacity gate: the recipient is at its published
3248
+ // maxActiveDelegations cap (or closed with 0). Same lifecycle as
3249
+ // the pricing gate — sequence consumed. TRANSIENT: retry later.
3250
+ "DELEGATION_CAPACITY_EXCEEDED",
3227
3251
  // `DELEGATION_PENDING_LOCK` fires from the body handler's
3228
3252
  // `requireDelegationInState` AFTER the event row is persisted
3229
3253
  // (same code path as DELEGATION_INVALID_STATE), so an accept
@@ -3489,6 +3513,11 @@ function settlementKeyFromDidDoc(didDoc, buyerDid) {
3489
3513
  return mb.startsWith("z") ? mb.slice(1) : mb;
3490
3514
  }
3491
3515
  function projectDelegationRowForHash(d) {
3516
+ if ((0, import_shield2.isShieldRedacted)(d.scopeSummary)) {
3517
+ throw new Error(
3518
+ `settlement: delegation ${d.delegationId} has shield-withheld scope (flagged by content-security). Cannot derive condition_hash over withheld content \u2014 do not settle; review and dispute.`
3519
+ );
3520
+ }
3492
3521
  const out = {
3493
3522
  delegationId: d.delegationId,
3494
3523
  scopeSummary: d.scopeSummary,
@@ -3896,10 +3925,11 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
3896
3925
  serverTimestamp: result.serverTimestamp
3897
3926
  };
3898
3927
  }
3899
- var import_sdk10, import_utils3, import_web33, import_chalk17, NATIVE_SOL_MINT2, SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS, SETTLEMENT_REFRESH_HEADROOM_SECS, LOCK_READ_NOT_VISIBLE_RETRIES, LOCK_READ_RETRY_DELAY_MS;
3928
+ var import_sdk10, import_shield2, import_utils3, import_web33, import_chalk17, NATIVE_SOL_MINT2, SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS, SETTLEMENT_REFRESH_HEADROOM_SECS, LOCK_READ_NOT_VISIBLE_RETRIES, LOCK_READ_RETRY_DELAY_MS;
3900
3929
  var init_settlement = __esm({
3901
3930
  "src/commands/settlement.ts"() {
3902
3931
  import_sdk10 = require("@heyanon-arp/sdk");
3932
+ import_shield2 = require("@heyanon-arp/shield");
3903
3933
  import_utils3 = require("@noble/hashes/utils");
3904
3934
  import_web33 = require("@solana/web3.js");
3905
3935
  import_chalk17 = __toESM(require("chalk"));
@@ -4263,6 +4293,11 @@ async function computeWorkLogHashes(api, sender, relationshipId, delegationId, r
4263
4293
  `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.`
4264
4294
  );
4265
4295
  }
4296
+ if ((0, import_shield3.isShieldRedacted)(workLog.requestParams) || (0, import_shield3.isShieldRedacted)(workLog.responseOutput)) {
4297
+ throw new Error(
4298
+ `receipt propose --auto-hashes: work-log row ${requestId} has shield-withheld content (the counterparty's request or response was flagged by content-security). Cannot reconstruct canonical hashes over withheld content \u2014 do not settle; review and dispute. See ~/.heyshield/receipts.jsonl.`
4299
+ );
4300
+ }
4266
4301
  const requestBody = {
4267
4302
  type: "work_request",
4268
4303
  content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, params: workLog.requestParams }
@@ -4965,11 +5000,12 @@ function requireDid2(cmdName, did, label) {
4965
5000
  throw new Error(`${cmdName}: ${label} must look like 'did:arp:...' (got '${did}')`);
4966
5001
  }
4967
5002
  }
4968
- var import_node_fs7, import_sdk11, import_chalk18, POST_COMMIT_ERROR_CODES2, VERDICT_VALUES, SHA256_RE;
5003
+ var import_node_fs7, import_sdk11, import_shield3, import_chalk18, POST_COMMIT_ERROR_CODES2, VERDICT_VALUES, SHA256_RE;
4969
5004
  var init_receipt = __esm({
4970
5005
  "src/commands/receipt.ts"() {
4971
5006
  import_node_fs7 = require("fs");
4972
5007
  import_sdk11 = require("@heyanon-arp/sdk");
5008
+ import_shield3 = require("@heyanon-arp/shield");
4973
5009
  import_chalk18 = __toESM(require("chalk"));
4974
5010
  init_api();
4975
5011
  init_format();
@@ -5080,14 +5116,14 @@ var init_receipt = __esm({
5080
5116
  });
5081
5117
 
5082
5118
  // src/cli.ts
5083
- var import_shield2 = require("@heyanon-arp/shield");
5119
+ var import_shield4 = require("@heyanon-arp/shield");
5084
5120
  var import_commander = require("commander");
5085
5121
  var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
5086
5122
 
5087
5123
  // package.json
5088
5124
  var package_default = {
5089
5125
  name: "@heyanon-arp/cli",
5090
- version: "0.0.12",
5126
+ version: "0.0.14",
5091
5127
  description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
5092
5128
  license: "MIT",
5093
5129
  keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
@@ -5240,9 +5276,12 @@ function registerAcceptPrefsCommands(agents) {
5240
5276
  `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
5277
  accumulate2,
5242
5278
  []
5279
+ ).option(
5280
+ "--max-active <n>",
5281
+ 'Capacity cap: max delegations held in ACTIVE states (offered/accepted/pending_lock/locked) at once. 0 = closed for new offers (the "busy flag"). Omit for unlimited. Excess offers are rejected server-side with DELEGATION_CAPACITY_EXCEEDED (transient \u2014 buyers retry later).'
5243
5282
  ).option(
5244
5283
  "--from-json <json>",
5245
- 'Full AcceptPrefs JSON object (mutually exclusive with --pricing-model / --currency). Shape: {"pricingModels":[...],"currencies":[{"assetId":"...","minAmount":"...","maxAmount":"..."}]}'
5284
+ 'Full AcceptPrefs JSON object (mutually exclusive with --pricing-model / --currency / --max-active). Shape: {"pricingModels":[...],"currencies":[{"assetId":"...","minAmount":"...","maxAmount":"..."}],"maxActiveDelegations":3}'
5246
5285
  ).action(async (did, _opts, cmd) => {
5247
5286
  const opts = cmd.optsWithGlobals();
5248
5287
  const body = buildAcceptPrefs(opts);
@@ -5282,9 +5321,9 @@ function registerAcceptPrefsCommands(agents) {
5282
5321
  });
5283
5322
  }
5284
5323
  function buildAcceptPrefs(opts) {
5285
- const hasGranular = (opts.pricingModel?.length ?? 0) > 0 || (opts.currency?.length ?? 0) > 0;
5324
+ const hasGranular = (opts.pricingModel?.length ?? 0) > 0 || (opts.currency?.length ?? 0) > 0 || opts.maxActive !== void 0;
5286
5325
  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.");
5326
+ throw new Error("agents accept-prefs set: --from-json and --pricing-model/--currency/--max-active are mutually exclusive. Pick one path.");
5288
5327
  }
5289
5328
  if (opts.fromJson !== void 0) {
5290
5329
  let parsed;
@@ -5299,7 +5338,9 @@ function buildAcceptPrefs(opts) {
5299
5338
  return parsed;
5300
5339
  }
5301
5340
  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`).");
5341
+ throw new Error(
5342
+ "agents accept-prefs set: pass at least one of --pricing-model / --currency / --max-active / --from-json (to remove prefs entirely use `accept-prefs clear`)."
5343
+ );
5303
5344
  }
5304
5345
  const prefs = {};
5305
5346
  if (opts.pricingModel && opts.pricingModel.length > 0) {
@@ -5314,6 +5355,12 @@ function buildAcceptPrefs(opts) {
5314
5355
  if (opts.currency && opts.currency.length > 0) {
5315
5356
  prefs.currencies = opts.currency.map(parseCurrencySpec);
5316
5357
  }
5358
+ if (opts.maxActive !== void 0) {
5359
+ if (!/^\d+$/.test(opts.maxActive)) {
5360
+ throw new Error(`agents accept-prefs set: --max-active must be a non-negative integer (got '${opts.maxActive}')`);
5361
+ }
5362
+ prefs.maxActiveDelegations = Number(opts.maxActive);
5363
+ }
5317
5364
  return prefs;
5318
5365
  }
5319
5366
  function parseCurrencySpec(spec) {
@@ -5332,12 +5379,16 @@ function parseCurrencySpec(spec) {
5332
5379
  };
5333
5380
  }
5334
5381
  function printAcceptPrefs(prefs) {
5335
- if (!prefs || (prefs.pricingModels?.length ?? 0) === 0 && (prefs.currencies?.length ?? 0) === 0) {
5382
+ if (!prefs || (prefs.pricingModels?.length ?? 0) === 0 && (prefs.currencies?.length ?? 0) === 0 && prefs.maxActiveDelegations === void 0) {
5336
5383
  console.log(import_chalk3.default.dim("(no accept-prefs published \u2014 this agent accepts any offer terms)"));
5337
5384
  return;
5338
5385
  }
5339
5386
  const models = prefs.pricingModels ?? [];
5340
5387
  console.log(`${import_chalk3.default.bold("Pricing models:")} ${models.length > 0 ? models.join(", ") : import_chalk3.default.dim("any")}`);
5388
+ if (prefs.maxActiveDelegations !== void 0) {
5389
+ const cap = prefs.maxActiveDelegations === 0 ? `0 ${import_chalk3.default.red("(closed for new offers \u2014 busy)")}` : String(prefs.maxActiveDelegations);
5390
+ console.log(`${import_chalk3.default.bold("Max active:")} ${cap}`);
5391
+ }
5341
5392
  const currencies = prefs.currencies ?? [];
5342
5393
  if (currencies.length === 0) {
5343
5394
  console.log(`${import_chalk3.default.bold("Currencies:")} ${import_chalk3.default.dim("any (no amount bounds)")}`);
@@ -5627,16 +5678,21 @@ async function probe(api, did) {
5627
5678
  const asOfMs = new Date(summary.asOf).getTime();
5628
5679
  const lastMs = summary.lastEventAt ? new Date(summary.lastEventAt).getTime() : null;
5629
5680
  const ageSeconds = lastMs !== null ? Math.max(0, Math.floor((asOfMs - lastMs) / 1e3)) : null;
5681
+ const seenMs = summary.lastSeenAt ? new Date(summary.lastSeenAt).getTime() : null;
5682
+ const seenAgeSeconds = seenMs !== null ? Math.max(0, Math.floor((asOfMs - seenMs) / 1e3)) : null;
5630
5683
  activity = {
5631
5684
  lastEventAt: summary.lastEventAt,
5632
5685
  asOf: summary.asOf,
5633
5686
  ageSeconds,
5687
+ lastSeenAt: summary.lastSeenAt ?? null,
5688
+ seenAgeSeconds,
5634
5689
  inboxStreamActive: summary.inboxStreamActive
5635
5690
  };
5636
5691
  } catch {
5637
5692
  return { ...base, verdict: "UNKNOWN", reason: "DID resolves but the server does not expose an activity summary \u2014 cannot assess liveness" };
5638
5693
  }
5639
5694
  const recentlyActive = activity.ageSeconds !== null && activity.ageSeconds !== void 0 && activity.ageSeconds <= LISTENING_THRESHOLD_SECONDS;
5695
+ const recentlySeen = activity.seenAgeSeconds !== null && activity.seenAgeSeconds !== void 0 && activity.seenAgeSeconds <= LISTENING_THRESHOLD_SECONDS;
5640
5696
  const inboxStreamActive = activity.inboxStreamActive === true;
5641
5697
  const result = { ...base, activity };
5642
5698
  if (inboxStreamActive) {
@@ -5646,6 +5702,14 @@ async function probe(api, did) {
5646
5702
  reason: "inbox-stream attached (active SSE subscription on the central server) \u2014 protocol-reachable; worker is actively waiting for envelopes"
5647
5703
  };
5648
5704
  }
5705
+ if (recentlySeen) {
5706
+ const seenStr = activity.seenAgeSeconds !== void 0 && activity.seenAgeSeconds !== null ? `${activity.seenAgeSeconds}s ago` : "recently";
5707
+ return {
5708
+ ...result,
5709
+ verdict: "LISTENING",
5710
+ reason: `recently seen (signed API request ${seenStr}) \u2014 agent is polling the server (cron-mode worker)`
5711
+ };
5712
+ }
5649
5713
  if (recentlyActive) {
5650
5714
  const ageStr = activity.ageSeconds !== void 0 && activity.ageSeconds !== null ? `${activity.ageSeconds}s ago` : "recently";
5651
5715
  return {
@@ -5655,7 +5719,8 @@ async function probe(api, did) {
5655
5719
  };
5656
5720
  }
5657
5721
  const ageNote = activity.lastEventAt === null ? "no events ever" : `last event ${activity.ageSeconds}s ago`;
5658
- return { ...result, verdict: "DORMANT", reason: `no open inbox stream and ${ageNote} \u2014 agent appears idle (cross-check before relying on it)` };
5722
+ const seenNote = activity.lastSeenAt === null || activity.lastSeenAt === void 0 ? "never seen polling" : `last seen ${activity.seenAgeSeconds}s ago`;
5723
+ return { ...result, verdict: "DORMANT", reason: `no open inbox stream, ${seenNote}, and ${ageNote} \u2014 agent appears idle (cross-check before relying on it)` };
5659
5724
  }
5660
5725
  function formatDoctorReport(r) {
5661
5726
  const lines = [];
@@ -5675,6 +5740,11 @@ function formatDoctorReport(r) {
5675
5740
  const ageStr = ageMin !== null && ageMin > 0 ? `${ageMin}m ago` : `${r.activity.ageSeconds}s ago`;
5676
5741
  lines.push(`${import_chalk8.default.dim("Activity:")} last event ${ageStr} (${r.activity.lastEventAt})`);
5677
5742
  }
5743
+ if (r.activity.lastSeenAt !== null && r.activity.lastSeenAt !== void 0) {
5744
+ const seenMin = r.activity.seenAgeSeconds !== null && r.activity.seenAgeSeconds !== void 0 ? Math.floor(r.activity.seenAgeSeconds / 60) : null;
5745
+ const seenStr = seenMin !== null && seenMin > 0 ? `${seenMin}m ago` : `${r.activity.seenAgeSeconds}s ago`;
5746
+ lines.push(`${import_chalk8.default.dim("Last seen:")} signed request ${seenStr} (${r.activity.lastSeenAt})`);
5747
+ }
5678
5748
  if (r.activity.inboxStreamActive === true) {
5679
5749
  lines.push(`${import_chalk8.default.dim("Inbox SSE:")} ${import_chalk8.default.green("\u2713")} active subscription on the central server`);
5680
5750
  }
@@ -6161,7 +6231,10 @@ var GUIDE_SECTIONS = [
6161
6231
  "",
6162
6232
  " heyarp agents accept-prefs set <your-did> \\",
6163
6233
  " --pricing-model flat \\",
6164
- ' --currency "<caip19-asset-id>,<min>,<max>" # bounds optional + per-currency',
6234
+ ' --currency "<caip19-asset-id>,<min>,<max>" \\',
6235
+ " --max-active 3",
6236
+ " # --currency bounds are optional + per-currency; --max-active is the",
6237
+ " # capacity cap (max in-flight delegations); 0 = busy (closed).",
6165
6238
  " heyarp agents accept-prefs show <your-did> # verify what is live",
6166
6239
  ' heyarp agents accept-prefs clear <your-did> # back to "anything reaches me"',
6167
6240
  "",
@@ -6170,7 +6243,8 @@ var GUIDE_SECTIONS = [
6170
6243
  " re-offer with matching terms."
6171
6244
  ],
6172
6245
  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.",
6246
+ "Do NOT treat accept-prefs as a guarantee of good offers \u2014 they filter currency/model/amount/capacity only; scope-vs-price judgment is still YOUR accept/decline decision.",
6247
+ "Set --max-active to what you can actually process in parallel \u2014 excess offers bounce server-side with DELEGATION_CAPACITY_EXCEEDED (transient; buyers are told to retry) instead of rotting unanswered in your funnel.",
6174
6248
  "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
6249
  "Remember bounds are per-currency and inclusive; an offer exactly at min or max passes."
6176
6250
  ]
@@ -6220,7 +6294,8 @@ var GUIDE_SECTIONS = [
6220
6294
  "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.",
6221
6295
  "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).",
6222
6296
  "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)."
6297
+ "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).",
6298
+ "DELEGATION_CAPACITY_EXCEEDED is the OPPOSITE: your terms are fine, the worker is just full (or closed with max-active 0). Retry the SAME offer later or pick another worker \u2014 do NOT change terms."
6224
6299
  ],
6225
6300
  crossRefs: ["A one-shot buyer facade (`heyarp quick-job`) is planned. For now, drive the cycle with the per-transition commands below."]
6226
6301
  },
@@ -6553,6 +6628,10 @@ var GUIDE_SECTIONS = [
6553
6628
  " receipt send-payee-sig <buyer> --delegation-id <id> --auto --cluster-tag",
6554
6629
  " <0|1>`, which resolves condition_hash from the on-chain lock (no manual",
6555
6630
  " hashes). Do NOT re-run `receipt propose` (the receipt already exists).",
6631
+ " \u2022 `DELEGATION_CAPACITY_EXCEEDED` at `delegation offer` \u2192 the worker is at",
6632
+ " its published max-active cap (details show current/max) or closed",
6633
+ " (max-active 0). Transient: retry the same offer later, or pick",
6634
+ " another worker. Your terms are fine \u2014 do not change them.",
6556
6635
  " \u2022 `DELEGATION_PRICING_MISMATCH` at `delegation offer` \u2192 the recipient's",
6557
6636
  " published accept-prefs reject one term; `details` names it ({reason,",
6558
6637
  " accepted, offered}). Check `heyarp agents accept-prefs show <worker-did>`,",
@@ -8645,7 +8724,7 @@ async function main() {
8645
8724
  registerReceiptsCommand(program);
8646
8725
  registerWalletCommands(program);
8647
8726
  registerSettlementCommands(program);
8648
- (0, import_shield2.installMiddleware)(program);
8727
+ (0, import_shield4.installMiddleware)(program);
8649
8728
  try {
8650
8729
  await program.parseAsync(process.argv);
8651
8730
  } catch (err) {