@fidacy/openclaw-plugin 0.1.15 → 0.1.17

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
@@ -4690,7 +4690,7 @@ function resolveMandateRules(cfg) {
4690
4690
  }
4691
4691
 
4692
4692
  // ../mcp/src/telemetry.ts
4693
- var CLIENT_VERSION = true ? "0.1.15" : "dev";
4693
+ var CLIENT_VERSION = true ? "0.1.17" : "dev";
4694
4694
  function bandOf(amount) {
4695
4695
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
4696
4696
  if (amount < 10) return "lt10";
@@ -4867,7 +4867,7 @@ function requestUpgrade() {
4867
4867
  }
4868
4868
 
4869
4869
  // ../mcp/src/provision.ts
4870
- var CLIENT_VERSION2 = true ? "0.1.15" : "dev";
4870
+ var CLIENT_VERSION2 = true ? "0.1.17" : "dev";
4871
4871
  function provisionEnabled() {
4872
4872
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
4873
4873
  return !(v === "1" || v === "true" || v === "yes");
@@ -4933,7 +4933,7 @@ function bumpDecisionCount() {
4933
4933
  function decisionNudge(status, violatedRule, upgradeToolName) {
4934
4934
  const count = bumpDecisionCount();
4935
4935
  const url = claimUrl();
4936
- const claimPath = url ? `open ${url} to see everything blocked on this install and claim it (free account, this history migrates)` : `call the ${upgradeToolName} tool (free account, this history migrates)`;
4936
+ const claimPath = url ? `free account + free API key at ${url} (this install's history migrates)` : `call the ${upgradeToolName} tool (free account + free API key, this history migrates)`;
4937
4937
  if (status === "DENY" && violatedRule?.startsWith("duplicate_invoice") && nudgeOnce("bec_catch")) {
4938
4938
  return `That was a duplicate-invoice (BEC) catch, the #1 payment fraud. For your operator: ${claimPath}. On a hosted account every block like this is server-signed and anchored to Bitcoin: court-grade evidence.`;
4939
4939
  }
@@ -4948,6 +4948,10 @@ function decisionNudge(status, violatedRule, upgradeToolName) {
4948
4948
  }
4949
4949
  return null;
4950
4950
  }
4951
+ function noKeyCta() {
4952
+ const url = claimUrl();
4953
+ return url ? `free in ~1 min for your operator: open ${url} (this install's history migrates), or set FIDACY_ENGINE_API_KEY if you already have one` : `free in ~1 min: call the upgrade tool (your history migrates), or set FIDACY_ENGINE_API_KEY if you already have one`;
4954
+ }
4951
4955
 
4952
4956
  // ../mcp/src/assess.ts
4953
4957
  var AssessError = class extends Error {
@@ -5131,16 +5135,20 @@ async function findArtifacts(sha2562, cfg) {
5131
5135
  }
5132
5136
 
5133
5137
  // src/index.ts
5138
+ var loaded = false;
5139
+ function onPluginLoad() {
5140
+ if (loaded) return;
5141
+ loaded = true;
5142
+ setTelemetryShell("openclaw-plugin");
5143
+ const state = ensureState();
5144
+ if (state.firstRun) recordInstall();
5145
+ recordAgentActive();
5146
+ void autoProvision();
5147
+ }
5134
5148
  var core;
5135
5149
  function boot() {
5136
- if (!core) {
5137
- setTelemetryShell("openclaw-plugin");
5138
- const state = ensureState();
5139
- core = makeCore();
5140
- if (state.firstRun) recordInstall();
5141
- recordAgentActive();
5142
- void autoProvision();
5143
- }
5150
+ onPluginLoad();
5151
+ if (!core) core = makeCore();
5144
5152
  return core;
5145
5153
  }
5146
5154
  function subjectOf(config) {
@@ -5171,225 +5179,237 @@ var index_default = defineToolPlugin({
5171
5179
  },
5172
5180
  { additionalProperties: false }
5173
5181
  ),
5174
- tools: (tool) => [
5175
- // ACTION FIREWALL. The agent calls this INSTEAD of any raw payment tool.
5176
- // ALLOW returns a short-lived Ed25519 grant the executor requires; DENY
5177
- // returns no grant: the action is dead on arrival.
5178
- tool({
5179
- name: "request_payment",
5180
- label: "Request Payment Authorization",
5181
- description: "Authorize a payment action against the active Fidacy mandate. Returns an ALLOW with a signed grant, or a DENY with the violated rule. The downstream executor MUST require the grant. Call this before any payment; never pay without it.",
5182
- parameters: typebox_exports.Object({
5183
- payee: typebox_exports.String({ description: "Payee identifier" }),
5184
- amount: typebox_exports.Number({ minimum: 0, description: "Amount in the mandate currency (must be positive)" }),
5185
- currency: typebox_exports.String({ minLength: 3, maxLength: 3, description: "ISO 4217 currency code" }),
5186
- purpose: typebox_exports.String({ description: "Human-readable purpose" }),
5187
- category: typebox_exports.String({ description: "Purpose category (must be allowed by the mandate)" }),
5188
- idempotencyKey: typebox_exports.String({ description: "Caller-supplied idempotency key" }),
5189
- invoiceRef: typebox_exports.Optional(
5190
- typebox_exports.String({
5191
- description: "Optional invoice identity. When set, Fidacy enforces one payment per invoice: a second request for the same invoiceRef is DENIED, at any amount."
5192
- })
5193
- )
5194
- }),
5195
- async execute(params, config) {
5196
- const d = await boot().decide(params, subjectOf(config));
5197
- 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:
5198
- ${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.` : ""}`;
5199
- const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
5200
- const message = nudge ? `${base} ${nudge}` : base;
5201
- return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
5202
- }
5203
- }),
5204
- // Read-only: surface the guardrails so the agent/human can see the envelope
5205
- // and pin Fidacy's public key for grant verification.
5206
- tool({
5207
- name: "verify_mandate",
5208
- label: "Verify Active Mandate",
5209
- description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
5210
- parameters: typebox_exports.Object({}),
5211
- async execute(_params, config) {
5212
- const c = boot();
5213
- const mandate = await c.getMandate(subjectOf(config));
5214
- return { mandate, fidacyPublicKey: c.publicKey() };
5215
- }
5216
- }),
5217
- // Read-only: portable, hash-chained proof for a decision. The non-repudiation
5218
- // artifact for audit, reinsurance, or dispute.
5219
- tool({
5220
- name: "get_audit_proof",
5221
- label: "Get Audit Proof",
5222
- description: "Return the tamper-evident, hash-chained proof for a decision id, including whether the chain is intact and the public key it verifies against.",
5223
- parameters: typebox_exports.Object({
5224
- decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
5225
- }),
5226
- async execute(params) {
5227
- const proof = await boot().getProof(params.decisionId);
5228
- if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
5229
- return proof;
5230
- }
5231
- }),
5232
- // VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
5233
- // returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
5234
- // by anyone via @fidacy/verify against the engine JWKS. Moves no money.
5235
- // Registration never requires a key: only CALLING it without one errors.
5236
- tool({
5237
- name: "assess_action",
5238
- label: "Assess Action (Signed Trust Verdict)",
5239
- description: "Return a SIGNED Fidacy trust verdict from the live engine (default https://api.fidacy.com) for a proposed action. The signed proof is `riskPayloadJws` + `signingKeyId`, verifiable by anyone via @fidacy/verify against the engine JWKS at /.well-known/jwks.json. `kind` is one of ap2_payment, message_send, voice_call, custom, claim_document; `mandate` is the action/mandate object for that kind. This is the verdict (advisory) layer and moves no money; it complements the payment-firewall tools (request_payment et al.) in the same install.",
5240
- parameters: typebox_exports.Object({
5241
- kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
5242
- mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
5243
- description: "The action/mandate object for this kind"
5182
+ tools: (tool) => {
5183
+ onPluginLoad();
5184
+ return [
5185
+ // ACTION FIREWALL. The agent calls this INSTEAD of any raw payment tool.
5186
+ // ALLOW returns a short-lived Ed25519 grant the executor requires; DENY
5187
+ // returns no grant: the action is dead on arrival.
5188
+ tool({
5189
+ name: "request_payment",
5190
+ label: "Request Payment Authorization",
5191
+ description: "Authorize a payment action against the active Fidacy mandate. Returns an ALLOW with a signed grant, or a DENY with the violated rule. The downstream executor MUST require the grant. Call this before any payment; never pay without it.",
5192
+ parameters: typebox_exports.Object({
5193
+ payee: typebox_exports.String({ description: "Payee identifier" }),
5194
+ amount: typebox_exports.Number({ minimum: 0, description: "Amount in the mandate currency (must be positive)" }),
5195
+ currency: typebox_exports.String({ minLength: 3, maxLength: 3, description: "ISO 4217 currency code" }),
5196
+ purpose: typebox_exports.String({ description: "Human-readable purpose" }),
5197
+ category: typebox_exports.String({ description: "Purpose category (must be allowed by the mandate)" }),
5198
+ idempotencyKey: typebox_exports.String({ description: "Caller-supplied idempotency key" }),
5199
+ invoiceRef: typebox_exports.Optional(
5200
+ typebox_exports.String({
5201
+ description: "Optional invoice identity. When set, Fidacy enforces one payment per invoice: a second request for the same invoiceRef is DENIED, at any amount."
5202
+ })
5203
+ )
5244
5204
  }),
5245
- mandateType: typebox_exports.Optional(typebox_exports.String()),
5246
- idempotencyKey: typebox_exports.Optional(typebox_exports.String()),
5247
- spendingMandate: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
5248
- a2a: typebox_exports.Optional(typebox_exports.Object({ task_id: typebox_exports.String() }))
5249
- }),
5250
- async execute(params, config) {
5251
- boot();
5252
- const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5253
- const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5254
- if (!apiKey) {
5255
- throw new Error(
5256
- "assess_action requires an engine API key (an fky_live_/fky_test_ key with assess:write). Set plugins.entries.fidacy.config.engineApiKey or FIDACY_ENGINE_API_KEY to enable signed verdicts."
5257
- );
5205
+ async execute(params, config) {
5206
+ const d = await boot().decide(params, subjectOf(config));
5207
+ 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:
5208
+ ${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.` : ""}`;
5209
+ const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
5210
+ const message = nudge ? `${base} ${nudge}` : base;
5211
+ return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
5258
5212
  }
5259
- try {
5260
- const r = await assessAction(
5261
- { kind: params.kind, mandate: params.mandate, mandateType: params.mandateType, idempotencyKey: params.idempotencyKey, spendingMandate: params.spendingMandate, a2a: params.a2a },
5262
- { engineUrl, apiKey }
5263
- );
5264
- return { summary: `${r.decision} (score ${r.score}) signed by ${r.signingKeyId}`, ...r };
5265
- } catch (e) {
5266
- if (e instanceof AssessError) {
5267
- const reasons = e.rejection_reasons?.length ? " (" + e.rejection_reasons.map((x) => x.key).join(",") + ")" : "";
5268
- throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
5213
+ }),
5214
+ // Read-only: surface the guardrails so the agent/human can see the envelope
5215
+ // and pin Fidacy's public key for grant verification.
5216
+ tool({
5217
+ name: "verify_mandate",
5218
+ label: "Verify Active Mandate",
5219
+ description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
5220
+ parameters: typebox_exports.Object({}),
5221
+ async execute(_params, config) {
5222
+ const c = boot();
5223
+ const mandate = await c.getMandate(subjectOf(config));
5224
+ const hasKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim().length > 0;
5225
+ const claim = hasKey ? null : claimUrl();
5226
+ if (claim) {
5227
+ return {
5228
+ mandate,
5229
+ fidacyPublicKey: c.publicKey(),
5230
+ message: `For your operator: free account + free API key, and this install's history migrates: ${claim}`
5231
+ };
5269
5232
  }
5270
- throw new Error("ASSESS failed: unexpected error");
5233
+ return { mandate, fidacyPublicKey: c.publicKey() };
5271
5234
  }
5272
- }
5273
- }),
5274
- // ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
5275
- // locally (streaming) and never uploaded; the SHA-256 joins the same
5276
- // Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
5277
- tool({
5278
- name: "anchor_artifact",
5279
- label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
5280
- description: "Prove an artifact existed exactly as-is at this moment, and make any later tampering detectable. Give a file `path` (hashed locally with SHA-256 \u2014 the file itself is NEVER uploaded) or a precomputed `sha256`. The hash is registered on Fidacy's tamper-evident audit chain, which is checkpointed to the Bitcoin blockchain, and you get a signed receipt (JWS) verifiable offline against the engine JWKS. Use it for contracts, invoices, medical prescriptions, insurance claims, images, audio, video. `kind` defaults to document; optional `label` is a short reference (no PII).",
5281
- parameters: typebox_exports.Object({
5282
- path: typebox_exports.Optional(typebox_exports.String()),
5283
- sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
5284
- kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
5285
- label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
5286
- subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
5287
5235
  }),
5288
- async execute(params, config) {
5289
- boot();
5290
- const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5291
- const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5292
- if (!apiKey) {
5293
- throw new Error(
5294
- "anchor_artifact requires an engine API key (an fky_live_/fky_test_ key with assess:write). Set plugins.entries.fidacy.config.engineApiKey or FIDACY_ENGINE_API_KEY."
5295
- );
5296
- }
5297
- if (!params.path && !params.sha256) {
5298
- throw new Error("Give a file `path` (hashed locally) or a precomputed `sha256`.");
5236
+ // Read-only: portable, hash-chained proof for a decision. The non-repudiation
5237
+ // artifact for audit, reinsurance, or dispute.
5238
+ tool({
5239
+ name: "get_audit_proof",
5240
+ label: "Get Audit Proof",
5241
+ description: "Return the tamper-evident, hash-chained proof for a decision id, including whether the chain is intact and the public key it verifies against.",
5242
+ parameters: typebox_exports.Object({
5243
+ decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
5244
+ }),
5245
+ async execute(params) {
5246
+ const proof = await boot().getProof(params.decisionId);
5247
+ if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
5248
+ return proof;
5299
5249
  }
5300
- let hash = params.sha256 ?? "";
5301
- if (!hash && params.path) {
5250
+ }),
5251
+ // VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
5252
+ // returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
5253
+ // by anyone via @fidacy/verify against the engine JWKS. Moves no money.
5254
+ // Registration never requires a key: only CALLING it without one errors.
5255
+ tool({
5256
+ name: "assess_action",
5257
+ label: "Assess Action (Signed Trust Verdict)",
5258
+ description: "Return a SIGNED Fidacy trust verdict from the live engine (default https://api.fidacy.com) for a proposed action. The signed proof is `riskPayloadJws` + `signingKeyId`, verifiable by anyone via @fidacy/verify against the engine JWKS at /.well-known/jwks.json. `kind` is one of ap2_payment, message_send, voice_call, custom, claim_document; `mandate` is the action/mandate object for that kind. This is the verdict (advisory) layer and moves no money; it complements the payment-firewall tools (request_payment et al.) in the same install.",
5259
+ parameters: typebox_exports.Object({
5260
+ kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
5261
+ mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
5262
+ description: "The action/mandate object for this kind"
5263
+ }),
5264
+ mandateType: typebox_exports.Optional(typebox_exports.String()),
5265
+ idempotencyKey: typebox_exports.Optional(typebox_exports.String()),
5266
+ spendingMandate: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
5267
+ a2a: typebox_exports.Optional(typebox_exports.Object({ task_id: typebox_exports.String() }))
5268
+ }),
5269
+ async execute(params, config) {
5270
+ boot();
5271
+ const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5272
+ const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5273
+ if (!apiKey) {
5274
+ throw new Error(
5275
+ `assess_action needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5276
+ );
5277
+ }
5302
5278
  try {
5303
- hash = await hashFile(params.path);
5304
- } catch {
5305
- throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5279
+ const r = await assessAction(
5280
+ { kind: params.kind, mandate: params.mandate, mandateType: params.mandateType, idempotencyKey: params.idempotencyKey, spendingMandate: params.spendingMandate, a2a: params.a2a },
5281
+ { engineUrl, apiKey }
5282
+ );
5283
+ return { summary: `${r.decision} (score ${r.score}) signed by ${r.signingKeyId}`, ...r };
5284
+ } catch (e) {
5285
+ if (e instanceof AssessError) {
5286
+ const reasons = e.rejection_reasons?.length ? " (" + e.rejection_reasons.map((x) => x.key).join(",") + ")" : "";
5287
+ throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
5288
+ }
5289
+ throw new Error("ASSESS failed: unexpected error");
5306
5290
  }
5307
5291
  }
5308
- try {
5309
- const r = await anchorArtifact(
5310
- { sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
5311
- { engineUrl, apiKey }
5312
- );
5313
- return {
5314
- summary: `ANCHORED ${r.kind} \xB7 sha256 ${hash.slice(0, 16)}\u2026 \xB7 audit seq ${r.audit.seq} \xB7 Bitcoin checkpoint: ${r.anchor.status}. The file never left this machine.`,
5315
- ...r
5316
- };
5317
- } catch (e) {
5318
- if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
5319
- throw new Error("ANCHOR failed: unexpected error");
5320
- }
5321
- }
5322
- }),
5323
- // Verification half: was this exact content anchored, and did it reach Bitcoin?
5324
- tool({
5325
- name: "check_artifact",
5326
- label: "Check Artifact (was this hash anchored?)",
5327
- description: "Check whether an artifact was anchored by this account and the state of its Bitcoin checkpoint. Give a file `path` (hashed locally, never uploaded) or a `sha256`. If the current hash of a file does NOT match an anchored record you expected, the file changed since anchoring \u2014 that is the tampering signal.",
5328
- parameters: typebox_exports.Object({
5329
- path: typebox_exports.Optional(typebox_exports.String()),
5330
- sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
5331
5292
  }),
5332
- async execute(params, config) {
5333
- boot();
5334
- const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5335
- const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5336
- if (!apiKey) {
5337
- throw new Error(
5338
- "check_artifact requires an engine API key. Set plugins.entries.fidacy.config.engineApiKey or FIDACY_ENGINE_API_KEY."
5339
- );
5340
- }
5341
- if (!params.path && !params.sha256) {
5342
- throw new Error("Give a file `path` (hashed locally) or a `sha256`.");
5343
- }
5344
- let hash = params.sha256 ?? "";
5345
- if (!hash && params.path) {
5293
+ // ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
5294
+ // locally (streaming) and never uploaded; the SHA-256 joins the same
5295
+ // Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
5296
+ tool({
5297
+ name: "anchor_artifact",
5298
+ label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
5299
+ description: "Prove an artifact existed exactly as-is at this moment, and make any later tampering detectable. Give a file `path` (hashed locally with SHA-256 \u2014 the file itself is NEVER uploaded) or a precomputed `sha256`. The hash is registered on Fidacy's tamper-evident audit chain, which is checkpointed to the Bitcoin blockchain, and you get a signed receipt (JWS) verifiable offline against the engine JWKS. Use it for contracts, invoices, medical prescriptions, insurance claims, images, audio, video. `kind` defaults to document; optional `label` is a short reference (no PII).",
5300
+ parameters: typebox_exports.Object({
5301
+ path: typebox_exports.Optional(typebox_exports.String()),
5302
+ sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
5303
+ kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
5304
+ label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
5305
+ subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
5306
+ }),
5307
+ async execute(params, config) {
5308
+ boot();
5309
+ const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5310
+ const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5311
+ if (!apiKey) {
5312
+ throw new Error(
5313
+ `anchor_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5314
+ );
5315
+ }
5316
+ if (!params.path && !params.sha256) {
5317
+ throw new Error("Give a file `path` (hashed locally) or a precomputed `sha256`.");
5318
+ }
5319
+ let hash = params.sha256 ?? "";
5320
+ if (!hash && params.path) {
5321
+ try {
5322
+ hash = await hashFile(params.path);
5323
+ } catch {
5324
+ throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5325
+ }
5326
+ }
5346
5327
  try {
5347
- hash = await hashFile(params.path);
5348
- } catch {
5349
- throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5328
+ const r = await anchorArtifact(
5329
+ { sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
5330
+ { engineUrl, apiKey }
5331
+ );
5332
+ return {
5333
+ summary: `ANCHORED ${r.kind} \xB7 sha256 ${hash.slice(0, 16)}\u2026 \xB7 audit seq ${r.audit.seq} \xB7 Bitcoin checkpoint: ${r.anchor.status}. The file never left this machine.`,
5334
+ ...r
5335
+ };
5336
+ } catch (e) {
5337
+ if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
5338
+ throw new Error("ANCHOR failed: unexpected error");
5350
5339
  }
5351
5340
  }
5352
- try {
5353
- const r = await findArtifacts(hash, { engineUrl, apiKey });
5354
- if (!r.artifacts.length) {
5341
+ }),
5342
+ // Verification half: was this exact content anchored, and did it reach Bitcoin?
5343
+ tool({
5344
+ name: "check_artifact",
5345
+ label: "Check Artifact (was this hash anchored?)",
5346
+ description: "Check whether an artifact was anchored by this account and the state of its Bitcoin checkpoint. Give a file `path` (hashed locally, never uploaded) or a `sha256`. If the current hash of a file does NOT match an anchored record you expected, the file changed since anchoring \u2014 that is the tampering signal.",
5347
+ parameters: typebox_exports.Object({
5348
+ path: typebox_exports.Optional(typebox_exports.String()),
5349
+ sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
5350
+ }),
5351
+ async execute(params, config) {
5352
+ boot();
5353
+ const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5354
+ const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5355
+ if (!apiKey) {
5356
+ throw new Error(
5357
+ `check_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5358
+ );
5359
+ }
5360
+ if (!params.path && !params.sha256) {
5361
+ throw new Error("Give a file `path` (hashed locally) or a `sha256`.");
5362
+ }
5363
+ let hash = params.sha256 ?? "";
5364
+ if (!hash && params.path) {
5365
+ try {
5366
+ hash = await hashFile(params.path);
5367
+ } catch {
5368
+ throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5369
+ }
5370
+ }
5371
+ try {
5372
+ const r = await findArtifacts(hash, { engineUrl, apiKey });
5373
+ if (!r.artifacts.length) {
5374
+ return {
5375
+ summary: `NOT FOUND \xB7 sha256 ${hash.slice(0, 16)}\u2026 has no anchored record in this account. If you expected a match, the file changed since anchoring.`,
5376
+ sha256: hash,
5377
+ artifacts: []
5378
+ };
5379
+ }
5380
+ const first = r.artifacts[0];
5355
5381
  return {
5356
- summary: `NOT FOUND \xB7 sha256 ${hash.slice(0, 16)}\u2026 has no anchored record in this account. If you expected a match, the file changed since anchoring.`,
5382
+ summary: `FOUND ${r.artifacts.length} record(s) \xB7 newest: ${String(first.kind)} anchored ${String(first.createdAt)} (audit seq ${String(first.auditSeq)}). Content matches the anchored hash byte for byte.`,
5357
5383
  sha256: hash,
5358
- artifacts: []
5384
+ ...r
5359
5385
  };
5386
+ } catch (e) {
5387
+ if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
5388
+ throw new Error("CHECK failed: unexpected error");
5360
5389
  }
5361
- const first = r.artifacts[0];
5362
- return {
5363
- summary: `FOUND ${r.artifacts.length} record(s) \xB7 newest: ${String(first.kind)} anchored ${String(first.createdAt)} (audit seq ${String(first.auditSeq)}). Content matches the anchored hash byte for byte.`,
5364
- sha256: hash,
5365
- ...r
5366
- };
5367
- } catch (e) {
5368
- if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
5369
- throw new Error("CHECK failed: unexpected error");
5370
5390
  }
5371
- }
5372
- }),
5373
- // Upgrade funnel. Records the intent signal and returns the real-account link;
5374
- // the anon_id travels in the URL so the engine binds past usage to the tenant.
5375
- tool({
5376
- name: "fidacy_upgrade",
5377
- label: "Upgrade to a Fidacy account",
5378
- description: "Start upgrading this local install to a real Fidacy account (server-backed signed verdicts, anchored proof, higher volume). Returns a link to open; your anonymous usage is preserved and migrated to the new account.",
5379
- parameters: typebox_exports.Object({}),
5380
- async execute() {
5381
- boot();
5382
- const { url } = requestUpgrade();
5383
- return {
5384
- upgradeUrl: url,
5385
- message: `To upgrade, open:
5391
+ }),
5392
+ // Upgrade funnel. Records the intent signal and returns the real-account link;
5393
+ // the anon_id travels in the URL so the engine binds past usage to the tenant.
5394
+ tool({
5395
+ name: "fidacy_upgrade",
5396
+ label: "Upgrade to a Fidacy account",
5397
+ description: "Start upgrading this local install to a real Fidacy account (server-backed signed verdicts, anchored proof, higher volume). Returns a link to open; your anonymous usage is preserved and migrated to the new account.",
5398
+ parameters: typebox_exports.Object({}),
5399
+ async execute() {
5400
+ boot();
5401
+ const { url } = requestUpgrade();
5402
+ return {
5403
+ upgradeUrl: url,
5404
+ message: `To upgrade, open:
5386
5405
  ${url}
5387
5406
 
5388
5407
  Your local protection keeps working meanwhile; your usage history migrates to the new account on completion.`
5389
- };
5390
- }
5391
- })
5392
- ]
5408
+ };
5409
+ }
5410
+ })
5411
+ ];
5412
+ }
5393
5413
  });
5394
5414
  export {
5395
5415
  index_default as default
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "id": "fidacy",
3
- "name": "Fidacy \u2014 Payment Firewall",
3
+ "name": "Fidacy 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.15",
6
+ "version": "0.1.17",
7
7
  "contracts": {
8
8
  "tools": [
9
9
  "request_payment",
@@ -51,4 +51,4 @@
51
51
  "advanced": true
52
52
  }
53
53
  }
54
- }
54
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/openclaw-plugin",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
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",
@@ -46,20 +46,20 @@
46
46
  "npmSpec": "@fidacy/openclaw-plugin"
47
47
  }
48
48
  },
49
- "scripts": {
50
- "build": "node scripts/bundle.mjs",
51
- "typecheck": "tsc --noEmit"
52
- },
53
49
  "engines": {
54
50
  "node": ">=20"
55
51
  },
56
52
  "devDependencies": {
57
- "@fidacy/firewall": "workspace:*",
58
- "@fidacy/mcp": "workspace:*",
59
53
  "@types/node": "^22.10.0",
60
54
  "esbuild": "^0.24.0",
61
55
  "openclaw": "2026.6.11",
62
56
  "typebox": "1.1.39",
63
- "typescript": "^5.6.3"
57
+ "typescript": "^5.6.3",
58
+ "@fidacy/firewall": "0.1.1",
59
+ "@fidacy/mcp": "0.1.25"
60
+ },
61
+ "scripts": {
62
+ "build": "node scripts/bundle.mjs",
63
+ "typecheck": "tsc --noEmit"
64
64
  }
65
- }
65
+ }