@fidacy/openclaw-plugin 0.4.2 → 0.5.0

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/index.js CHANGED
@@ -1310,8 +1310,8 @@ function CreateObject(types, value) {
1310
1310
  }
1311
1311
  function FromUnionKey(types, value) {
1312
1312
  const flattened = Flatten(types);
1313
- const record2 = TryBuildRecord(flattened, value);
1314
- return IsSchema(record2) ? record2 : CreateObject(flattened, value);
1313
+ const record3 = TryBuildRecord(flattened, value);
1314
+ return IsSchema(record3) ? record3 : CreateObject(flattened, value);
1315
1315
  }
1316
1316
 
1317
1317
  // ../../node_modules/.pnpm/typebox@1.1.39/node_modules/typebox/build/type/engine/record/from_key.mjs
@@ -4224,6 +4224,331 @@ __export(typebox_exports, {
4224
4224
  // src/index.ts
4225
4225
  import { defineToolPlugin } from "openclaw/plugin-sdk/tool-plugin";
4226
4226
 
4227
+ // src/observer.ts
4228
+ import { createHash as createHash2 } from "node:crypto";
4229
+
4230
+ // src/evidence.ts
4231
+ import { createHash } from "node:crypto";
4232
+ import { appendFileSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
4233
+ import { homedir } from "node:os";
4234
+ import { join } from "node:path";
4235
+ var GENESIS = "fidacy.observer.v1";
4236
+ var sha256 = (s) => createHash("sha256").update(s).digest("hex");
4237
+ function canonical(obj) {
4238
+ const keys = Object.keys(obj).sort();
4239
+ return "{" + keys.map((k) => `${JSON.stringify(k)}:${JSON.stringify(obj[k])}`).join(",") + "}";
4240
+ }
4241
+ function chainStep(prevHead, a) {
4242
+ return sha256(`${prevHead}|${sha256(canonical({ category: a.category, i: a.i, tool: a.tool, ts: a.ts }))}`);
4243
+ }
4244
+ function genesisHead() {
4245
+ return sha256(GENESIS);
4246
+ }
4247
+ function fidacyDir() {
4248
+ return join(process.env.FIDACY_HOME ?? join(homedir(), ".fidacy"), "sessions");
4249
+ }
4250
+ function writeSessionLog(sessionId, actions, head, meta) {
4251
+ try {
4252
+ const dir = fidacyDir();
4253
+ mkdirSync(dir, { recursive: true, mode: 448 });
4254
+ const safe = sessionId.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80) || "session";
4255
+ const path = join(dir, `${safe}.json`);
4256
+ const body = {
4257
+ v: "fidacy.observer.v1",
4258
+ recipe: 'h_0 = sha256("fidacy.observer.v1"); h_i = sha256(h_prev + "|" + sha256(canonical(action)))',
4259
+ sessionId,
4260
+ head,
4261
+ ...meta,
4262
+ actions
4263
+ };
4264
+ writeFileSync(path, JSON.stringify(body, null, 2), { mode: 384 });
4265
+ return path;
4266
+ } catch {
4267
+ return null;
4268
+ }
4269
+ }
4270
+ function queuePending(head, sessionId, at, total = 0, byCategory = {}) {
4271
+ try {
4272
+ const dir = fidacyDir();
4273
+ mkdirSync(dir, { recursive: true, mode: 448 });
4274
+ appendFileSync(join(dir, "pending-anchors.jsonl"), JSON.stringify({ head, sessionId, at, total, byCategory }) + "\n", {
4275
+ mode: 384
4276
+ });
4277
+ } catch {
4278
+ }
4279
+ }
4280
+ function takePending(limit = 50) {
4281
+ const dir = fidacyDir();
4282
+ const path = join(dir, "pending-anchors.jsonl");
4283
+ const taken = join(dir, "pending-anchors.taking");
4284
+ try {
4285
+ renameSync(path, taken);
4286
+ } catch {
4287
+ return [];
4288
+ }
4289
+ try {
4290
+ const lines = readFileSync(taken, "utf8").split("\n").filter(Boolean);
4291
+ const out = [];
4292
+ for (const line of lines.slice(-limit)) {
4293
+ try {
4294
+ const p = JSON.parse(line);
4295
+ if (typeof p.head === "string" && /^[0-9a-f]{64}$/.test(p.head)) out.push(p);
4296
+ } catch {
4297
+ }
4298
+ }
4299
+ return out;
4300
+ } catch {
4301
+ return [];
4302
+ } finally {
4303
+ try {
4304
+ writeFileSync(taken, "");
4305
+ } catch {
4306
+ }
4307
+ }
4308
+ }
4309
+ function verifyUrl(head) {
4310
+ return `https://fidacy.com/verify?sha256=${head}`;
4311
+ }
4312
+ var CODE = {
4313
+ money: "m",
4314
+ shell: "s",
4315
+ file: "f",
4316
+ network: "net",
4317
+ message: "msg",
4318
+ other: "o"
4319
+ };
4320
+ function sessionLabel(sessionId, total, byCategory) {
4321
+ const id = sessionId.replace(/[^A-Za-z0-9-]/g, "").slice(0, 8) || "unknown";
4322
+ const parts = [`session:${id}`, `n=${total}`];
4323
+ for (const [cat, code] of Object.entries(CODE)) {
4324
+ const n = byCategory[cat] ?? 0;
4325
+ if (n > 0) parts.push(`${code}=${n}`);
4326
+ }
4327
+ return parts.join(" ").slice(0, 120);
4328
+ }
4329
+
4330
+ // src/observer.ts
4331
+ var EXACT = {
4332
+ // Fidacy's own money tools, so the report counts what we ourselves gated.
4333
+ request_payment: "money",
4334
+ assess_action: "money",
4335
+ // The common OpenClaw / agent-host tool vocabulary.
4336
+ bash: "shell",
4337
+ shell: "shell",
4338
+ exec: "shell",
4339
+ run_command: "shell",
4340
+ code_mode_exec: "shell",
4341
+ read: "file",
4342
+ read_file: "file",
4343
+ write: "file",
4344
+ write_file: "file",
4345
+ edit: "file",
4346
+ edit_file: "file",
4347
+ apply_patch: "file",
4348
+ glob: "file",
4349
+ grep: "file",
4350
+ ls: "file",
4351
+ fetch: "network",
4352
+ web_fetch: "network",
4353
+ web_search: "network",
4354
+ http_request: "network",
4355
+ browser: "network",
4356
+ send_message: "message",
4357
+ reply: "message",
4358
+ send_email: "message"
4359
+ };
4360
+ var FUZZY = [
4361
+ ["payment", "money"],
4362
+ ["invoice", "money"],
4363
+ ["transfer", "money"],
4364
+ ["charge", "money"],
4365
+ ["file", "file"],
4366
+ ["patch", "file"],
4367
+ ["write", "file"],
4368
+ ["read", "file"],
4369
+ ["fetch", "network"],
4370
+ ["http", "network"],
4371
+ ["browser", "network"],
4372
+ ["search", "network"],
4373
+ ["exec", "shell"],
4374
+ ["command", "shell"],
4375
+ ["shell", "shell"],
4376
+ ["mail", "message"],
4377
+ ["message", "message"],
4378
+ ["send", "message"]
4379
+ ];
4380
+ function classify(toolName, derivedPaths) {
4381
+ const name = toolName.toLowerCase();
4382
+ const exact = EXACT[name];
4383
+ if (exact) return exact;
4384
+ if (derivedPaths && derivedPaths.length > 0) return "file";
4385
+ for (const [needle, category] of FUZZY) if (name.includes(needle)) return category;
4386
+ return "other";
4387
+ }
4388
+ function fingerprint(toolName, derivedPaths) {
4389
+ const target = derivedPaths && derivedPaths.length > 0 ? [...derivedPaths].sort().join("\0") : toolName;
4390
+ return createHash2("sha256").update(`${toolName}\0${target}`).digest("hex").slice(0, 16);
4391
+ }
4392
+ var ACTION_LOG_CAP = 5e3;
4393
+ function newLedger(sessionId, now) {
4394
+ return {
4395
+ sessionId,
4396
+ startedAt: now,
4397
+ byCategory: /* @__PURE__ */ new Map(),
4398
+ total: 0,
4399
+ toolsSeen: /* @__PURE__ */ new Set(),
4400
+ head: genesisHead(),
4401
+ actions: [],
4402
+ truncated: false
4403
+ };
4404
+ }
4405
+ function record(ledger, toolName, derivedPaths, failed = false, at = Date.now()) {
4406
+ const category = classify(toolName, derivedPaths);
4407
+ let tally = ledger.byCategory.get(category);
4408
+ if (!tally) {
4409
+ tally = { calls: 0, failures: 0, targets: /* @__PURE__ */ new Set() };
4410
+ ledger.byCategory.set(category, tally);
4411
+ }
4412
+ tally.calls += 1;
4413
+ if (failed) tally.failures += 1;
4414
+ if (derivedPaths && derivedPaths.length > 0 && tally.targets.size < TARGET_CAP) {
4415
+ tally.targets.add(fingerprint(toolName, derivedPaths));
4416
+ }
4417
+ ledger.total += 1;
4418
+ if (ledger.toolsSeen.size < TOOLS_CAP) ledger.toolsSeen.add(toolName);
4419
+ const action = {
4420
+ i: ledger.total,
4421
+ category,
4422
+ tool: toolName,
4423
+ ts: new Date(at).toISOString()
4424
+ };
4425
+ ledger.head = chainStep(ledger.head, action);
4426
+ if (ledger.actions.length < ACTION_LOG_CAP) ledger.actions.push(action);
4427
+ else ledger.truncated = true;
4428
+ }
4429
+ var TARGET_CAP = 500;
4430
+ var TOOLS_CAP = 200;
4431
+ var ORDER = ["money", "shell", "file", "network", "message", "other"];
4432
+ var LABEL = {
4433
+ money: "moved money",
4434
+ shell: "ran commands",
4435
+ file: "touched files",
4436
+ network: "reached the network",
4437
+ message: "sent messages",
4438
+ other: "other"
4439
+ };
4440
+ function renderReport(ledger, now, activated, logPath) {
4441
+ if (ledger.total < MIN_ACTIONS_FOR_REPORT) return null;
4442
+ const minutes = Math.max(1, Math.round((now - ledger.startedAt) / 6e4));
4443
+ const lines = [];
4444
+ lines.push(
4445
+ `[fidacy] SESSION REPORT \xB7 ${ledger.total} agent actions watched over ${minutes} min \xB7 nothing left this machine`
4446
+ );
4447
+ for (const category of ORDER) {
4448
+ const tally = ledger.byCategory.get(category);
4449
+ if (!tally) continue;
4450
+ const capped = tally.targets.size >= TARGET_CAP ? "+" : "";
4451
+ const parts = [`${tally.calls} ${tally.calls === 1 ? "call" : "calls"}`];
4452
+ if (tally.targets.size > 0) parts.push(`${tally.targets.size}${capped} distinct`);
4453
+ if (tally.failures > 0) parts.push(`${tally.failures} failed`);
4454
+ lines.push(`[fidacy] ${LABEL[category].padEnd(20)} ${parts.join(" \xB7 ")}`);
4455
+ }
4456
+ const money2 = ledger.byCategory.get("money");
4457
+ if (!money2) {
4458
+ lines.push(`[fidacy] no money-moving action was attempted \xB7 the firewall stayed armed and idle`);
4459
+ }
4460
+ lines.push(`[fidacy] digest ${ledger.head}`);
4461
+ if (logPath) lines.push(`[fidacy] log ${logPath}${ledger.truncated ? " (partial: action cap reached)" : ""}`);
4462
+ lines.push(
4463
+ activated ? `[fidacy] anchored to the audit chain \xB7 verify: ${verifyUrl(ledger.head)}` : `[fidacy] activate free to anchor this digest so nobody can rewrite it later: https://fidacy.com/claim`
4464
+ );
4465
+ return lines.join("\n");
4466
+ }
4467
+ var MIN_ACTIONS_FOR_REPORT = 5;
4468
+ function installObserver(api, emit, isActivated, now = Date.now, anchor) {
4469
+ if (typeof api?.on !== "function") return;
4470
+ const ledgers = /* @__PURE__ */ new Map();
4471
+ const LEDGER_CAP = 50;
4472
+ const ledgerFor = (sessionId) => {
4473
+ let ledger = ledgers.get(sessionId);
4474
+ if (!ledger) {
4475
+ if (ledgers.size >= LEDGER_CAP) ledgers.delete(ledgers.keys().next().value);
4476
+ ledger = newLedger(sessionId, now());
4477
+ ledgers.set(sessionId, ledger);
4478
+ }
4479
+ return ledger;
4480
+ };
4481
+ api.on(
4482
+ "before_tool_call",
4483
+ (event, ctx) => {
4484
+ try {
4485
+ const toolName = typeof event.toolName === "string" ? event.toolName : "unknown";
4486
+ const paths = Array.isArray(event.derivedPaths) ? event.derivedPaths : void 0;
4487
+ const sessionId = sessionIdOf(ctx);
4488
+ record(ledgerFor(sessionId), toolName, paths, false);
4489
+ } catch {
4490
+ }
4491
+ },
4492
+ // Lowest priority on purpose: anything that actually decides should run first,
4493
+ // and we only want to count what the host was really about to do.
4494
+ { priority: 1 }
4495
+ );
4496
+ api.on("session_end", async (event, ctx) => {
4497
+ try {
4498
+ const sessionId = sessionIdOf(ctx) || (typeof event.sessionId === "string" ? event.sessionId : "");
4499
+ const ledger = ledgers.get(sessionId);
4500
+ if (!ledger) return;
4501
+ ledgers.delete(sessionId);
4502
+ if (ledger.total < MIN_ACTIONS_FOR_REPORT) return;
4503
+ const logPath = persist(ledger, now());
4504
+ let anchored = false;
4505
+ if (anchor) {
4506
+ anchored = await anchor(ledger.head, ledger.sessionId, rollup(ledger)).catch(() => false);
4507
+ }
4508
+ if (!anchored) queuePending(ledger.head, ledger.sessionId, now(), ledger.total, rollup(ledger));
4509
+ const report = renderReport(ledger, now(), anchored, logPath);
4510
+ if (report) emit(report);
4511
+ } catch {
4512
+ }
4513
+ });
4514
+ api.on("gateway_stop", () => drain());
4515
+ process.on("exit", () => drain());
4516
+ function drain() {
4517
+ try {
4518
+ for (const [id, ledger] of ledgers) {
4519
+ ledgers.delete(id);
4520
+ if (ledger.total < MIN_ACTIONS_FOR_REPORT) continue;
4521
+ const logPath = persist(ledger, now());
4522
+ queuePending(ledger.head, ledger.sessionId, now(), ledger.total, rollup(ledger));
4523
+ const report = renderReport(ledger, now(), false, logPath);
4524
+ if (report) emit(report);
4525
+ }
4526
+ } catch {
4527
+ }
4528
+ }
4529
+ function rollup(ledger) {
4530
+ const out = {};
4531
+ for (const [cat, tally] of ledger.byCategory) out[cat] = tally.calls;
4532
+ return out;
4533
+ }
4534
+ function persist(ledger, at) {
4535
+ return writeSessionLog(ledger.sessionId, ledger.actions, ledger.head, {
4536
+ startedAt: new Date(ledger.startedAt).toISOString(),
4537
+ endedAt: new Date(at).toISOString(),
4538
+ totalActions: ledger.total,
4539
+ truncated: ledger.truncated,
4540
+ byCategory: Object.fromEntries(
4541
+ [...ledger.byCategory].map(([k, v]) => [k, { calls: v.calls, failures: v.failures, distinct: v.targets.size }])
4542
+ )
4543
+ });
4544
+ }
4545
+ }
4546
+ function sessionIdOf(ctx) {
4547
+ if (!ctx) return "default";
4548
+ const id = ctx.sessionId ?? ctx.sessionKey;
4549
+ return typeof id === "string" && id.length > 0 ? id : "default";
4550
+ }
4551
+
4227
4552
  // ../firewall/dist/util.js
4228
4553
  function stableStringify(obj) {
4229
4554
  if (obj === null || typeof obj !== "object")
@@ -4253,7 +4578,7 @@ function publicKeyPem(publicKey) {
4253
4578
  function sign(privateKey, message) {
4254
4579
  return crypto.sign(null, Buffer.from(message, "utf8"), privateKey).toString("base64url");
4255
4580
  }
4256
- function sha256(input) {
4581
+ function sha2562(input) {
4257
4582
  return crypto.createHash("sha256").update(input).digest("hex");
4258
4583
  }
4259
4584
 
@@ -4482,10 +4807,10 @@ var DevFidacyCore = class {
4482
4807
  return decision;
4483
4808
  }
4484
4809
  async getProof(decisionId) {
4485
- const record2 = this.store.find(decisionId);
4486
- if (!record2)
4810
+ const record3 = this.store.find(decisionId);
4811
+ if (!record3)
4487
4812
  return null;
4488
- return { record: record2, chainIntact: this.store.intact(), verifiedAgainstPublicKey: this.pubPem };
4813
+ return { record: record3, chainIntact: this.store.intact(), verifiedAgainstPublicKey: this.pubPem };
4489
4814
  }
4490
4815
  async history(limit = 50) {
4491
4816
  const all = this.store.records();
@@ -4588,36 +4913,36 @@ var FileAuditStore = class {
4588
4913
  const prevHash = this.head();
4589
4914
  const seq = this.chain.length;
4590
4915
  const ts2 = decision.ts;
4591
- const digest = sha256(stableStringify({ decisionId: decision.decisionId, status: decision.status, request: decision.request, violatedRule: decision.violatedRule ?? null }));
4592
- const hash = sha256(`${prevHash}|${digest}|${seq}|${ts2}`);
4593
- const record2 = { seq, decisionId: decision.decisionId, status: decision.status, subject: decision.subject, digest, prevHash, hash, ts: ts2 };
4916
+ const digest = sha2562(stableStringify({ decisionId: decision.decisionId, status: decision.status, request: decision.request, violatedRule: decision.violatedRule ?? null }));
4917
+ const hash = sha2562(`${prevHash}|${digest}|${seq}|${ts2}`);
4918
+ const record3 = { seq, decisionId: decision.decisionId, status: decision.status, subject: decision.subject, digest, prevHash, hash, ts: ts2 };
4594
4919
  if (typeof decision.request?.purpose === "string" && decision.request.purpose.trim()) {
4595
- record2.purpose = decision.request.purpose.slice(0, 500);
4920
+ record3.purpose = decision.request.purpose.slice(0, 500);
4596
4921
  }
4597
4922
  if (typeof decision.request?.payee === "string" && decision.request.payee.trim()) {
4598
- record2.payee = decision.request.payee.slice(0, 200);
4923
+ record3.payee = decision.request.payee.slice(0, 200);
4599
4924
  }
4600
4925
  if (decision.status === "ALLOW") {
4601
4926
  const req = decision.request;
4602
4927
  if (typeof req?.amount === "number" && Number.isFinite(req.amount))
4603
- record2.amount = req.amount;
4928
+ record3.amount = req.amount;
4604
4929
  if (typeof req?.currency === "string")
4605
- record2.currency = req.currency;
4930
+ record3.currency = req.currency;
4606
4931
  const invoice = canonInvoice(req?.invoiceRef);
4607
4932
  if (invoice)
4608
- record2.invoiceRef = invoice;
4933
+ record3.invoiceRef = invoice;
4609
4934
  } else {
4610
4935
  if (decision.violatedRule)
4611
- record2.violatedRule = decision.violatedRule;
4936
+ record3.violatedRule = decision.violatedRule;
4612
4937
  const req = decision.request;
4613
4938
  if (typeof req?.amount === "number" && Number.isFinite(req.amount))
4614
- record2.amount = req.amount;
4939
+ record3.amount = req.amount;
4615
4940
  if (typeof req?.currency === "string")
4616
- record2.currency = req.currency;
4941
+ record3.currency = req.currency;
4617
4942
  }
4618
- fs.appendFileSync(this.path, JSON.stringify(record2) + "\n");
4619
- this.chain.push(record2);
4620
- return record2;
4943
+ fs.appendFileSync(this.path, JSON.stringify(record3) + "\n");
4944
+ this.chain.push(record3);
4945
+ return record3;
4621
4946
  }
4622
4947
  find(decisionId) {
4623
4948
  return this.chain.find((r) => r.decisionId === decisionId);
@@ -4629,7 +4954,7 @@ var FileAuditStore = class {
4629
4954
  intact() {
4630
4955
  let prev = "GENESIS";
4631
4956
  for (const r of this.chain) {
4632
- const expected = sha256(`${prev}|${r.digest}|${r.seq}|${r.ts}`);
4957
+ const expected = sha2562(`${prev}|${r.digest}|${r.seq}|${r.ts}`);
4633
4958
  if (expected !== r.hash || r.prevHash !== prev)
4634
4959
  return false;
4635
4960
  prev = r.hash;
@@ -4643,37 +4968,37 @@ import { statSync } from "node:fs";
4643
4968
 
4644
4969
  // ../mcp/src/config.ts
4645
4970
  import { randomUUID as randomUUID2 } from "node:crypto";
4646
- import { homedir } from "node:os";
4647
- import { join } from "node:path";
4971
+ import { homedir as homedir2 } from "node:os";
4972
+ import { join as join2 } from "node:path";
4648
4973
  import {
4649
4974
  existsSync,
4650
- mkdirSync,
4651
- readFileSync,
4652
- writeFileSync
4975
+ mkdirSync as mkdirSync2,
4976
+ readFileSync as readFileSync2,
4977
+ writeFileSync as writeFileSync2
4653
4978
  } from "node:fs";
4654
4979
  function hasEngineKey(keyOverride) {
4655
4980
  return Boolean((keyOverride ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim());
4656
4981
  }
4657
4982
  function configDir() {
4658
- return process.env.FIDACY_CONFIG_DIR ?? join(homedir(), ".fidacy");
4983
+ return process.env.FIDACY_CONFIG_DIR ?? join2(homedir2(), ".fidacy");
4659
4984
  }
4660
4985
  function configPath() {
4661
- return join(configDir(), "config.json");
4986
+ return join2(configDir(), "config.json");
4662
4987
  }
4663
4988
  function auditLogPath() {
4664
4989
  if (process.env.FIDACY_AUDIT_PATH) return process.env.FIDACY_AUDIT_PATH;
4665
- const dir = join(configDir(), "audit");
4990
+ const dir = join2(configDir(), "audit");
4666
4991
  try {
4667
- mkdirSync(dir, { recursive: true, mode: 448 });
4992
+ mkdirSync2(dir, { recursive: true, mode: 448 });
4668
4993
  } catch {
4669
4994
  }
4670
- return join(dir, "audit.log");
4995
+ return join2(dir, "audit.log");
4671
4996
  }
4672
4997
  function readConfig() {
4673
4998
  const p = configPath();
4674
4999
  if (!existsSync(p)) return null;
4675
5000
  try {
4676
- const raw = JSON.parse(readFileSync(p, "utf8"));
5001
+ const raw = JSON.parse(readFileSync2(p, "utf8"));
4677
5002
  if (!raw || typeof raw.anon_id !== "string") return null;
4678
5003
  return {
4679
5004
  anon_id: raw.anon_id,
@@ -4695,8 +5020,8 @@ function readConfig() {
4695
5020
  }
4696
5021
  function writeConfig(cfg) {
4697
5022
  const dir = configDir();
4698
- mkdirSync(dir, { recursive: true, mode: 448 });
4699
- writeFileSync(configPath(), JSON.stringify(cfg, null, 2), { mode: 384 });
5023
+ mkdirSync2(dir, { recursive: true, mode: 448 });
5024
+ writeFileSync2(configPath(), JSON.stringify(cfg, null, 2), { mode: 384 });
4700
5025
  }
4701
5026
  function ensureState() {
4702
5027
  const existing = readConfig();
@@ -4714,6 +5039,7 @@ function ensureState() {
4714
5039
  }
4715
5040
  return { config, firstRun: true };
4716
5041
  }
5042
+ var DEMO_PAYEE = "fidacy:demo";
4717
5043
  function resolveMandateRules(cfg) {
4718
5044
  const m = cfg?.mandate ?? {};
4719
5045
  const envList = (v) => v === void 0 ? void 0 : v.split(",").map((s) => s.trim()).filter(Boolean);
@@ -4729,8 +5055,9 @@ function resolveMandateRules(cfg) {
4729
5055
  return n;
4730
5056
  };
4731
5057
  const fileNum = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
5058
+ const payees = envList(process.env.FIDACY_ALLOW_PAYEES) ?? m.payees ?? [];
4732
5059
  return {
4733
- payees: envList(process.env.FIDACY_ALLOW_PAYEES) ?? m.payees ?? [],
5060
+ payees: payees.includes(DEMO_PAYEE) ? payees : [...payees, DEMO_PAYEE],
4734
5061
  categories: envList(process.env.FIDACY_ALLOW_CATEGORIES) ?? m.categories ?? ["*"],
4735
5062
  currency: process.env.FIDACY_CURRENCY ?? m.currency ?? "USD",
4736
5063
  perTxMax: envNum("FIDACY_PER_TX_MAX", process.env.FIDACY_PER_TX_MAX) ?? fileNum(m.perTxMax) ?? 2500,
@@ -4739,7 +5066,7 @@ function resolveMandateRules(cfg) {
4739
5066
  }
4740
5067
 
4741
5068
  // ../mcp/src/telemetry.ts
4742
- var CLIENT_VERSION = true ? "0.4.2" : "dev";
5069
+ var CLIENT_VERSION = true ? "0.5.0" : "dev";
4743
5070
  function bandOf(amount) {
4744
5071
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
4745
5072
  if (amount < 10) return "lt10";
@@ -4798,7 +5125,7 @@ function resultOf(status, violatedRule) {
4798
5125
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
4799
5126
  return "deny_scope";
4800
5127
  }
4801
- function record(type, result, band, capRatio, action) {
5128
+ function record2(type, result, band, capRatio, action) {
4802
5129
  if (!telemetryEnabled()) return;
4803
5130
  armExitFlush();
4804
5131
  buffer.push({
@@ -4818,10 +5145,10 @@ function record(type, result, band, capRatio, action) {
4818
5145
  if (typeof flushTimer.unref === "function") flushTimer.unref();
4819
5146
  }
4820
5147
  }
4821
- var recordInstall = () => record("install");
4822
- var recordAgentActive = () => record("agent_active");
4823
- var recordDecision = (status, violatedRule, amount, perTxMax) => record("decision", resultOf(status, violatedRule), bandOf(amount), capRatioOf(amount, perTxMax));
4824
- var recordUpgradeIntent = () => record("upgrade_intent");
5148
+ var recordInstall = () => record2("install");
5149
+ var recordAgentActive = () => record2("agent_active");
5150
+ var recordDecision = (status, violatedRule, amount, perTxMax) => record2("decision", resultOf(status, violatedRule), bandOf(amount), capRatioOf(amount, perTxMax));
5151
+ var recordUpgradeIntent = () => record2("upgrade_intent");
4825
5152
  async function flush() {
4826
5153
  if (flushTimer) {
4827
5154
  clearTimeout(flushTimer);
@@ -4927,7 +5254,7 @@ function requestUpgrade() {
4927
5254
  }
4928
5255
 
4929
5256
  // ../mcp/src/provision.ts
4930
- var CLIENT_VERSION2 = true ? "0.4.2" : "dev";
5257
+ var CLIENT_VERSION2 = true ? "0.5.0" : "dev";
4931
5258
  function provisionEnabled() {
4932
5259
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
4933
5260
  return !(v === "1" || v === "true" || v === "yes");
@@ -5069,7 +5396,7 @@ function trialCountdownLine(keyOverride) {
5069
5396
  }
5070
5397
 
5071
5398
  // ../mcp/src/register.ts
5072
- var CLIENT_VERSION3 = true ? "0.4.2" : "dev";
5399
+ var CLIENT_VERSION3 = true ? "0.5.0" : "dev";
5073
5400
  function endpoint3() {
5074
5401
  const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
5075
5402
  return `${base}/v1/register`;
@@ -5522,7 +5849,7 @@ async function postOnce(fetchImpl, url, headers, payload, timeoutMs) {
5522
5849
  }
5523
5850
 
5524
5851
  // ../mcp/src/artifacts.ts
5525
- import { createHash } from "node:crypto";
5852
+ import { createHash as createHash3 } from "node:crypto";
5526
5853
  import { createReadStream } from "node:fs";
5527
5854
  import { pipeline } from "node:stream/promises";
5528
5855
  var ARTIFACT_KINDS = [
@@ -5538,7 +5865,7 @@ var ARTIFACT_KINDS = [
5538
5865
  "custom"
5539
5866
  ];
5540
5867
  async function hashFile(path) {
5541
- const hash = createHash("sha256");
5868
+ const hash = createHash3("sha256");
5542
5869
  await pipeline(createReadStream(path), async function* (source) {
5543
5870
  for await (const chunk of source) {
5544
5871
  hash.update(chunk);
@@ -5582,8 +5909,8 @@ async function engineFetch(method, pathAndQuery, cfg, body) {
5582
5909
  async function anchorArtifact(params, cfg) {
5583
5910
  return await engineFetch("POST", "/v1/artifacts", cfg, params);
5584
5911
  }
5585
- async function findArtifacts(sha2562, cfg) {
5586
- return await engineFetch("GET", `/v1/artifacts?sha256=${sha2562}`, cfg);
5912
+ async function findArtifacts(sha2563, cfg) {
5913
+ return await engineFetch("GET", `/v1/artifacts?sha256=${sha2563}`, cfg);
5587
5914
  }
5588
5915
 
5589
5916
  // src/index.ts
@@ -5616,7 +5943,7 @@ function maybeRegisterOperator(config) {
5616
5943
  });
5617
5944
  }
5618
5945
  var ASSESS_KINDS = ["ap2_payment", "message_send", "voice_call", "custom", "claim_document"];
5619
- var index_default = defineToolPlugin({
5946
+ var entry = defineToolPlugin({
5620
5947
  id: "fidacy",
5621
5948
  name: "Fidacy \u2014 Payment Firewall",
5622
5949
  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.",
@@ -5798,12 +6125,12 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
5798
6125
  async execute(params) {
5799
6126
  const c = boot();
5800
6127
  const records = await c.history(5e3);
5801
- const record2 = records.find((r) => r.decisionId === params.decisionId);
5802
- if (!record2) throw new Error(`No decision ${params.decisionId} on this chain. Run list_decisions to see the ids that exist here.`);
6128
+ const record3 = records.find((r) => r.decisionId === params.decisionId);
6129
+ if (!record3) throw new Error(`No decision ${params.decisionId} on this chain. Run list_decisions to see the ids that exist here.`);
5803
6130
  const proof = await c.getProof(params.decisionId);
5804
6131
  return {
5805
- record: record2,
5806
- explanation: explainRule(record2.violatedRule, record2.payee),
6132
+ record: record3,
6133
+ explanation: explainRule(record3.violatedRule, record3.payee),
5807
6134
  chainIntact: proof?.chainIntact ?? null
5808
6135
  };
5809
6136
  }
@@ -5981,6 +6308,70 @@ Your local protection keeps working meanwhile; your usage history migrates to th
5981
6308
  ];
5982
6309
  }
5983
6310
  });
6311
+ function machineSubject() {
6312
+ try {
6313
+ const id = ensureState().config.anon_id;
6314
+ return id ? `agent:${String(id).slice(0, 8)}` : "agent:unknown";
6315
+ } catch {
6316
+ return "agent:unknown";
6317
+ }
6318
+ }
6319
+ var registerTools = entry.register;
6320
+ entry.register = (api) => {
6321
+ registerTools(api);
6322
+ const creds = () => {
6323
+ try {
6324
+ const cfg = ensureState().config;
6325
+ const apiKey = (cfg.api_key || process.env.FIDACY_ENGINE_API_KEY || "").trim();
6326
+ if (!apiKey) return null;
6327
+ return { engineUrl: process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com", apiKey };
6328
+ } catch {
6329
+ return null;
6330
+ }
6331
+ };
6332
+ const anchor = async (head, sessionId, byCategory = {}) => {
6333
+ const cfg = creds();
6334
+ if (!cfg) return false;
6335
+ try {
6336
+ const total = Object.values(byCategory).reduce((a, b) => a + b, 0);
6337
+ await anchorArtifact(
6338
+ {
6339
+ sha256: head,
6340
+ kind: "custom",
6341
+ label: sessionLabel(sessionId, total, byCategory),
6342
+ // Which install produced the session, so a fleet view can group by
6343
+ // machine. The anon id is a random UUID minted on this machine and
6344
+ // tied to no person, which is the point: the cockpit answers "which
6345
+ // agent" without ever learning "which human".
6346
+ subject: machineSubject()
6347
+ },
6348
+ cfg
6349
+ );
6350
+ return true;
6351
+ } catch {
6352
+ return false;
6353
+ }
6354
+ };
6355
+ installObserver(
6356
+ api,
6357
+ // Same channel as the boot banner: stderr is where the human operator looks,
6358
+ // and stdout belongs to the MCP/JSON-RPC framing in the other shell.
6359
+ (line) => console.error(line),
6360
+ () => creds() !== null,
6361
+ Date.now,
6362
+ anchor
6363
+ );
6364
+ void (async () => {
6365
+ if (!creds()) return;
6366
+ for (const p of takePending()) {
6367
+ if (!await anchor(p.head, p.sessionId, p.byCategory ?? {})) {
6368
+ queuePending(p.head, p.sessionId, p.at, p.total ?? 0, p.byCategory ?? {});
6369
+ }
6370
+ }
6371
+ })().catch(() => {
6372
+ });
6373
+ };
6374
+ var index_default = entry;
5984
6375
  export {
5985
6376
  index_default as default
5986
6377
  };
@@ -3,7 +3,7 @@
3
3
  "name": "Fidacy AI Agent 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.4.2",
6
+ "version": "0.5.0",
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.4.2",
3
+ "version": "0.5.0",
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
  "author": "Fidacy (ZeepCode Group Technology LLC) <hello@fidacy.com> (https://fidacy.com)",
@@ -47,21 +47,21 @@
47
47
  "npmSpec": "@fidacy/openclaw-plugin"
48
48
  }
49
49
  },
50
- "scripts": {
51
- "build": "node scripts/bundle.mjs",
52
- "typecheck": "tsc --noEmit",
53
- "test": "npm run build && node --test test/*.test.mjs"
54
- },
55
50
  "engines": {
56
51
  "node": ">=20"
57
52
  },
58
53
  "devDependencies": {
59
- "@fidacy/firewall": "workspace:*",
60
- "@fidacy/mcp": "workspace:*",
61
54
  "@types/node": "^22.10.0",
62
55
  "esbuild": "^0.24.0",
63
56
  "openclaw": "2026.6.11",
64
57
  "typebox": "1.1.39",
65
- "typescript": "^5.6.3"
58
+ "typescript": "^5.6.3",
59
+ "@fidacy/mcp": "0.6.4",
60
+ "@fidacy/firewall": "0.1.1"
61
+ },
62
+ "scripts": {
63
+ "build": "node scripts/bundle.mjs",
64
+ "typecheck": "tsc --noEmit",
65
+ "test": "npm run build && node --test test/*.test.mjs"
66
66
  }
67
- }
67
+ }