@fidacy/openclaw-plugin 0.1.16 → 0.1.18

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.16" : "dev";
4693
+ var CLIENT_VERSION = true ? "0.1.18" : "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";
@@ -4856,9 +4856,9 @@ function makeCore() {
4856
4856
 
4857
4857
  // ../mcp/src/upgrade.ts
4858
4858
  function upgradeUrl() {
4859
- const base = (process.env.FIDACY_APP_URL ?? "https://app.fidacy.com").replace(/\/$/, "");
4859
+ const base = (process.env.FIDACY_WEB_URL ?? "https://fidacy.com").replace(/\/$/, "");
4860
4860
  const anon = readConfig()?.anon_id ?? "";
4861
- return `${base}/upgrade?anon=${encodeURIComponent(anon)}`;
4861
+ return anon ? `${base}/claim?ref=${encodeURIComponent(anon)}` : `${base}/claim`;
4862
4862
  }
4863
4863
  function requestUpgrade() {
4864
4864
  recordUpgradeIntent();
@@ -4867,7 +4867,7 @@ function requestUpgrade() {
4867
4867
  }
4868
4868
 
4869
4869
  // ../mcp/src/provision.ts
4870
- var CLIENT_VERSION2 = true ? "0.1.16" : "dev";
4870
+ var CLIENT_VERSION2 = true ? "0.1.18" : "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");
@@ -4940,6 +4940,9 @@ function decisionNudge(status, violatedRule, upgradeToolName) {
4940
4940
  if (status === "DENY" && nudgeOnce("first_deny")) {
4941
4941
  return `Fidacy just blocked this for you. For your operator: ${claimPath}.`;
4942
4942
  }
4943
+ if (status === "ALLOW" && nudgeOnce("first_allow")) {
4944
+ return `Fidacy authorized this and signed the decision. For your operator: ${claimPath}.`;
4945
+ }
4943
4946
  if (count === 25 && nudgeOnce("proof_teaser")) {
4944
4947
  return `Your local chain now holds ${count} decisions only you can read. Anchor them and anyone can verify at fidacy.com/verify, no account needed on their side. For your operator: ${claimPath}.`;
4945
4948
  }
@@ -5135,16 +5138,20 @@ async function findArtifacts(sha2562, cfg) {
5135
5138
  }
5136
5139
 
5137
5140
  // src/index.ts
5141
+ var loaded = false;
5142
+ function onPluginLoad() {
5143
+ if (loaded) return;
5144
+ loaded = true;
5145
+ setTelemetryShell("openclaw-plugin");
5146
+ const state = ensureState();
5147
+ if (state.firstRun) recordInstall();
5148
+ recordAgentActive();
5149
+ void autoProvision();
5150
+ }
5138
5151
  var core;
5139
5152
  function boot() {
5140
- if (!core) {
5141
- setTelemetryShell("openclaw-plugin");
5142
- const state = ensureState();
5143
- core = makeCore();
5144
- if (state.firstRun) recordInstall();
5145
- recordAgentActive();
5146
- void autoProvision();
5147
- }
5153
+ onPluginLoad();
5154
+ if (!core) core = makeCore();
5148
5155
  return core;
5149
5156
  }
5150
5157
  function subjectOf(config) {
@@ -5175,234 +5182,237 @@ var index_default = defineToolPlugin({
5175
5182
  },
5176
5183
  { additionalProperties: false }
5177
5184
  ),
5178
- tools: (tool) => [
5179
- // ACTION FIREWALL. The agent calls this INSTEAD of any raw payment tool.
5180
- // ALLOW returns a short-lived Ed25519 grant the executor requires; DENY
5181
- // returns no grant: the action is dead on arrival.
5182
- tool({
5183
- name: "request_payment",
5184
- label: "Request Payment Authorization",
5185
- 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.",
5186
- parameters: typebox_exports.Object({
5187
- payee: typebox_exports.String({ description: "Payee identifier" }),
5188
- amount: typebox_exports.Number({ minimum: 0, description: "Amount in the mandate currency (must be positive)" }),
5189
- currency: typebox_exports.String({ minLength: 3, maxLength: 3, description: "ISO 4217 currency code" }),
5190
- purpose: typebox_exports.String({ description: "Human-readable purpose" }),
5191
- category: typebox_exports.String({ description: "Purpose category (must be allowed by the mandate)" }),
5192
- idempotencyKey: typebox_exports.String({ description: "Caller-supplied idempotency key" }),
5193
- invoiceRef: typebox_exports.Optional(
5194
- typebox_exports.String({
5195
- description: "Optional invoice identity. When set, Fidacy enforces one payment per invoice: a second request for the same invoiceRef is DENIED, at any amount."
5196
- })
5197
- )
5198
- }),
5199
- async execute(params, config) {
5200
- const d = await boot().decide(params, subjectOf(config));
5201
- 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:
5185
+ tools: (tool) => {
5186
+ onPluginLoad();
5187
+ return [
5188
+ // ACTION FIREWALL. The agent calls this INSTEAD of any raw payment tool.
5189
+ // ALLOW returns a short-lived Ed25519 grant the executor requires; DENY
5190
+ // returns no grant: the action is dead on arrival.
5191
+ tool({
5192
+ name: "request_payment",
5193
+ label: "Request Payment Authorization",
5194
+ 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.",
5195
+ parameters: typebox_exports.Object({
5196
+ payee: typebox_exports.String({ description: "Payee identifier" }),
5197
+ amount: typebox_exports.Number({ minimum: 0, description: "Amount in the mandate currency (must be positive)" }),
5198
+ currency: typebox_exports.String({ minLength: 3, maxLength: 3, description: "ISO 4217 currency code" }),
5199
+ purpose: typebox_exports.String({ description: "Human-readable purpose" }),
5200
+ category: typebox_exports.String({ description: "Purpose category (must be allowed by the mandate)" }),
5201
+ idempotencyKey: typebox_exports.String({ description: "Caller-supplied idempotency key" }),
5202
+ invoiceRef: typebox_exports.Optional(
5203
+ typebox_exports.String({
5204
+ description: "Optional invoice identity. When set, Fidacy enforces one payment per invoice: a second request for the same invoiceRef is DENIED, at any amount."
5205
+ })
5206
+ )
5207
+ }),
5208
+ async execute(params, config) {
5209
+ const d = await boot().decide(params, subjectOf(config));
5210
+ 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:
5202
5211
  ${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.` : ""}`;
5203
- const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
5204
- const message = nudge ? `${base} ${nudge}` : base;
5205
- return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
5206
- }
5207
- }),
5208
- // Read-only: surface the guardrails so the agent/human can see the envelope
5209
- // and pin Fidacy's public key for grant verification.
5210
- tool({
5211
- name: "verify_mandate",
5212
- label: "Verify Active Mandate",
5213
- description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
5214
- parameters: typebox_exports.Object({}),
5215
- async execute(_params, config) {
5216
- const c = boot();
5217
- const mandate = await c.getMandate(subjectOf(config));
5218
- const hasKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim().length > 0;
5219
- const claim = hasKey ? null : claimUrl();
5220
- if (claim) {
5221
- return {
5222
- mandate,
5223
- fidacyPublicKey: c.publicKey(),
5224
- message: `For your operator: free account + free API key, and this install's history migrates: ${claim}`
5225
- };
5212
+ const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
5213
+ const message = nudge ? `${base} ${nudge}` : base;
5214
+ return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
5226
5215
  }
5227
- return { mandate, fidacyPublicKey: c.publicKey() };
5228
- }
5229
- }),
5230
- // Read-only: portable, hash-chained proof for a decision. The non-repudiation
5231
- // artifact for audit, reinsurance, or dispute.
5232
- tool({
5233
- name: "get_audit_proof",
5234
- label: "Get Audit Proof",
5235
- 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.",
5236
- parameters: typebox_exports.Object({
5237
- decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
5238
- }),
5239
- async execute(params) {
5240
- const proof = await boot().getProof(params.decisionId);
5241
- if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
5242
- return proof;
5243
- }
5244
- }),
5245
- // VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
5246
- // returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
5247
- // by anyone via @fidacy/verify against the engine JWKS. Moves no money.
5248
- // Registration never requires a key: only CALLING it without one errors.
5249
- tool({
5250
- name: "assess_action",
5251
- label: "Assess Action (Signed Trust Verdict)",
5252
- 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.",
5253
- parameters: typebox_exports.Object({
5254
- kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
5255
- mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
5256
- description: "The action/mandate object for this kind"
5257
- }),
5258
- mandateType: typebox_exports.Optional(typebox_exports.String()),
5259
- idempotencyKey: typebox_exports.Optional(typebox_exports.String()),
5260
- spendingMandate: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
5261
- a2a: typebox_exports.Optional(typebox_exports.Object({ task_id: typebox_exports.String() }))
5262
5216
  }),
5263
- async execute(params, config) {
5264
- boot();
5265
- const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5266
- const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5267
- if (!apiKey) {
5268
- throw new Error(
5269
- `assess_action needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5270
- );
5271
- }
5272
- try {
5273
- const r = await assessAction(
5274
- { kind: params.kind, mandate: params.mandate, mandateType: params.mandateType, idempotencyKey: params.idempotencyKey, spendingMandate: params.spendingMandate, a2a: params.a2a },
5275
- { engineUrl, apiKey }
5276
- );
5277
- return { summary: `${r.decision} (score ${r.score}) signed by ${r.signingKeyId}`, ...r };
5278
- } catch (e) {
5279
- if (e instanceof AssessError) {
5280
- const reasons = e.rejection_reasons?.length ? " (" + e.rejection_reasons.map((x) => x.key).join(",") + ")" : "";
5281
- throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
5217
+ // Read-only: surface the guardrails so the agent/human can see the envelope
5218
+ // and pin Fidacy's public key for grant verification.
5219
+ tool({
5220
+ name: "verify_mandate",
5221
+ label: "Verify Active Mandate",
5222
+ description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
5223
+ parameters: typebox_exports.Object({}),
5224
+ async execute(_params, config) {
5225
+ const c = boot();
5226
+ const mandate = await c.getMandate(subjectOf(config));
5227
+ const hasKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim().length > 0;
5228
+ const claim = hasKey ? null : claimUrl();
5229
+ if (claim) {
5230
+ return {
5231
+ mandate,
5232
+ fidacyPublicKey: c.publicKey(),
5233
+ message: `For your operator: free account + free API key, and this install's history migrates: ${claim}`
5234
+ };
5282
5235
  }
5283
- throw new Error("ASSESS failed: unexpected error");
5236
+ return { mandate, fidacyPublicKey: c.publicKey() };
5284
5237
  }
5285
- }
5286
- }),
5287
- // ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
5288
- // locally (streaming) and never uploaded; the SHA-256 joins the same
5289
- // Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
5290
- tool({
5291
- name: "anchor_artifact",
5292
- label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
5293
- 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).",
5294
- parameters: typebox_exports.Object({
5295
- path: typebox_exports.Optional(typebox_exports.String()),
5296
- sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
5297
- kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
5298
- label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
5299
- subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
5300
5238
  }),
5301
- async execute(params, config) {
5302
- boot();
5303
- const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5304
- const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5305
- if (!apiKey) {
5306
- throw new Error(
5307
- `anchor_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5308
- );
5309
- }
5310
- if (!params.path && !params.sha256) {
5311
- throw new Error("Give a file `path` (hashed locally) or a precomputed `sha256`.");
5239
+ // Read-only: portable, hash-chained proof for a decision. The non-repudiation
5240
+ // artifact for audit, reinsurance, or dispute.
5241
+ tool({
5242
+ name: "get_audit_proof",
5243
+ label: "Get Audit Proof",
5244
+ 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.",
5245
+ parameters: typebox_exports.Object({
5246
+ decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
5247
+ }),
5248
+ async execute(params) {
5249
+ const proof = await boot().getProof(params.decisionId);
5250
+ if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
5251
+ return proof;
5312
5252
  }
5313
- let hash = params.sha256 ?? "";
5314
- if (!hash && params.path) {
5253
+ }),
5254
+ // VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
5255
+ // returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
5256
+ // by anyone via @fidacy/verify against the engine JWKS. Moves no money.
5257
+ // Registration never requires a key: only CALLING it without one errors.
5258
+ tool({
5259
+ name: "assess_action",
5260
+ label: "Assess Action (Signed Trust Verdict)",
5261
+ 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.",
5262
+ parameters: typebox_exports.Object({
5263
+ kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
5264
+ mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
5265
+ description: "The action/mandate object for this kind"
5266
+ }),
5267
+ mandateType: typebox_exports.Optional(typebox_exports.String()),
5268
+ idempotencyKey: typebox_exports.Optional(typebox_exports.String()),
5269
+ spendingMandate: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
5270
+ a2a: typebox_exports.Optional(typebox_exports.Object({ task_id: typebox_exports.String() }))
5271
+ }),
5272
+ async execute(params, config) {
5273
+ boot();
5274
+ const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5275
+ const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5276
+ if (!apiKey) {
5277
+ throw new Error(
5278
+ `assess_action needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5279
+ );
5280
+ }
5315
5281
  try {
5316
- hash = await hashFile(params.path);
5317
- } catch {
5318
- throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5282
+ const r = await assessAction(
5283
+ { kind: params.kind, mandate: params.mandate, mandateType: params.mandateType, idempotencyKey: params.idempotencyKey, spendingMandate: params.spendingMandate, a2a: params.a2a },
5284
+ { engineUrl, apiKey }
5285
+ );
5286
+ return { summary: `${r.decision} (score ${r.score}) signed by ${r.signingKeyId}`, ...r };
5287
+ } catch (e) {
5288
+ if (e instanceof AssessError) {
5289
+ const reasons = e.rejection_reasons?.length ? " (" + e.rejection_reasons.map((x) => x.key).join(",") + ")" : "";
5290
+ throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
5291
+ }
5292
+ throw new Error("ASSESS failed: unexpected error");
5319
5293
  }
5320
5294
  }
5321
- try {
5322
- const r = await anchorArtifact(
5323
- { sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
5324
- { engineUrl, apiKey }
5325
- );
5326
- return {
5327
- 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.`,
5328
- ...r
5329
- };
5330
- } catch (e) {
5331
- if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
5332
- throw new Error("ANCHOR failed: unexpected error");
5333
- }
5334
- }
5335
- }),
5336
- // Verification half: was this exact content anchored, and did it reach Bitcoin?
5337
- tool({
5338
- name: "check_artifact",
5339
- label: "Check Artifact (was this hash anchored?)",
5340
- 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.",
5341
- parameters: typebox_exports.Object({
5342
- path: typebox_exports.Optional(typebox_exports.String()),
5343
- sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
5344
5295
  }),
5345
- async execute(params, config) {
5346
- boot();
5347
- const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5348
- const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5349
- if (!apiKey) {
5350
- throw new Error(
5351
- `check_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5352
- );
5353
- }
5354
- if (!params.path && !params.sha256) {
5355
- throw new Error("Give a file `path` (hashed locally) or a `sha256`.");
5356
- }
5357
- let hash = params.sha256 ?? "";
5358
- if (!hash && params.path) {
5296
+ // ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
5297
+ // locally (streaming) and never uploaded; the SHA-256 joins the same
5298
+ // Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
5299
+ tool({
5300
+ name: "anchor_artifact",
5301
+ label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
5302
+ 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).",
5303
+ parameters: typebox_exports.Object({
5304
+ path: typebox_exports.Optional(typebox_exports.String()),
5305
+ sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
5306
+ kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
5307
+ label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
5308
+ subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
5309
+ }),
5310
+ async execute(params, config) {
5311
+ boot();
5312
+ const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5313
+ const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5314
+ if (!apiKey) {
5315
+ throw new Error(
5316
+ `anchor_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5317
+ );
5318
+ }
5319
+ if (!params.path && !params.sha256) {
5320
+ throw new Error("Give a file `path` (hashed locally) or a precomputed `sha256`.");
5321
+ }
5322
+ let hash = params.sha256 ?? "";
5323
+ if (!hash && params.path) {
5324
+ try {
5325
+ hash = await hashFile(params.path);
5326
+ } catch {
5327
+ throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5328
+ }
5329
+ }
5359
5330
  try {
5360
- hash = await hashFile(params.path);
5361
- } catch {
5362
- throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5331
+ const r = await anchorArtifact(
5332
+ { sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
5333
+ { engineUrl, apiKey }
5334
+ );
5335
+ return {
5336
+ 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.`,
5337
+ ...r
5338
+ };
5339
+ } catch (e) {
5340
+ if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
5341
+ throw new Error("ANCHOR failed: unexpected error");
5363
5342
  }
5364
5343
  }
5365
- try {
5366
- const r = await findArtifacts(hash, { engineUrl, apiKey });
5367
- if (!r.artifacts.length) {
5344
+ }),
5345
+ // Verification half: was this exact content anchored, and did it reach Bitcoin?
5346
+ tool({
5347
+ name: "check_artifact",
5348
+ label: "Check Artifact (was this hash anchored?)",
5349
+ 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.",
5350
+ parameters: typebox_exports.Object({
5351
+ path: typebox_exports.Optional(typebox_exports.String()),
5352
+ sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
5353
+ }),
5354
+ async execute(params, config) {
5355
+ boot();
5356
+ const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
5357
+ const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
5358
+ if (!apiKey) {
5359
+ throw new Error(
5360
+ `check_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
5361
+ );
5362
+ }
5363
+ if (!params.path && !params.sha256) {
5364
+ throw new Error("Give a file `path` (hashed locally) or a `sha256`.");
5365
+ }
5366
+ let hash = params.sha256 ?? "";
5367
+ if (!hash && params.path) {
5368
+ try {
5369
+ hash = await hashFile(params.path);
5370
+ } catch {
5371
+ throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
5372
+ }
5373
+ }
5374
+ try {
5375
+ const r = await findArtifacts(hash, { engineUrl, apiKey });
5376
+ if (!r.artifacts.length) {
5377
+ return {
5378
+ 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.`,
5379
+ sha256: hash,
5380
+ artifacts: []
5381
+ };
5382
+ }
5383
+ const first = r.artifacts[0];
5368
5384
  return {
5369
- 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.`,
5385
+ 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.`,
5370
5386
  sha256: hash,
5371
- artifacts: []
5387
+ ...r
5372
5388
  };
5389
+ } catch (e) {
5390
+ if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
5391
+ throw new Error("CHECK failed: unexpected error");
5373
5392
  }
5374
- const first = r.artifacts[0];
5375
- return {
5376
- 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.`,
5377
- sha256: hash,
5378
- ...r
5379
- };
5380
- } catch (e) {
5381
- if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
5382
- throw new Error("CHECK failed: unexpected error");
5383
5393
  }
5384
- }
5385
- }),
5386
- // Upgrade funnel. Records the intent signal and returns the real-account link;
5387
- // the anon_id travels in the URL so the engine binds past usage to the tenant.
5388
- tool({
5389
- name: "fidacy_upgrade",
5390
- label: "Upgrade to a Fidacy account",
5391
- 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.",
5392
- parameters: typebox_exports.Object({}),
5393
- async execute() {
5394
- boot();
5395
- const { url } = requestUpgrade();
5396
- return {
5397
- upgradeUrl: url,
5398
- message: `To upgrade, open:
5394
+ }),
5395
+ // Upgrade funnel. Records the intent signal and returns the real-account link;
5396
+ // the anon_id travels in the URL so the engine binds past usage to the tenant.
5397
+ tool({
5398
+ name: "fidacy_upgrade",
5399
+ label: "Upgrade to a Fidacy account",
5400
+ 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.",
5401
+ parameters: typebox_exports.Object({}),
5402
+ async execute() {
5403
+ boot();
5404
+ const { url } = requestUpgrade();
5405
+ return {
5406
+ upgradeUrl: url,
5407
+ message: `To upgrade, open:
5399
5408
  ${url}
5400
5409
 
5401
5410
  Your local protection keeps working meanwhile; your usage history migrates to the new account on completion.`
5402
- };
5403
- }
5404
- })
5405
- ]
5411
+ };
5412
+ }
5413
+ })
5414
+ ];
5415
+ }
5406
5416
  });
5407
5417
  export {
5408
5418
  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.16",
6
+ "version": "0.1.18",
7
7
  "contracts": {
8
8
  "tools": [
9
9
  "request_payment",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/openclaw-plugin",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
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.26"
60
+ },
61
+ "scripts": {
62
+ "build": "node scripts/bundle.mjs",
63
+ "typecheck": "tsc --noEmit"
64
64
  }
65
- }
65
+ }