@fidacy/openclaw-plugin 0.1.10 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.11 — 2026-07-03
4
+
5
+ - Config hot-reload (shared core) + actionable `payee_not_in_allowlist` DENY.
6
+
3
7
  ## 0.1.10 — 2026-07-03
4
8
 
5
9
  - Artifact tools gain the `conversation` kind (chatbot session digests via
package/dist/index.js CHANGED
@@ -4415,6 +4415,19 @@ var DevFidacyCore = class {
4415
4415
  this.spent += r.amount;
4416
4416
  }
4417
4417
  }
4418
+ /**
4419
+ * Swap the active mandate (shell config hot-reload: edit config.json, the next
4420
+ * call picks it up, no host restart). Decision state is REBUILT from the audit
4421
+ * log under the new window/currency, so a reload can never reset the BEC
4422
+ * invoice dedup or undercount spend: a paid invoice stays paid, spent is
4423
+ * recomputed.
4424
+ */
4425
+ setMandate(m) {
4426
+ this.mandate = m;
4427
+ this.spent = 0;
4428
+ this.claimedInvoices.clear();
4429
+ this.rehydrate();
4430
+ }
4418
4431
  async getMandate() {
4419
4432
  return this.mandate;
4420
4433
  }
@@ -4557,6 +4570,9 @@ var FileAuditStore = class {
4557
4570
  const digest = sha256(stableStringify({ decisionId: decision.decisionId, status: decision.status, request: decision.request, violatedRule: decision.violatedRule ?? null }));
4558
4571
  const hash = sha256(`${prevHash}|${digest}|${seq}|${ts}`);
4559
4572
  const record2 = { seq, decisionId: decision.decisionId, status: decision.status, subject: decision.subject, digest, prevHash, hash, ts };
4573
+ if (typeof decision.request?.purpose === "string" && decision.request.purpose.trim()) {
4574
+ record2.purpose = decision.request.purpose.slice(0, 500);
4575
+ }
4560
4576
  if (decision.status === "ALLOW") {
4561
4577
  const req = decision.request;
4562
4578
  if (typeof req?.amount === "number" && Number.isFinite(req.amount))
@@ -4590,6 +4606,9 @@ var FileAuditStore = class {
4590
4606
  }
4591
4607
  };
4592
4608
 
4609
+ // ../mcp/src/core.ts
4610
+ import { statSync } from "node:fs";
4611
+
4593
4612
  // ../mcp/src/config.ts
4594
4613
  import { randomUUID as randomUUID2 } from "node:crypto";
4595
4614
  import { homedir } from "node:os";
@@ -4671,7 +4690,25 @@ function resolveMandateRules(cfg) {
4671
4690
  }
4672
4691
 
4673
4692
  // ../mcp/src/telemetry.ts
4674
- var CLIENT_VERSION = true ? "0.1.10" : "dev";
4693
+ var CLIENT_VERSION = true ? "0.1.12" : "dev";
4694
+ function bandOf(amount) {
4695
+ if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
4696
+ if (amount < 10) return "lt10";
4697
+ if (amount < 100) return "10_99";
4698
+ if (amount < 1e3) return "100_999";
4699
+ if (amount < 1e4) return "1k_10k";
4700
+ if (amount < 1e5) return "10k_100k";
4701
+ return "gte100k";
4702
+ }
4703
+ function capRatioOf(amount, perTxMax) {
4704
+ if (typeof amount !== "number" || typeof perTxMax !== "number") return void 0;
4705
+ if (!Number.isFinite(amount) || !Number.isFinite(perTxMax) || perTxMax <= 0 || amount <= perTxMax) return void 0;
4706
+ const r = amount / perTxMax;
4707
+ if (r < 2) return "1_2x";
4708
+ if (r < 5) return "2_5x";
4709
+ if (r < 10) return "5_10x";
4710
+ return "gt10x";
4711
+ }
4675
4712
  var currentShell = "mcp";
4676
4713
  function setTelemetryShell(shell) {
4677
4714
  currentShell = shell;
@@ -4704,9 +4741,17 @@ function resultOf(status, violatedRule) {
4704
4741
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
4705
4742
  return "deny_scope";
4706
4743
  }
4707
- function record(type, result) {
4744
+ function record(type, result, band, capRatio) {
4708
4745
  if (!telemetryEnabled()) return;
4709
- buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, shell: currentShell, ...result ? { result } : {} });
4746
+ buffer.push({
4747
+ type,
4748
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4749
+ client_version: CLIENT_VERSION,
4750
+ shell: currentShell,
4751
+ ...result ? { result } : {},
4752
+ ...band ? { band } : {},
4753
+ ...capRatio ? { cap_ratio: capRatio } : {}
4754
+ });
4710
4755
  if (!flushTimer) {
4711
4756
  flushTimer = setTimeout(() => {
4712
4757
  void flush();
@@ -4716,7 +4761,7 @@ function record(type, result) {
4716
4761
  }
4717
4762
  var recordInstall = () => record("install");
4718
4763
  var recordAgentActive = () => record("agent_active");
4719
- var recordDecision = (status, violatedRule) => record("decision", resultOf(status, violatedRule));
4764
+ var recordDecision = (status, violatedRule, amount, perTxMax) => record("decision", resultOf(status, violatedRule), bandOf(amount), capRatioOf(amount, perTxMax));
4720
4765
  var recordUpgradeIntent = () => record("upgrade_intent");
4721
4766
  async function flush() {
4722
4767
  if (flushTimer) {
@@ -4779,11 +4824,34 @@ function makeCore() {
4779
4824
  if (!url || !key) throw new Error("FIDACY_MODE=http requires FIDACY_API_URL and FIDACY_API_KEY");
4780
4825
  return new HttpFidacyCore(url, key, process.env.FIDACY_PUBLIC_KEY_PEM ?? "");
4781
4826
  }
4782
- return new DevFidacyCore({
4783
- mandate: buildMandate(),
4827
+ let activeMandate = buildMandate();
4828
+ const dev = new DevFidacyCore({
4829
+ mandate: activeMandate,
4784
4830
  auditLogPath: auditLogPath(),
4785
- onDecision: (d) => recordDecision(d.status, d.violatedRule)
4831
+ onDecision: (d) => recordDecision(d.status, d.violatedRule, d.request?.amount, activeMandate?.allow?.perTxMax)
4786
4832
  });
4833
+ const mtimeOf = () => statSync(configPath(), { throwIfNoEntry: false })?.mtimeMs ?? 0;
4834
+ let seenMtime = mtimeOf();
4835
+ const maybeReload = () => {
4836
+ const m = mtimeOf();
4837
+ if (m !== seenMtime) {
4838
+ seenMtime = m;
4839
+ activeMandate = buildMandate();
4840
+ dev.setMandate(activeMandate);
4841
+ }
4842
+ };
4843
+ return {
4844
+ getMandate: () => {
4845
+ maybeReload();
4846
+ return dev.getMandate();
4847
+ },
4848
+ decide: (req, subject) => {
4849
+ maybeReload();
4850
+ return dev.decide(req, subject);
4851
+ },
4852
+ getProof: (decisionId) => dev.getProof(decisionId),
4853
+ publicKey: () => dev.publicKey()
4854
+ };
4787
4855
  }
4788
4856
 
4789
4857
  // ../mcp/src/upgrade.ts
@@ -4799,7 +4867,7 @@ function requestUpgrade() {
4799
4867
  }
4800
4868
 
4801
4869
  // ../mcp/src/provision.ts
4802
- var CLIENT_VERSION2 = true ? "0.1.10" : "dev";
4870
+ var CLIENT_VERSION2 = true ? "0.1.12" : "dev";
4803
4871
  function provisionEnabled() {
4804
4872
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
4805
4873
  return !(v === "1" || v === "true" || v === "yes");
@@ -4962,7 +5030,7 @@ async function postOnce(fetchImpl, url, headers, payload, timeoutMs) {
4962
5030
  body: payload,
4963
5031
  signal: controller.signal
4964
5032
  });
4965
- } catch (err) {
5033
+ } catch {
4966
5034
  const aborted = controller.signal.aborted;
4967
5035
  throw new AssessError({ type: aborted ? "timeout" : "network_error", status: 0 });
4968
5036
  } finally {
@@ -5118,7 +5186,7 @@ var index_default = defineToolPlugin({
5118
5186
  async execute(params, config) {
5119
5187
  const d = await boot().decide(params, subjectOf(config));
5120
5188
  const base = d.status === "ALLOW" ? `ALLOW (decision ${d.decisionId})${params.invoiceRef ? ` for invoice ${params.invoiceRef}` : ""}. To settle, call the executor with the SAME payee, amount, currency, and idempotencyKey, and set "grant" to EXACTLY this signed value:
5121
- ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}. No grant issued, this payment cannot proceed. The denial itself is recorded in the tamper-evident, hash-chained audit: call get_audit_proof with decisionId ${d.decisionId} for the proof of what was blocked.`;
5189
+ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}. No grant issued, this payment cannot proceed. The denial itself is recorded in the tamper-evident, hash-chained audit: call get_audit_proof with decisionId ${d.decisionId} for the proof of what was blocked.${d.violatedRule?.startsWith("payee_not_in_allowlist") ? ` If the user trusts this payee, add "${params.payee}" to mandate.payees in ~/.fidacy/config.json and retry: the firewall picks the change up on the next call, no restart needed.` : ""}`;
5122
5190
  const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
5123
5191
  const message = nudge ? `${base} ${nudge}` : base;
5124
5192
  return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
@@ -3,7 +3,7 @@
3
3
  "name": "Fidacy \u2014 Payment Firewall",
4
4
  "description": "A signed, independently-verifiable verdict on every money-moving agent action. Blocks wrong/lookalike payee, over-cap, and duplicate-invoice fraud before money moves. Non-custodial, local-first, deny-by-default.",
5
5
  "icon": "https://fidacy.com/logo.png",
6
- "version": "0.1.10",
6
+ "version": "0.1.11",
7
7
  "contracts": {
8
8
  "tools": [
9
9
  "request_payment",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/openclaw-plugin",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Fidacy payment firewall as a native OpenClaw plugin: signed, verifiable verdicts on every money-moving agent action, in-process (no MCP subprocess).",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://fidacy.com",