@fidacy/openclaw-plugin 0.4.3 → 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();
@@ -4741,7 +5066,7 @@ function resolveMandateRules(cfg) {
4741
5066
  }
4742
5067
 
4743
5068
  // ../mcp/src/telemetry.ts
4744
- var CLIENT_VERSION = true ? "0.4.3" : "dev";
5069
+ var CLIENT_VERSION = true ? "0.5.0" : "dev";
4745
5070
  function bandOf(amount) {
4746
5071
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
4747
5072
  if (amount < 10) return "lt10";
@@ -4800,7 +5125,7 @@ function resultOf(status, violatedRule) {
4800
5125
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
4801
5126
  return "deny_scope";
4802
5127
  }
4803
- function record(type, result, band, capRatio, action) {
5128
+ function record2(type, result, band, capRatio, action) {
4804
5129
  if (!telemetryEnabled()) return;
4805
5130
  armExitFlush();
4806
5131
  buffer.push({
@@ -4820,10 +5145,10 @@ function record(type, result, band, capRatio, action) {
4820
5145
  if (typeof flushTimer.unref === "function") flushTimer.unref();
4821
5146
  }
4822
5147
  }
4823
- var recordInstall = () => record("install");
4824
- var recordAgentActive = () => record("agent_active");
4825
- var recordDecision = (status, violatedRule, amount, perTxMax) => record("decision", resultOf(status, violatedRule), bandOf(amount), capRatioOf(amount, perTxMax));
4826
- 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");
4827
5152
  async function flush() {
4828
5153
  if (flushTimer) {
4829
5154
  clearTimeout(flushTimer);
@@ -4929,7 +5254,7 @@ function requestUpgrade() {
4929
5254
  }
4930
5255
 
4931
5256
  // ../mcp/src/provision.ts
4932
- var CLIENT_VERSION2 = true ? "0.4.3" : "dev";
5257
+ var CLIENT_VERSION2 = true ? "0.5.0" : "dev";
4933
5258
  function provisionEnabled() {
4934
5259
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
4935
5260
  return !(v === "1" || v === "true" || v === "yes");
@@ -5071,7 +5396,7 @@ function trialCountdownLine(keyOverride) {
5071
5396
  }
5072
5397
 
5073
5398
  // ../mcp/src/register.ts
5074
- var CLIENT_VERSION3 = true ? "0.4.3" : "dev";
5399
+ var CLIENT_VERSION3 = true ? "0.5.0" : "dev";
5075
5400
  function endpoint3() {
5076
5401
  const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
5077
5402
  return `${base}/v1/register`;
@@ -5524,7 +5849,7 @@ async function postOnce(fetchImpl, url, headers, payload, timeoutMs) {
5524
5849
  }
5525
5850
 
5526
5851
  // ../mcp/src/artifacts.ts
5527
- import { createHash } from "node:crypto";
5852
+ import { createHash as createHash3 } from "node:crypto";
5528
5853
  import { createReadStream } from "node:fs";
5529
5854
  import { pipeline } from "node:stream/promises";
5530
5855
  var ARTIFACT_KINDS = [
@@ -5540,7 +5865,7 @@ var ARTIFACT_KINDS = [
5540
5865
  "custom"
5541
5866
  ];
5542
5867
  async function hashFile(path) {
5543
- const hash = createHash("sha256");
5868
+ const hash = createHash3("sha256");
5544
5869
  await pipeline(createReadStream(path), async function* (source) {
5545
5870
  for await (const chunk of source) {
5546
5871
  hash.update(chunk);
@@ -5584,8 +5909,8 @@ async function engineFetch(method, pathAndQuery, cfg, body) {
5584
5909
  async function anchorArtifact(params, cfg) {
5585
5910
  return await engineFetch("POST", "/v1/artifacts", cfg, params);
5586
5911
  }
5587
- async function findArtifacts(sha2562, cfg) {
5588
- 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);
5589
5914
  }
5590
5915
 
5591
5916
  // src/index.ts
@@ -5618,7 +5943,7 @@ function maybeRegisterOperator(config) {
5618
5943
  });
5619
5944
  }
5620
5945
  var ASSESS_KINDS = ["ap2_payment", "message_send", "voice_call", "custom", "claim_document"];
5621
- var index_default = defineToolPlugin({
5946
+ var entry = defineToolPlugin({
5622
5947
  id: "fidacy",
5623
5948
  name: "Fidacy \u2014 Payment Firewall",
5624
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.",
@@ -5800,12 +6125,12 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
5800
6125
  async execute(params) {
5801
6126
  const c = boot();
5802
6127
  const records = await c.history(5e3);
5803
- const record2 = records.find((r) => r.decisionId === params.decisionId);
5804
- 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.`);
5805
6130
  const proof = await c.getProof(params.decisionId);
5806
6131
  return {
5807
- record: record2,
5808
- explanation: explainRule(record2.violatedRule, record2.payee),
6132
+ record: record3,
6133
+ explanation: explainRule(record3.violatedRule, record3.payee),
5809
6134
  chainIntact: proof?.chainIntact ?? null
5810
6135
  };
5811
6136
  }
@@ -5983,6 +6308,70 @@ Your local protection keeps working meanwhile; your usage history migrates to th
5983
6308
  ];
5984
6309
  }
5985
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;
5986
6375
  export {
5987
6376
  index_default as default
5988
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.3",
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.3",
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)",
@@ -56,8 +56,8 @@
56
56
  "openclaw": "2026.6.11",
57
57
  "typebox": "1.1.39",
58
58
  "typescript": "^5.6.3",
59
- "@fidacy/firewall": "0.1.1",
60
- "@fidacy/mcp": "0.6.4"
59
+ "@fidacy/mcp": "0.6.4",
60
+ "@fidacy/firewall": "0.1.1"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "node scripts/bundle.mjs",