@metamynd/agentsafe-signer 0.20.0 → 0.20.3

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/daemon.mjs CHANGED
@@ -84,10 +84,10 @@ export function buildServiceCallMessage({ action, authorizationId, fields, nonce
84
84
  export const DEFAULT_CHECKPOINT_INTERVAL_MS = 15 * 60 * 1000;
85
85
 
86
86
  // Matches agentsafe-guard.mjs's own REPORTABLE_LOCAL_DECISIONS exactly (LOCAL_DECISIONS the
87
- // backend's /policy/decisions/local actually accepts) — this daemon must never sign a
88
- // 'quarantine'/'suspend' or other value here just because a caller asked; only a genuine
89
- // local-first verdict shape is a valid thing to have signed under this op.
90
- const REPORTABLE_LOCAL_DECISIONS = new Set(['allow', 'observe', 'block', 'escalate']);
87
+ // backend's /policy/decisions/local accepts) — this daemon never signs another value here just because a caller asked;
88
+ // only a genuine local-first verdict shape, or (since 0.20.3, N-4) a containment refusal the guard gave from the
89
+ // server's own `contained` flag, is a valid thing to have signed under this op. Never 'decommission'.
90
+ const REPORTABLE_LOCAL_DECISIONS = new Set(['allow', 'observe', 'block', 'escalate', 'suspend', 'quarantine']);
91
91
 
92
92
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
93
93
  const HEX_RE = /^[0-9a-f]+$/i;
@@ -390,8 +390,7 @@ export class SignerDaemon {
390
390
 
391
391
  /**
392
392
  * An agent settling its OWN hold that nobody has claimed (MAGP §8.7.4): capture it, or void it. The issuer accepts an
393
- * unclaimed settlement only from that hold's agent, a counterparty the owner registered, or — for an open testnet owner —
394
- * anyone. Built from validated fields under the fixed MAGP-SETTLE-v1 prefix, which no other message this daemon signs
393
+ * unclaimed settlement only from that hold's agent or a counterparty the owner registered, on every owner. Built from validated fields under the fixed MAGP-SETTLE-v1 prefix, which no other message this daemon signs
395
394
  * starts with; it names the agent, so it is never a counterparty's signature (sign-service-call, MAGP-SERVICE-v1), and an
396
395
  * agent cannot use it to claim. Agent-role only.
397
396
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamynd/agentsafe-signer",
3
- "version": "0.20.0",
3
+ "version": "0.20.3",
4
4
  "description": "Local signer daemon for AgentSafe agent/service keys \u2014 the key never enters the calling guard's own process. See docs/design/agent-key-custody-local-signer-daemon-plan.md.",
5
5
  "type": "module",
6
6
  "main": "./daemon.mjs",
package/policy-core.mjs CHANGED
@@ -43,6 +43,57 @@ function riskFloorFor(mandate, target) {
43
43
  }
44
44
  return floor;
45
45
  }
46
+ var DEFAULT_AMOUNT_SHARE_HIGH = 0.7;
47
+ function grantsFor(mandate, target) {
48
+ return (mandate?.permission ?? []).filter((p) => !!p && typeof p === "object" && (p.target ?? mandate?.target) === target);
49
+ }
50
+ function perTxnCapFor(mandate, target) {
51
+ let cap = null;
52
+ for (const p of grantsFor(mandate, target)) {
53
+ for (const c of p.constraint ?? []) {
54
+ if (c?.leftOperand !== "mm:payAmount") continue;
55
+ const n = Number(c.rightOperand);
56
+ if (Number.isFinite(n) && n > 0 && (cap === null || n < cap)) cap = n;
57
+ }
58
+ }
59
+ return cap;
60
+ }
61
+ function hasMerchantAllowList(mandate, target) {
62
+ return grantsFor(mandate, target).some((p) => (p.constraint ?? []).some((c) => c?.leftOperand === "mm:merchant"));
63
+ }
64
+ function riskSignalSettings(mandate, target) {
65
+ const grants = grantsFor(mandate, target);
66
+ if (grants.length === 0) return { amountShare: DEFAULT_AMOUNT_SHARE_HIGH, newMerchant: true };
67
+ let amountShare = null;
68
+ let newMerchant = false;
69
+ for (const p of grants) {
70
+ const s = p.riskSignals;
71
+ if (s === false) continue;
72
+ const cfg = s && typeof s === "object" ? s : {};
73
+ const share = cfg.amountShare === false ? null : cfg.amountShare === void 0 ? DEFAULT_AMOUNT_SHARE_HIGH : Number(cfg.amountShare);
74
+ if (share !== null && Number.isFinite(share) && share > 0 && share <= 1 && (amountShare === null || share < amountShare)) amountShare = share;
75
+ if (cfg.newMerchant !== false) newMerchant = true;
76
+ }
77
+ return { amountShare, newMerchant };
78
+ }
79
+ function riskSignalsFor(mandate, target, amount, opts = {}) {
80
+ const out = [];
81
+ const tier = riskFloorFor(mandate, target);
82
+ if (tier) out.push({ signal: "owner-tier", level: tier, detail: `the owner's risk tier for ${target}` });
83
+ const settings = riskSignalSettings(mandate, target);
84
+ const n = typeof amount === "number" ? amount : Number(amount);
85
+ const cap = perTxnCapFor(mandate, target);
86
+ if (settings.amountShare !== null && cap !== null && Number.isFinite(n) && n > 0 && n >= settings.amountShare * cap) {
87
+ out.push({ signal: "amount-share", level: "high", detail: `${Math.round(n / cap * 100)}% of the ${cap} per-transaction cap (review from ${Math.round(settings.amountShare * 100)}%)` });
88
+ }
89
+ if (opts.newMerchant === true && settings.newMerchant && !hasMerchantAllowList(mandate, target)) {
90
+ out.push({ signal: "new-merchant", level: "high", detail: "the first payment to this merchant" });
91
+ }
92
+ return out;
93
+ }
94
+ function effectiveRiskFloor(mandate, target, amount, opts = {}) {
95
+ return maxRisk(...riskSignalsFor(mandate, target, amount, opts).map((s) => s.level));
96
+ }
46
97
  function requiresPayloadBindingFor(mandate, target) {
47
98
  if (!mandate) return false;
48
99
  for (const p of mandate.permission ?? []) {
@@ -793,6 +844,7 @@ export {
793
844
  AUTH_MESSAGE_V2_TAG,
794
845
  CATALOGUED_ATOMS,
795
846
  CONTEXT_UNVERIFIABLE,
847
+ DEFAULT_AMOUNT_SHARE_HIGH,
796
848
  JURISDICTION_ATOM,
797
849
  MODES_BY_RANK,
798
850
  MODE_RANK,
@@ -815,10 +867,12 @@ export {
815
867
  canAuthorize,
816
868
  contextFieldProblem,
817
869
  documentEnforcesJurisdiction,
870
+ effectiveRiskFloor,
818
871
  evaluate,
819
872
  evaluateBoundStandards,
820
873
  evaluateMandate,
821
874
  evaluateStandardRules,
875
+ hasMerchantAllowList,
822
876
  isAuthorityFailure,
823
877
  isOperatingMode,
824
878
  isProvenance,
@@ -829,6 +883,7 @@ export {
829
883
  moreRestrictive,
830
884
  normalizeRiskLevel,
831
885
  operatingModeGate,
886
+ perTxnCapFor,
832
887
  provenanceOf,
833
888
  releaseHold,
834
889
  remainingBudget,
@@ -836,6 +891,8 @@ export {
836
891
  requiredContextOf,
837
892
  requiresPayloadBindingFor,
838
893
  riskFloorFor,
894
+ riskSignalSettings,
895
+ riskSignalsFor,
839
896
  sumEventField,
840
897
  validateMolecules
841
898
  };