@fidacy/mcp 0.2.10 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 01f0734: Telemetry now flushes on process exit. The 2s flush timer is unref'd so a short-lived process could exit with a full buffer and silently lose it, which is exactly how plugin-channel installs went uncounted. A beforeExit hook sends the last batch, best-effort as always.
8
+
9
+ ## 0.3.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 0a1bf9e: Anonymous telemetry now counts tool usage: a `tool_use` event with which tool ran (assess, anchor, check, verify, explain) and nothing else. Same privacy invariant as every other event: an allowlisted enum on the wire, never the tool's content, payee, amount, hash or mandate. Opt out as always with FIDACY_DISABLE_TELEMETRY=1.
14
+
15
+ ### Patch Changes
16
+
17
+ - 0a1bf9e: Fix the free-trial decision counter being wiped by the background auto-provision write. The provision task captured config.json before its network call and wrote that stale snapshot back after, erasing every decision counted in between, so the 20-decision anonymous trial reset on every process restart. The provision write now merges the key onto a fresh read, and the counter write self-heals from the in-process floor. The wall now holds across restarts: 20 free decisions per install, as designed.
18
+
3
19
  ## 0.2.10
4
20
 
5
21
  ### Patch Changes
package/dist/core.js CHANGED
@@ -461,7 +461,7 @@ function resolveMandateRules(cfg) {
461
461
  }
462
462
 
463
463
  // src/telemetry.ts
464
- var CLIENT_VERSION = true ? "0.2.10" : "dev";
464
+ var CLIENT_VERSION = true ? "0.3.1" : "dev";
465
465
  function bandOf(amount) {
466
466
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
467
467
  if (amount < 10) return "lt10";
@@ -497,6 +497,14 @@ function anonId() {
497
497
  }
498
498
  var buffer = [];
499
499
  var flushTimer = null;
500
+ var exitHookArmed = false;
501
+ function armExitFlush() {
502
+ if (exitHookArmed || typeof process === "undefined" || typeof process.once !== "function") return;
503
+ exitHookArmed = true;
504
+ process.once("beforeExit", () => {
505
+ if (buffer.length > 0) void flush();
506
+ });
507
+ }
500
508
  function resultOf(status, violatedRule) {
501
509
  if (status === "ALLOW") return "allow";
502
510
  const r = violatedRule ?? "";
@@ -509,8 +517,9 @@ function resultOf(status, violatedRule) {
509
517
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
510
518
  return "deny_scope";
511
519
  }
512
- function record(type, result, band, capRatio) {
520
+ function record(type, result, band, capRatio, action) {
513
521
  if (!telemetryEnabled()) return;
522
+ armExitFlush();
514
523
  buffer.push({
515
524
  type,
516
525
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -518,7 +527,8 @@ function record(type, result, band, capRatio) {
518
527
  shell: currentShell,
519
528
  ...result ? { result } : {},
520
529
  ...band ? { band } : {},
521
- ...capRatio ? { cap_ratio: capRatio } : {}
530
+ ...capRatio ? { cap_ratio: capRatio } : {},
531
+ ...action ? { action } : {}
522
532
  });
523
533
  if (!flushTimer) {
524
534
  flushTimer = setTimeout(() => {
package/dist/index.js CHANGED
@@ -490,7 +490,7 @@ function resolveMandateRules(cfg) {
490
490
  }
491
491
 
492
492
  // src/telemetry.ts
493
- var CLIENT_VERSION = true ? "0.2.10" : "dev";
493
+ var CLIENT_VERSION = true ? "0.3.1" : "dev";
494
494
  function bandOf(amount) {
495
495
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
496
496
  if (amount < 10) return "lt10";
@@ -526,6 +526,14 @@ function anonId() {
526
526
  }
527
527
  var buffer = [];
528
528
  var flushTimer = null;
529
+ var exitHookArmed = false;
530
+ function armExitFlush() {
531
+ if (exitHookArmed || typeof process === "undefined" || typeof process.once !== "function") return;
532
+ exitHookArmed = true;
533
+ process.once("beforeExit", () => {
534
+ if (buffer.length > 0) void flush();
535
+ });
536
+ }
529
537
  function resultOf(status, violatedRule) {
530
538
  if (status === "ALLOW") return "allow";
531
539
  const r = violatedRule ?? "";
@@ -538,8 +546,9 @@ function resultOf(status, violatedRule) {
538
546
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
539
547
  return "deny_scope";
540
548
  }
541
- function record(type, result, band, capRatio) {
549
+ function record(type, result, band, capRatio, action) {
542
550
  if (!telemetryEnabled()) return;
551
+ armExitFlush();
543
552
  buffer.push({
544
553
  type,
545
554
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -547,7 +556,8 @@ function record(type, result, band, capRatio) {
547
556
  shell: currentShell,
548
557
  ...result ? { result } : {},
549
558
  ...band ? { band } : {},
550
- ...capRatio ? { cap_ratio: capRatio } : {}
559
+ ...capRatio ? { cap_ratio: capRatio } : {},
560
+ ...action ? { action } : {}
551
561
  });
552
562
  if (!flushTimer) {
553
563
  flushTimer = setTimeout(() => {
@@ -560,6 +570,7 @@ var recordInstall = () => record("install");
560
570
  var recordAgentActive = () => record("agent_active");
561
571
  var recordDecision = (status, violatedRule, amount, perTxMax) => record("decision", resultOf(status, violatedRule), bandOf(amount), capRatioOf(amount, perTxMax));
562
572
  var recordUpgradeIntent = () => record("upgrade_intent");
573
+ var recordToolUse = (action) => record("tool_use", void 0, void 0, void 0, action);
563
574
  async function flush() {
564
575
  if (flushTimer) {
565
576
  clearTimeout(flushTimer);
@@ -834,7 +845,7 @@ async function findArtifacts(sha2562, cfg) {
834
845
  }
835
846
 
836
847
  // src/provision.ts
837
- var CLIENT_VERSION2 = true ? "0.2.10" : "dev";
848
+ var CLIENT_VERSION2 = true ? "0.3.1" : "dev";
838
849
  function provisionEnabled() {
839
850
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
840
851
  return !(v === "1" || v === "true" || v === "yes");
@@ -858,8 +869,9 @@ async function autoProvision() {
858
869
  if (!res.ok) return "failed";
859
870
  const data = await res.json();
860
871
  if (typeof data.api_key !== "string" || !data.api_key) return "failed";
872
+ const fresh = readConfig() ?? cfg;
861
873
  writeConfig({
862
- ...cfg,
874
+ ...fresh,
863
875
  api_key: data.api_key,
864
876
  tier: data.tier === "paid" ? "paid" : "free"
865
877
  });
@@ -906,7 +918,7 @@ function bumpDecisionCount() {
906
918
  sessionDecisions += 1;
907
919
  const cfg = readConfig();
908
920
  if (!cfg) return sessionDecisions;
909
- const n = (cfg.decisions_count ?? 0) + 1;
921
+ const n = Math.max((cfg.decisions_count ?? 0) + 1, sessionDecisions);
910
922
  cfg.decisions_count = n;
911
923
  try {
912
924
  writeConfig(cfg);
@@ -1264,7 +1276,7 @@ function renderAlertLine(alerts) {
1264
1276
  var state = ensureState();
1265
1277
  var core = makeCore();
1266
1278
  var subject = process.env.FIDACY_SUBJECT ?? "agent:demo";
1267
- var SERVER_VERSION = true ? "0.2.10" : "dev";
1279
+ var SERVER_VERSION = true ? "0.3.1" : "dev";
1268
1280
  var bootClaim = claimUrl();
1269
1281
  var server = new McpServer(
1270
1282
  { name: "fidacy", version: SERVER_VERSION },
@@ -1331,6 +1343,7 @@ server.registerTool(
1331
1343
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1332
1344
  },
1333
1345
  async () => {
1346
+ recordToolUse("verify");
1334
1347
  const m = await core.getMandate(subject);
1335
1348
  const payload = { mandate: m, fidacyPublicKey: core.publicKey() };
1336
1349
  const claim = process.env.FIDACY_ENGINE_API_KEY ? null : claimUrl();
@@ -1409,6 +1422,7 @@ server.registerTool(
1409
1422
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1410
1423
  },
1411
1424
  async ({ decisionId }) => {
1425
+ recordToolUse("explain");
1412
1426
  const records = await core.history(5e3);
1413
1427
  const record2 = records.find((r) => r.decisionId === decisionId);
1414
1428
  if (!record2) {
@@ -1469,6 +1483,7 @@ server.registerTool(
1469
1483
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
1470
1484
  },
1471
1485
  async ({ kind, mandate, mandateType, idempotencyKey, spendingMandate, a2a }) => {
1486
+ recordToolUse("assess");
1472
1487
  const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
1473
1488
  const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
1474
1489
  if (!apiKey) {
@@ -1519,6 +1534,7 @@ server.registerTool(
1519
1534
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
1520
1535
  },
1521
1536
  async ({ path, sha256: sha2562, kind, label, subject: subj }) => {
1537
+ recordToolUse("anchor");
1522
1538
  const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
1523
1539
  const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
1524
1540
  if (!apiKey) {
@@ -1573,6 +1589,7 @@ server.registerTool(
1573
1589
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
1574
1590
  },
1575
1591
  async ({ path, sha256: sha2562 }) => {
1592
+ recordToolUse("check");
1576
1593
  const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
1577
1594
  const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
1578
1595
  if (!apiKey) {
package/dist/lib.d.ts CHANGED
@@ -3,7 +3,7 @@ export { makeCore } from "./core.js";
3
3
  export { ensureState, readConfig } from "./config.js";
4
4
  export { requestUpgrade, upgradeUrl } from "./upgrade.js";
5
5
  export { autoProvision } from "./provision.js";
6
- export { recordInstall, recordAgentActive, setTelemetryShell } from "./telemetry.js";
6
+ export { recordInstall, recordAgentActive, setTelemetryShell, flush as flushTelemetry } from "./telemetry.js";
7
7
  export { decisionNudge, weekOneBootNudge, nudgeOnce, installAgeDays, claimUrl, noKeyCta } from "./nudges.js";
8
8
  export { FREE_DECISIONS, activationGate, activationBootLine, trialCountdownLine, hasEngineKey, remainingFree } from "./activation.js";
9
9
  export { summarize, renderSummary, toRows, renderRows, explainRule, withinDays } from "./reporting.js";
package/dist/lib.js CHANGED
@@ -613,7 +613,7 @@ function resolveMandateRules(cfg) {
613
613
  }
614
614
 
615
615
  // src/telemetry.ts
616
- var CLIENT_VERSION = true ? "0.2.10" : "dev";
616
+ var CLIENT_VERSION = true ? "0.3.1" : "dev";
617
617
  function bandOf(amount) {
618
618
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
619
619
  if (amount < 10) return "lt10";
@@ -652,6 +652,14 @@ function anonId() {
652
652
  }
653
653
  var buffer = [];
654
654
  var flushTimer = null;
655
+ var exitHookArmed = false;
656
+ function armExitFlush() {
657
+ if (exitHookArmed || typeof process === "undefined" || typeof process.once !== "function") return;
658
+ exitHookArmed = true;
659
+ process.once("beforeExit", () => {
660
+ if (buffer.length > 0) void flush();
661
+ });
662
+ }
655
663
  function resultOf(status, violatedRule) {
656
664
  if (status === "ALLOW") return "allow";
657
665
  const r = violatedRule ?? "";
@@ -664,8 +672,9 @@ function resultOf(status, violatedRule) {
664
672
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
665
673
  return "deny_scope";
666
674
  }
667
- function record(type, result, band, capRatio) {
675
+ function record(type, result, band, capRatio, action) {
668
676
  if (!telemetryEnabled()) return;
677
+ armExitFlush();
669
678
  buffer.push({
670
679
  type,
671
680
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -673,7 +682,8 @@ function record(type, result, band, capRatio) {
673
682
  shell: currentShell,
674
683
  ...result ? { result } : {},
675
684
  ...band ? { band } : {},
676
- ...capRatio ? { cap_ratio: capRatio } : {}
685
+ ...capRatio ? { cap_ratio: capRatio } : {},
686
+ ...action ? { action } : {}
677
687
  });
678
688
  if (!flushTimer) {
679
689
  flushTimer = setTimeout(() => {
@@ -791,7 +801,7 @@ function requestUpgrade() {
791
801
  }
792
802
 
793
803
  // src/provision.ts
794
- var CLIENT_VERSION2 = true ? "0.2.10" : "dev";
804
+ var CLIENT_VERSION2 = true ? "0.3.1" : "dev";
795
805
  function provisionEnabled() {
796
806
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
797
807
  return !(v === "1" || v === "true" || v === "yes");
@@ -815,8 +825,9 @@ async function autoProvision() {
815
825
  if (!res.ok) return "failed";
816
826
  const data = await res.json();
817
827
  if (typeof data.api_key !== "string" || !data.api_key) return "failed";
828
+ const fresh = readConfig() ?? cfg;
818
829
  writeConfig({
819
- ...cfg,
830
+ ...fresh,
820
831
  api_key: data.api_key,
821
832
  tier: data.tier === "paid" ? "paid" : "free"
822
833
  });
@@ -851,7 +862,7 @@ function bumpDecisionCount() {
851
862
  sessionDecisions += 1;
852
863
  const cfg = readConfig();
853
864
  if (!cfg) return sessionDecisions;
854
- const n = (cfg.decisions_count ?? 0) + 1;
865
+ const n = Math.max((cfg.decisions_count ?? 0) + 1, sessionDecisions);
855
866
  cfg.decisions_count = n;
856
867
  try {
857
868
  writeConfig(cfg);
@@ -1154,6 +1165,7 @@ export {
1154
1165
  ensureState,
1155
1166
  evaluate,
1156
1167
  explainRule,
1168
+ flush as flushTelemetry,
1157
1169
  hasEngineKey,
1158
1170
  httpRedeem,
1159
1171
  installAgeDays,
@@ -1,4 +1,7 @@
1
- export type TelemetryEventType = "install" | "agent_active" | "decision" | "upgrade_intent";
1
+ export type TelemetryEventType = "install" | "agent_active" | "decision" | "upgrade_intent" | "tool_use";
2
+ /** Which tool a tool_use event counts (0059). An ENUM mirrored by the ingest
3
+ * allowlist: which tool ran, never what it ran on. */
4
+ export type ToolAction = "assess" | "anchor" | "check" | "verify" | "explain";
2
5
  export type DecisionResult = "allow" | "deny_cap" | "deny_payee" | "deny_scope" | "deny_lookalike" | "deny_duplicate" | "deny_window" | "deny_invalid";
3
6
  /** Which shell this engine is embedded in. An ENUM mirrored by the ingest allowlist
4
7
  * and the DB CHECK — never a free-form string, so it can't become a PII channel. */
@@ -18,10 +21,12 @@ export declare function telemetryEnabled(): boolean;
18
21
  /** Map a firewall violatedRule to the coarse, PII-free decision result enum. */
19
22
  export declare function resultOf(status: "ALLOW" | "DENY", violatedRule?: string): DecisionResult;
20
23
  /** Buffer an event (sync, never blocks, never throws). Auto-flushes shortly after. */
21
- export declare function record(type: TelemetryEventType, result?: DecisionResult, band?: AmountBand, capRatio?: CapRatioBand): void;
24
+ export declare function record(type: TelemetryEventType, result?: DecisionResult, band?: AmountBand, capRatio?: CapRatioBand, action?: ToolAction): void;
22
25
  export declare const recordInstall: () => void;
23
26
  export declare const recordAgentActive: () => void;
24
27
  export declare const recordDecision: (status: "ALLOW" | "DENY", violatedRule?: string, amount?: number, perTxMax?: number) => void;
25
28
  export declare const recordUpgradeIntent: () => void;
29
+ /** Count a tool invocation (which tool, never its content) — the agent-activity profile. */
30
+ export declare const recordToolUse: (action: ToolAction) => void;
26
31
  /** Send the buffered batch fire-and-forget. Swallows all errors. Best-effort. */
27
32
  export declare function flush(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/mcp",
3
- "version": "0.2.10",
3
+ "version": "0.3.1",
4
4
  "description": "Fidacy action firewall for AI agents. Mandate-gated payment authorization as an MCP server.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Fidacy (ZeepCode Group Technology LLC) <hello@fidacy.com> (https://fidacy.com)",