thumbgate 1.22.0 → 1.23.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +1 -0
- package/adapters/chatgpt/openapi.yaml +10 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +194 -30
- package/openapi/openapi.yaml +10 -0
- package/package.json +13 -3
- package/public/agents-cost-savings.html +151 -0
- package/public/ai-malpractice-prevention.html +183 -0
- package/public/codex-plugin.html +1 -1
- package/public/index.html +3 -3
- package/public/numbers.html +2 -2
- package/public/pricing.html +1 -1
- package/scripts/cli-telemetry.js +6 -1
- package/scripts/gates-engine.js +119 -6
- package/scripts/meta-agent-loop.js +32 -0
- package/scripts/pro-local-dashboard.js +4 -4
- package/scripts/rate-limiter.js +7 -1
- package/scripts/self-healing-check.js +193 -0
- package/scripts/silent-failure-cluster.js +512 -0
- package/scripts/telemetry-analytics.js +38 -0
- package/src/api/server.js +252 -36
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>FinOps for AI Agents — ThumbGate prevents the spend FinOps tools just report on</title>
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
|
+
<meta name="description" content="Most FinOps platforms report on AI agent spend after it happens. ThumbGate is the runtime layer that prevents the wasted tool calls in the first place — and prints the dollar amount you saved.">
|
|
9
|
+
<meta property="og:title" content="FinOps for AI Agents — Prevention, Not Reporting">
|
|
10
|
+
<meta property="og:description" content="Cost dashboards tell you what your agents wasted last week. ThumbGate's PreToolUse gates stop the wasted tool calls before they fire — and `thumbgate cost` shows you the dollar amount.">
|
|
11
|
+
<meta property="og:type" content="article">
|
|
12
|
+
<meta property="og:image" content="https://thumbgate-production.up.railway.app/og.png">
|
|
13
|
+
<link rel="canonical" href="https://thumbgate-production.up.railway.app/agents-cost-savings">
|
|
14
|
+
<script type="application/ld+json">
|
|
15
|
+
{
|
|
16
|
+
"@context": "https://schema.org",
|
|
17
|
+
"@type": "TechArticle",
|
|
18
|
+
"headline": "FinOps for AI Agents — Prevention vs. Reporting",
|
|
19
|
+
"description": "Cost dashboards report agent spend after it happens. ThumbGate's runtime gates prevent the wasted tool calls in the first place, and `thumbgate cost` prints the dollar amount saved.",
|
|
20
|
+
"datePublished": "2026-05-21",
|
|
21
|
+
"dateModified": "2026-05-21",
|
|
22
|
+
"author": { "@type": "Person", "name": "Igor Ganapolsky", "url": "https://github.com/IgorGanapolsky" },
|
|
23
|
+
"publisher": { "@type": "Organization", "name": "ThumbGate", "url": "https://thumbgate-production.up.railway.app" },
|
|
24
|
+
"about": [
|
|
25
|
+
{ "@type": "Thing", "name": "FinOps for AI" },
|
|
26
|
+
{ "@type": "Thing", "name": "Agent Cost Optimization" },
|
|
27
|
+
{ "@type": "Thing", "name": "LLM Token Savings" },
|
|
28
|
+
{ "@type": "Thing", "name": "PreToolUse Gates" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
<style>
|
|
33
|
+
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
34
|
+
:root { --bg:#0a0a0b; --card:#161618; --border:#222225; --text:#e8e8ec; --muted:#8b8b94; --cyan:#22d3ee; --green:#34d399; }
|
|
35
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--bg); color: var(--text); line-height: 1.7; }
|
|
36
|
+
.container { max-width: 860px; margin: 0 auto; padding: 2rem 1.5rem 4rem; }
|
|
37
|
+
nav { padding: 1rem 2rem; border-bottom: 1px solid var(--border); display:flex; gap:1.5rem; flex-wrap:wrap; }
|
|
38
|
+
nav a { color: var(--muted); text-decoration:none; font-size:0.9rem; }
|
|
39
|
+
nav .brand { color: var(--text); font-weight:700; }
|
|
40
|
+
.pill { display:inline-block; font-size:0.75rem; letter-spacing:0.08em; text-transform:uppercase; color:var(--cyan); background:rgba(34,211,238,0.08); border:1px solid rgba(34,211,238,0.2); padding:4px 12px; border-radius:100px; margin-top:1.5rem; font-weight:600; }
|
|
41
|
+
h1 { font-size:2.2rem; line-height:1.15; margin:1rem 0 1rem; }
|
|
42
|
+
h2 { font-size:1.45rem; margin:2.2rem 0 1rem; color:var(--cyan); }
|
|
43
|
+
h3 { margin:0.6rem 0; font-size:1rem; }
|
|
44
|
+
p, li { margin-bottom:0.75rem; }
|
|
45
|
+
ul, ol { padding-left:1.25rem; }
|
|
46
|
+
.card { background: var(--card); border:1px solid var(--border); border-radius:12px; padding:1.25rem; margin:1rem 0; }
|
|
47
|
+
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:1rem; margin:1rem 0; }
|
|
48
|
+
.grid .card h3 { color:var(--cyan); }
|
|
49
|
+
.cta { display:inline-block; background:var(--cyan); color:#000; padding:0.8rem 1.2rem; border-radius:8px; text-decoration:none; font-weight:700; }
|
|
50
|
+
.secondary { color:var(--cyan); text-decoration:underline; margin-left:1rem; }
|
|
51
|
+
.quote { border-left:3px solid var(--cyan); padding:0.75rem 1rem; margin:1rem 0; color:var(--muted); font-style:italic; }
|
|
52
|
+
code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background:#0f0f11; border:1px solid var(--border); border-radius:6px; padding:0.15rem 0.4rem; font-size:0.9rem; }
|
|
53
|
+
pre { padding:0.85rem 1rem; overflow-x:auto; }
|
|
54
|
+
.footer-links { margin-top:2.5rem; padding-top:1.25rem; border-top:1px solid var(--border); color:var(--muted); font-size:0.9rem; }
|
|
55
|
+
.footer-links a { color:var(--cyan); text-decoration:none; }
|
|
56
|
+
table.compare { width:100%; border-collapse:collapse; margin:1rem 0; }
|
|
57
|
+
table.compare th, table.compare td { padding:0.6rem 0.8rem; border-bottom:1px solid var(--border); text-align:left; vertical-align:top; }
|
|
58
|
+
table.compare th { color:var(--cyan); font-size:0.85rem; text-transform:uppercase; letter-spacing:0.05em; }
|
|
59
|
+
.savings-num { color:var(--green); font-weight:700; }
|
|
60
|
+
</style>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<nav>
|
|
64
|
+
<a href="/" class="brand">ThumbGate</a>
|
|
65
|
+
<a href="/guide">Guide</a>
|
|
66
|
+
<a href="/agent-manager">Agent Manager</a>
|
|
67
|
+
<a href="/codex-enterprise">Codex Enterprise</a>
|
|
68
|
+
<a href="/dashboard">Dashboard demo</a>
|
|
69
|
+
<a href="https://github.com/IgorGanapolsky/ThumbGate" target="_blank" rel="noopener">GitHub</a>
|
|
70
|
+
</nav>
|
|
71
|
+
<div class="container">
|
|
72
|
+
<span class="pill">FinOps for AI agents</span>
|
|
73
|
+
<h1>Cost dashboards tell you what your agents wasted last week. ThumbGate stops the waste before it fires.</h1>
|
|
74
|
+
<p>Most AI-spend platforms — Finout, Vantage, the new "AI FinOps Assistant" wave — focus on <em>showing you the bill after the agent ran</em>: cost allocation, anomaly detection, unit economics finance can trust. A few (Helicone's rate limits, Revenium's Economic Control) add coarse runtime enforcement keyed off $ thresholds or request counts. None of them stop the wasted tool call by understanding <em>why</em> it would have failed.</p>
|
|
75
|
+
<p>That's the layer ThumbGate occupies. Every PreToolUse gate that fires is a Claude / GPT call your agent <em>did not</em> make — input tokens you didn't spend, output tokens you didn't spend, retry loop you didn't trigger. The savings are computable, conservative, and now surfaced as a number on your CLI.</p>
|
|
76
|
+
|
|
77
|
+
<div class="quote">"74% of CIOs say their role will be at risk if their company does not deliver measurable business gains from AI within the next two years." — <a href="https://www.cio.com/article/4172555/how-it-teams-are-putting-ai-agents-to-work.html" target="_blank" rel="noopener" style="color:var(--cyan);font-style:normal;">CIO Online, 2026</a></div>
|
|
78
|
+
<p>"Measurable" is the operative word. A token-spend dashboard tells finance how much got burned; it doesn't tell the CIO board what was averted. <code>thumbgate cost</code> prints a single conservative dollar figure backed by the gate-block count from <em>your</em> machine — not "what enterprises like you saved." That's the artifact that survives a 2026 budget review.</p>
|
|
79
|
+
|
|
80
|
+
<h2>One command, one number</h2>
|
|
81
|
+
<p>Once ThumbGate is installed and gates have been firing, this is what an operator sees:</p>
|
|
82
|
+
<pre><code>$ thumbgate cost
|
|
83
|
+
|
|
84
|
+
💰 ThumbGate cost-savings — cumulative
|
|
85
|
+
──────────────────────────────────────────────────
|
|
86
|
+
Tool calls blocked : 247
|
|
87
|
+
Tool calls warned : 12
|
|
88
|
+
Tool calls passed : 3,401
|
|
89
|
+
Top blocker : no-mocked-db (138 blocks)
|
|
90
|
+
|
|
91
|
+
Tokens you did NOT spend
|
|
92
|
+
Input : 494K
|
|
93
|
+
Output : 148K
|
|
94
|
+
Total : 642K
|
|
95
|
+
|
|
96
|
+
Estimated <span class="savings-num">$ saved : $3.95</span></code></pre>
|
|
97
|
+
<p>The methodology is intentionally conservative: 2,000 input + 600 output tokens per blocked call, a Sonnet-heavy model mix (80% Sonnet 4.5, 15% Opus 4.6, 5% Haiku 4.5), Anthropic published prices. The goal is "you almost certainly saved at least this much" — not "let's flatter ourselves." Override the mix with <code>--mix '{"claude-sonnet-4-5":1.0}'</code> if your stack is different.</p>
|
|
98
|
+
|
|
99
|
+
<h2>Prevention vs. reporting</h2>
|
|
100
|
+
<table class="compare">
|
|
101
|
+
<thead>
|
|
102
|
+
<tr><th>Capability</th><th>Reporting-layer FinOps</th><th>ThumbGate (runtime gates)</th></tr>
|
|
103
|
+
</thead>
|
|
104
|
+
<tbody>
|
|
105
|
+
<tr><td>See what agents spent last week</td><td>✅</td><td>Partial (via dashboard)</td></tr>
|
|
106
|
+
<tr><td>Allocate spend to teams / features</td><td>✅</td><td>Per-gate breakdown via <code>byGate</code></td></tr>
|
|
107
|
+
<tr><td>Stop a known-bad tool call before it hits the model</td><td>❌</td><td>✅ — PreToolUse gate fires, no API call made</td></tr>
|
|
108
|
+
<tr><td>Promote a one-off failure into a permanent gate</td><td>❌</td><td>✅ — feedback loop + lesson DB</td></tr>
|
|
109
|
+
<tr><td>Print conservative $ saved per day</td><td>❌</td><td>✅ — <code>thumbgate cost</code></td></tr>
|
|
110
|
+
<tr><td>K8s pod-level allocation, finance-grade reporting</td><td>✅ (that's their core)</td><td>❌ (not our layer)</td></tr>
|
|
111
|
+
</tbody>
|
|
112
|
+
</table>
|
|
113
|
+
<p>The two layers compose. ThumbGate prevents the wasted spend; a reporting FinOps tool tells finance what the remaining spend was for. Picking ThumbGate doesn't mean you don't also need cost visibility — it means the visibility number gets smaller.</p>
|
|
114
|
+
|
|
115
|
+
<h2>Why the savings are real, not theoretical</h2>
|
|
116
|
+
<ol>
|
|
117
|
+
<li><strong>Every block is one fewer round trip.</strong> A blocked tool call doesn't reach the model. There's no "ThumbGate intercepted but the request still cost you" — the agent's tool-call execution is replaced with the gate's verdict, and the agent's next reasoning step takes the verdict as context instead of the failed result.</li>
|
|
118
|
+
<li><strong>The avoided retry loop is the bulk of the saving.</strong> Failed tool calls don't just cost the call — they cost the model's next reasoning turn (which sees the failure and tries again), and often a third turn (which tries a different approach). Conservative 2k input + 600 output assumes one retry; in practice it's often more.</li>
|
|
119
|
+
<li><strong>The numbers come from your local <code>gate-stats.json</code>.</strong> Not from a marketing model, not from "what enterprises like you saved." Your machine, your gates, your blocks.</li>
|
|
120
|
+
</ol>
|
|
121
|
+
|
|
122
|
+
<h2>Get the number on your machine</h2>
|
|
123
|
+
<pre><code>npx thumbgate init # wire the PreToolUse hook
|
|
124
|
+
# ...let your agent run for a few hours...
|
|
125
|
+
npx thumbgate cost # see what the gates were worth</code></pre>
|
|
126
|
+
<p>Or as JSON, if you want to ship it to a dashboard:</p>
|
|
127
|
+
<pre><code>npx thumbgate cost --json | jq .savings.dollarsSaved</code></pre>
|
|
128
|
+
|
|
129
|
+
<div class="quote">"The category isn't 'FinOps for AI' — it's 'gates that stop the spend so FinOps has less to report on.' One sits behind the other."</div>
|
|
130
|
+
|
|
131
|
+
<div class="card">
|
|
132
|
+
<p><strong>The free CLI is real. The paid tier is the hosted dashboard, org-wide rule library, and the operator the Agent Manager doesn't have to be themselves.</strong></p>
|
|
133
|
+
<p>
|
|
134
|
+
<a href="/#workflow-sprint-intake?utm_source=website&utm_medium=agents_cost_savings_page&utm_campaign=finops_sprint&cta_id=agents_cost_savings_sprint_intake&cta_placement=agents_cost_savings_page" class="cta">Start the Workflow Hardening Sprint</a>
|
|
135
|
+
<a href="/checkout/pro?utm_source=website&utm_medium=agents_cost_savings_page&utm_campaign=pro_upgrade&cta_id=agents_cost_savings_pro_checkout&cta_placement=agents_cost_savings_page&plan_id=pro" class="secondary">Or start Pro at $19/mo →</a>
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<h2>Related reading</h2>
|
|
140
|
+
<ul>
|
|
141
|
+
<li><a href="/codex-enterprise">ThumbGate for Codex in the Enterprise</a> — the same prevention story for the OpenAI×Dell distribution wave.</li>
|
|
142
|
+
<li><a href="/agent-manager">ThumbGate for the Agent Manager</a> — the role inside the org that owns "what are our agents costing us."</li>
|
|
143
|
+
<li><a href="/dashboard">Dashboard demo</a> — the $ saved number rendered against demo data, so you can see the shape before installing.</li>
|
|
144
|
+
</ul>
|
|
145
|
+
|
|
146
|
+
<div class="footer-links">
|
|
147
|
+
Built for teams who watched their Claude bill spike, installed a FinOps dashboard, and realized the dashboard only told them <em>which</em> failed agent loop ran the meter — not how to stop it.
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</body>
|
|
151
|
+
</html>
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>AI Malpractice Prevention for Law Firms — ThumbGate</title>
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
|
+
<meta name="description" content="Your AI intake agent can commit unauthorized practice of law, miss a conflict, or breach privilege — usually all three. ThumbGate physically blocks each at the tool-call boundary, with an audit trail your malpractice carrier can read.">
|
|
9
|
+
<meta property="og:title" content="AI Malpractice Prevention for Law Firms">
|
|
10
|
+
<meta property="og:description" content="Runtime governance for legal AI agents — block UPL, miss-conflict, and privilege breach at the tool-call boundary. ABA Formal Op. 512-ready audit trail.">
|
|
11
|
+
<meta property="og:type" content="article">
|
|
12
|
+
<meta property="og:image" content="https://thumbgate-production.up.railway.app/og.png">
|
|
13
|
+
<link rel="canonical" href="https://thumbgate-production.up.railway.app/ai-malpractice-prevention">
|
|
14
|
+
<script type="application/ld+json">
|
|
15
|
+
{
|
|
16
|
+
"@context": "https://schema.org",
|
|
17
|
+
"@type": "TechArticle",
|
|
18
|
+
"headline": "AI Malpractice Prevention for Law Firms",
|
|
19
|
+
"description": "ThumbGate is a runtime governance layer that physically blocks AI legal-assistant agents from committing unauthorized practice of law, missing conflicts, or breaching privilege.",
|
|
20
|
+
"datePublished": "2026-05-21",
|
|
21
|
+
"dateModified": "2026-05-21",
|
|
22
|
+
"author": { "@type": "Person", "name": "Igor Ganapolsky", "url": "https://github.com/IgorGanapolsky" },
|
|
23
|
+
"publisher": { "@type": "Organization", "name": "ThumbGate", "url": "https://thumbgate-production.up.railway.app" },
|
|
24
|
+
"about": [
|
|
25
|
+
{ "@type": "Thing", "name": "Legal AI" },
|
|
26
|
+
{ "@type": "Thing", "name": "Unauthorized Practice of Law" },
|
|
27
|
+
{ "@type": "Thing", "name": "Attorney-Client Privilege" },
|
|
28
|
+
{ "@type": "Thing", "name": "ABA Model Rules" },
|
|
29
|
+
{ "@type": "Thing", "name": "Conflict of Interest Check" }
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
<style>
|
|
34
|
+
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
35
|
+
:root { --bg:#0a0a0b; --card:#161618; --border:#222225; --text:#e8e8ec; --muted:#8b8b94; --cyan:#22d3ee; --red:#f87171; --green:#34d399; }
|
|
36
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--bg); color: var(--text); line-height: 1.7; }
|
|
37
|
+
.container { max-width: 860px; margin: 0 auto; padding: 2rem 1.5rem 4rem; }
|
|
38
|
+
nav { padding: 1rem 2rem; border-bottom: 1px solid var(--border); display:flex; gap:1.5rem; flex-wrap:wrap; }
|
|
39
|
+
nav a { color: var(--muted); text-decoration:none; font-size:0.9rem; }
|
|
40
|
+
nav .brand { color: var(--text); font-weight:700; }
|
|
41
|
+
.pill { display:inline-block; font-size:0.75rem; letter-spacing:0.08em; text-transform:uppercase; color:var(--cyan); background:rgba(34,211,238,0.08); border:1px solid rgba(34,211,238,0.2); padding:4px 12px; border-radius:100px; margin-top:1.5rem; font-weight:600; }
|
|
42
|
+
h1 { font-size:2.2rem; line-height:1.15; margin:1rem 0 1rem; }
|
|
43
|
+
h2 { font-size:1.45rem; margin:2.2rem 0 1rem; color:var(--cyan); }
|
|
44
|
+
h3 { margin:0.6rem 0; font-size:1rem; }
|
|
45
|
+
p, li { margin-bottom:0.75rem; }
|
|
46
|
+
ul, ol { padding-left:1.25rem; }
|
|
47
|
+
.card { background: var(--card); border:1px solid var(--border); border-radius:12px; padding:1.25rem; margin:1rem 0; }
|
|
48
|
+
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:1rem; margin:1rem 0; }
|
|
49
|
+
.grid .card h3 { color:var(--cyan); }
|
|
50
|
+
.scenario { border-left:3px solid var(--red); padding:0.9rem 1.1rem; margin:1rem 0; background:rgba(248,113,113,0.04); border-radius:6px; }
|
|
51
|
+
.scenario .label { display:inline-block; font-size:0.7rem; letter-spacing:0.08em; text-transform:uppercase; color:var(--red); font-weight:700; margin-bottom:0.5rem; }
|
|
52
|
+
.scenario .resolve { display:inline-block; font-size:0.7rem; letter-spacing:0.08em; text-transform:uppercase; color:var(--green); font-weight:700; margin:0.6rem 0 0.3rem; }
|
|
53
|
+
.cta { display:inline-block; background:var(--cyan); color:#000; padding:0.8rem 1.2rem; border-radius:8px; text-decoration:none; font-weight:700; }
|
|
54
|
+
.secondary { color:var(--cyan); text-decoration:underline; margin-left:1rem; }
|
|
55
|
+
.quote { border-left:3px solid var(--cyan); padding:0.75rem 1rem; margin:1rem 0; color:var(--muted); font-style:italic; }
|
|
56
|
+
code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background:#0f0f11; border:1px solid var(--border); border-radius:6px; padding:0.15rem 0.4rem; font-size:0.9rem; }
|
|
57
|
+
pre { padding:0.85rem 1rem; overflow-x:auto; }
|
|
58
|
+
.footer-links { margin-top:2.5rem; padding-top:1.25rem; border-top:1px solid var(--border); color:var(--muted); font-size:0.9rem; }
|
|
59
|
+
.footer-links a { color:var(--cyan); text-decoration:none; }
|
|
60
|
+
table.compliance { width:100%; border-collapse:collapse; margin:1rem 0; font-size:0.95rem; }
|
|
61
|
+
table.compliance th, table.compliance td { padding:0.6rem 0.8rem; border-bottom:1px solid var(--border); text-align:left; vertical-align:top; }
|
|
62
|
+
table.compliance th { color:var(--cyan); font-size:0.8rem; text-transform:uppercase; letter-spacing:0.05em; }
|
|
63
|
+
.rule-cite { color:var(--cyan); font-weight:600; }
|
|
64
|
+
</style>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<nav>
|
|
68
|
+
<a href="/" class="brand">ThumbGate</a>
|
|
69
|
+
<a href="/agent-manager">Agent Manager</a>
|
|
70
|
+
<a href="/codex-enterprise">Codex Enterprise</a>
|
|
71
|
+
<a href="/agents-cost-savings">FinOps for Agents</a>
|
|
72
|
+
<a href="/federal">Federal</a>
|
|
73
|
+
<a href="/dashboard">Dashboard demo</a>
|
|
74
|
+
<a href="https://github.com/IgorGanapolsky/ThumbGate" target="_blank" rel="noopener">GitHub</a>
|
|
75
|
+
</nav>
|
|
76
|
+
<div class="container">
|
|
77
|
+
<span class="pill">AI Malpractice Prevention</span>
|
|
78
|
+
<h1>Your AI intake agent can commit UPL, miss a conflict, or breach privilege — usually all three. ThumbGate prevents each at the tool-call boundary.</h1>
|
|
79
|
+
<p>2025 produced <strong>66 documented court sanctions against attorneys</strong> for AI-generated fake citations and related failures, with fines up to $31,000. That is just the public surface. The internal events — UPL-shaped responses from intake bots, conflict misses, privilege leaks to external LLM processors — are happening at every firm that deployed generative AI in the last 18 months, and most of them are not yet surfacing in OPR review or malpractice claims because the audit trail to catch them doesn't exist.</p>
|
|
80
|
+
<p>ThumbGate is the runtime layer that catches them <em>before</em> they happen. Every agent action — every API call, every document fetch, every drafted message — passes through a PreToolUse gate that fires before the action executes. Known-bad shapes are blocked with the audit trail your malpractice carrier and your OPR review actually want to read.</p>
|
|
81
|
+
<p>The framing matters: ThumbGate isn't another legal AI tool your innovation team has to vet. It's the <strong>vetting-collapse layer</strong> that sits between the agents you've already adopted — Harvey, Copilot, Legora, internal scripts, whatever a client mandates next quarter — and the tool calls those agents try to make. One control plane, every model, every matter, every output.</p>
|
|
82
|
+
|
|
83
|
+
<h2>The three failure modes ThumbGate prevents</h2>
|
|
84
|
+
<div class="grid">
|
|
85
|
+
<div class="card">
|
|
86
|
+
<h3>1. Unauthorized practice of law <span class="rule-cite">(Rule 5.5)</span></h3>
|
|
87
|
+
<p>The AI intake bot tells a prospect <em>"based on what you've described, you have a strong case for breach of fiduciary duty."</em> That's legal advice from a non-lawyer. Under Rule 5.5 — and under most state bar interpretations — the firm is on the hook. ThumbGate's UPL gate intercepts response candidates that match advice-shaped patterns (predictions, recommendations, outcome assertions) and replaces them with an intake hand-off to a licensed attorney.</p>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="card">
|
|
90
|
+
<h3>2. Missed conflicts <span class="rule-cite">(Rules 1.7, 1.9, 1.10)</span></h3>
|
|
91
|
+
<p>The agent processes a new-client inquiry at 11pm on Sunday, schedules an intake call for Monday, sends a generic engagement letter — and only then runs the conflict check that finds the prospect is the opposing party in an existing matter. By then the firm has already received confidential information from the prospect. ThumbGate's conflict gate requires a positive clearance from the firm's adverse-parties list <em>before</em> the agent can accept any intake content beyond the initial routing question.</p>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="card">
|
|
94
|
+
<h3>3. Privilege breach <span class="rule-cite">(Rule 1.6 + state evidence rules)</span></h3>
|
|
95
|
+
<p>An associate uses the firm's AI assistant to summarize a privileged deposition. The agent calls a public LLM endpoint to "improve the summary." Privileged content just left the firm's infrastructure to a third-party processor that has no equivalent privilege protection. ThumbGate's egress gate inspects every outbound API call from agents and blocks transmissions of content matching privilege-policy patterns (matter ID, client name, "Attorney Work Product" markers, custom firm classifiers) to non-approved processors.</p>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<h2>How the prevention actually works</h2>
|
|
100
|
+
<p>The mechanism is deliberately simple. ThumbGate sits between the agent and the world as a hook layer; every tool call the agent attempts (HTTP request, file read, database query, generated response delivery) passes through a <code>PreToolUse</code> gate first. The gate evaluates the proposed action against a lesson database built from your firm's own observed failures plus a library of legal-vertical defaults shipped with the product.</p>
|
|
101
|
+
<ul>
|
|
102
|
+
<li><strong>Promoted rules block known-bad shapes.</strong> When the same failure pattern recurs three or more times — silently, without a human even noticing — silent-failure clustering surfaces it as a candidate rule. A pre-promotion eval verifies precision before it joins the active gate set.</li>
|
|
103
|
+
<li><strong>Every block is logged with provenance.</strong> What was attempted, what rule fired, what corrective action the agent was redirected to. That log is the artifact your malpractice carrier and your OPR review actually want — not a vendor's "trust me" assurance.</li>
|
|
104
|
+
<li><strong>Nothing leaves your boundary.</strong> ThumbGate runs in-process or as a sidecar in your Azure / AWS tenant or on-prem. No client data, no privileged content, no matter metadata traverses our infrastructure. The hosted dashboard is optional and never receives privileged payloads — only counters and rule metadata.</li>
|
|
105
|
+
</ul>
|
|
106
|
+
|
|
107
|
+
<h2>Three scenarios from real firm pain</h2>
|
|
108
|
+
|
|
109
|
+
<div class="scenario">
|
|
110
|
+
<span class="label">Scenario 1 — after-hours UPL</span>
|
|
111
|
+
<p><strong>Without ThumbGate:</strong> Saturday 11 PM. An estate-planning prospect uses the firm's website AI assistant to ask "if I name my brother as executor but he lives in another state, does that cause problems?" The assistant, trained on legal content, replies with a 4-paragraph explanation of out-of-state-executor bonds and tax implications. That's legal advice. The firm's malpractice carrier finds out 8 months later when the prospect (who hired a different firm) sues over an estate dispute and the deposition surfaces the chatbot transcript.</p>
|
|
112
|
+
<span class="resolve">With ThumbGate</span>
|
|
113
|
+
<p>The UPL gate matches the response shape (jurisdictional analysis + recommendation) against the promoted rule for "advice-shaped output from non-attorney source." The assistant's response is intercepted before delivery and replaced with: <em>"That's a legal question that needs a licensed attorney in your state. I can book you a 30-min consult with one of our estate-planning attorneys — would Monday at 10 AM work?"</em> The intake gets scheduled, the firm captures the lead, no UPL ever occurs, and the audit log shows the firm prevented the failure mode.</p>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="scenario">
|
|
117
|
+
<span class="label">Scenario 2 — adverse-party conflict miss</span>
|
|
118
|
+
<p><strong>Without ThumbGate:</strong> A junior associate uses the firm's AI document-fetcher agent to pull "all recent filings involving Acme Corporation" for due diligence on a new M&A engagement. The agent retrieves dozens of documents — including filings from a matter where the firm represents Acme's largest competitor. Privileged work product from the existing matter now sits in the associate's local cache. The firm has just created a screen problem at minimum; at worst, a disqualification motion six weeks later.</p>
|
|
119
|
+
<span class="resolve">With ThumbGate</span>
|
|
120
|
+
<p>The conflict gate fires on every document-fetch tool call. Before the fetch executes, it cross-references the requesting matter ID against the firm's adverse-parties list. The Acme-competitor matter is flagged. The fetch is blocked and the agent is redirected to: <em>"Acme Corporation appears as an adverse party in matter [REDACTED]. This fetch is blocked. Contact [matter-attorney email] to discuss whether an ethics screen is needed before proceeding."</em> No cross-contamination, no waiver risk.</p>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="scenario">
|
|
124
|
+
<span class="label">Scenario 3 — egress privilege breach</span>
|
|
125
|
+
<p><strong>Without ThumbGate:</strong> A partner pastes a 200-page deposition transcript into the firm's "AI Brief Assistant" and asks for a summary. The Brief Assistant, under the hood, calls an external LLM API for the long-context summarization step because the in-house model's context window is too short. Privileged deposition content just left the firm's network to a vendor whose terms of service include "we may use submitted content to improve our models." Privilege waiver argument waiting to happen.</p>
|
|
126
|
+
<span class="resolve">With ThumbGate</span>
|
|
127
|
+
<p>The egress gate inspects every outbound API call. The deposition's metadata header includes the firm's "Attorney Work Product" marker. The call to the external LLM is blocked. The agent is redirected to a privilege-safe alternative: in-tenant summarization via the firm's Azure OpenAI deployment (which carries the firm's BAA) or chunked summarization that stays inside the model's context window. The transcript never leaves the firm's boundary; the audit log records the block.</p>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<h2>Compliance matrix — what ThumbGate maps to</h2>
|
|
131
|
+
<table class="compliance">
|
|
132
|
+
<thead>
|
|
133
|
+
<tr><th>Authority</th><th>Requirement</th><th>ThumbGate's mechanism</th></tr>
|
|
134
|
+
</thead>
|
|
135
|
+
<tbody>
|
|
136
|
+
<tr><td>ABA Model Rule 1.1 + cmt. 8</td><td>Competence in the benefits and risks of relevant technology</td><td>Audit trail of every agent action gives partners evidence of supervision-grade understanding</td></tr>
|
|
137
|
+
<tr><td>ABA Model Rule 1.6</td><td>Protect confidential information</td><td>Egress gate blocks outbound calls carrying client-confidential or privileged content to non-approved processors</td></tr>
|
|
138
|
+
<tr><td>ABA Model Rule 5.3</td><td>Supervise non-lawyer assistance, including AI tools</td><td>Per-call evidence + per-rule provenance is the supervision artifact</td></tr>
|
|
139
|
+
<tr><td>ABA Model Rule 5.5</td><td>No unauthorized practice of law</td><td>UPL gate intercepts advice-shaped output from non-attorney agents pre-delivery</td></tr>
|
|
140
|
+
<tr><td>ABA Formal Op. 512 (Jul 2024)</td><td>Verify AI output, protect confidentiality, consider client disclosure</td><td>Audit trail covers the verification + disclosure questions in one artifact</td></tr>
|
|
141
|
+
<tr><td>Rules 1.7 / 1.9 / 1.10</td><td>Conflict of interest screening</td><td>Conflict gate requires positive clearance against adverse-parties list before agent can accept intake content</td></tr>
|
|
142
|
+
</tbody>
|
|
143
|
+
</table>
|
|
144
|
+
|
|
145
|
+
<h2>Why this is the Chief Pricing & Innovation Officer's problem (not just the GC's)</h2>
|
|
146
|
+
<p>Every alternative-fee arrangement carries an implicit risk reserve against malpractice tail events. A single sanction, disqualification motion, or bar complaint compresses AFA margins for the entire vintage of matters affected. The events ThumbGate prevents are precisely the events that trigger reserves. Framed in pricing terms, the runtime gate is a <strong>reserve-cost reduction control</strong>: prevented sanctions are dollars not held against alternative-fee matter margins. The audit trail is the artifact the firm's malpractice carrier reads when arguing for a premium reduction at the next renewal.</p>
|
|
147
|
+
<p>Standardization gets easier the same way. Each new client mandate ("you must use Tool X for our matters, you may not use Tool Y") becomes a policy update at the gate, not a per-tool re-vetting cycle. The vetting work that takes calendar weeks today becomes a one-line rule in the gate config — applied across every existing agent without re-implementation.</p>
|
|
148
|
+
|
|
149
|
+
<h2>The deployment story (security committee's first objection answered first)</h2>
|
|
150
|
+
<ul>
|
|
151
|
+
<li><strong>Runs inside your boundary.</strong> ThumbGate is a Node.js process that runs as a sidecar in your Azure / AWS / on-prem environment. No client data, no privileged content, no matter metadata traverses our infrastructure.</li>
|
|
152
|
+
<li><strong>Microsoft 365 / Azure OpenAI compatible.</strong> If your firm is on the Microsoft stack, ThumbGate gates calls to your Azure OpenAI endpoint just as cleanly as it gates Anthropic, OpenAI public API, or any other LLM.</li>
|
|
153
|
+
<li><strong>BAA / DPA path.</strong> The optional hosted dashboard (analytics + rule library) carries a BAA. The runtime gate layer carries no BAA need because it never receives PHI / PII / privileged content — only counters and metadata.</li>
|
|
154
|
+
<li><strong>SOC 2 Type II in progress.</strong> Audit underway; final report Q3 2026. Pilot engagements can proceed under SOC 2 Type I + a Vendor Security Questionnaire response on file.</li>
|
|
155
|
+
<li><strong>No model lock-in.</strong> ThumbGate is vendor-neutral on the LLM. It works equally over Claude (Anthropic + AWS Bedrock), GPT-4 (OpenAI + Azure), Gemini, Llama-on-Mosaic, or any HTTP-callable model.</li>
|
|
156
|
+
</ul>
|
|
157
|
+
|
|
158
|
+
<h2>Pilot shape</h2>
|
|
159
|
+
<p>The recommended first engagement is a 30-day pilot focused on a single intake-channel and a single practice-area-specific conflict-list. Two of your attorneys, two of your IT/innovation staff, and one ThumbGate engineer running biweekly sync calls. Pilot deliverable: a documented set of promoted gate rules specific to your firm's risk profile, the audit-trail format reviewed by your malpractice carrier or OPR liaison, and a written go/no-go recommendation on firm-wide rollout. Investment for the pilot is positioned as a Workflow Hardening Sprint — fixed-scope, fixed-price, no per-attorney metering during evaluation.</p>
|
|
160
|
+
|
|
161
|
+
<div class="quote">"The job of legal-AI governance isn't 'tell the model to be more careful.' It's the tool-call boundary, with an audit trail that survives the deposition."</div>
|
|
162
|
+
|
|
163
|
+
<div class="card">
|
|
164
|
+
<p><strong>Next step: a 25-min walkthrough on a hypothetical intake-and-conflict scenario specific to your firm.</strong></p>
|
|
165
|
+
<p>
|
|
166
|
+
<a href="mailto:iganapolsky@gmail.com?subject=ThumbGate%20AI%20Malpractice%20Prevention%20-%20demo%20request&body=Hi%20Igor%2C%0A%0AI%27m%20at%20%5Bfirm%5D%20and%20saw%20your%20AI%20malpractice%20prevention%20page.%20%0A%0AWe%27re%20evaluating%20how%20to%20govern%20our%20agentic%20legal-AI%20deployment%20and%20I%27d%20like%20to%20see%20a%20walkthrough.%20%0A%0AMy%20practice%20area%20is%3A%20%5B%5D%0AThe%20intake%20channel%20we%27re%20most%20worried%20about%3A%20%5B%5D%0A%0ABest%2C" class="cta">Book a 25-min walkthrough</a>
|
|
167
|
+
<a href="/agent-manager" class="secondary">Or read the Agent Manager role framing →</a>
|
|
168
|
+
</p>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<h2>Related reading</h2>
|
|
172
|
+
<ul>
|
|
173
|
+
<li><a href="/agents-cost-savings">FinOps for AI agents</a> — the cost-control composition for firms running multiple agents across matters.</li>
|
|
174
|
+
<li><a href="/federal">Federal / regulated workloads</a> — the same compliance bones (deployable inside your tenant, audit trail, SOC 2 path) that work for federal also satisfy law-firm professional-responsibility committees.</li>
|
|
175
|
+
<li><a href="/agent-manager">ThumbGate for the Agent Manager</a> — the role inside the firm that owns "what are our agents costing us, and what did we stop them from doing?"</li>
|
|
176
|
+
</ul>
|
|
177
|
+
|
|
178
|
+
<div class="footer-links">
|
|
179
|
+
Built for law firms whose Innovation function has been told to "make AI work in intake and document review" but hasn't been given the safety net that lets their partners sign off without losing sleep. ABA Formal Op. 512 is the bar; ThumbGate is the floor.
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</body>
|
|
183
|
+
</html>
|
package/public/codex-plugin.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<meta property="og:type" content="website">
|
|
13
13
|
<meta property="og:url" content="https://thumbgate-production.up.railway.app/codex-plugin">
|
|
14
14
|
<link rel="canonical" href="https://thumbgate-production.up.railway.app/codex-plugin">
|
|
15
|
-
<link rel="llm-context" href="/
|
|
15
|
+
<link rel="llm-context" href="/llm-context.md" type="text/markdown">
|
|
16
16
|
|
|
17
17
|
<script type="application/ld+json">
|
|
18
18
|
{
|
package/public/index.html
CHANGED
|
@@ -19,7 +19,7 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
19
19
|
<meta property="og:image" content="https://thumbgate-production.up.railway.app/og.png">
|
|
20
20
|
<meta name="twitter:card" content="summary_large_image">
|
|
21
21
|
<meta name="twitter:image" content="https://thumbgate-production.up.railway.app/og.png">
|
|
22
|
-
<meta name="thumbgate-version" content="1.
|
|
22
|
+
<meta name="thumbgate-version" content="1.23.0">
|
|
23
23
|
<meta name="keywords" content="ThumbGate, thumbgate, AI agent orchestration, AI experience orchestration, agent enforcement layer, save LLM tokens, reduce Claude API cost, reduce OpenAI cost, AI agent token savings, prevent LLM retries, prevent hallucination retries, stop AI token waste, pre-action checks, agent governance, Claude Code, Cursor, Codex, Gemini, Amp, Cline, OpenCode, workflow hardening, context engineering, AI authenticity, brand authenticity AI">
|
|
24
24
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
25
25
|
|
|
@@ -721,7 +721,7 @@ __GA_BOOTSTRAP__
|
|
|
721
721
|
</div>
|
|
722
722
|
<a href="/go/install?utm_source=website&utm_medium=hero_cta&utm_campaign=install_free&cta_id=hero_install_cli&cta_placement=hero" onclick="event.preventDefault(); navigator.clipboard.writeText('npx thumbgate init'); this.textContent='Copied ✓ — paste in your repo'; setTimeout(()=>{this.textContent='Install Free CLI'},2000); try{posthog.capture('hero_install_click',{cta:'install_cli'})}catch(_){}" class="btn-gpt-page btn-install-hero" title="Click to copy: npx thumbgate init">Install Free CLI</a>
|
|
723
723
|
<a href="#workflow-sprint-intake" onclick="try{posthog.capture('hero_sprint_click',{cta:'sprint_intake'})}catch(_){};sendFirstPartyTelemetry('hero_sprint_intake_started',{ctaId:'hero_workflow_sprint',ctaPlacement:'hero',offer:'workflow_sprint'});" class="btn-pro-page hero-pro">Talk to me — Workflow Hardening Sprint →</a>
|
|
724
|
-
<a href="#demo" onclick="try{posthog.capture('hero_demo_click',{cta:'
|
|
724
|
+
<a href="#demo" onclick="try{posthog.capture('hero_demo_click',{cta:'see_enforcement'})}catch(_){};sendFirstPartyTelemetry('hero_demo_clicked',{ctaId:'hero_see_enforcement',ctaPlacement:'hero'});" class="btn-free" style="font-size:15px;padding:14px 22px;">See the enforcement in action</a>
|
|
725
725
|
</div>
|
|
726
726
|
|
|
727
727
|
<div class="hero-trust-bar">
|
|
@@ -1492,7 +1492,7 @@ __GA_BOOTSTRAP__
|
|
|
1492
1492
|
<a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
|
|
1493
1493
|
<a href="/blog">Blog</a>
|
|
1494
1494
|
</div>
|
|
1495
|
-
<span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.
|
|
1495
|
+
<span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.23.0</span>
|
|
1496
1496
|
</div>
|
|
1497
1497
|
</footer>
|
|
1498
1498
|
|
package/public/numbers.html
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"alternateName": "thumbgate",
|
|
26
26
|
"applicationCategory": "DeveloperApplication",
|
|
27
27
|
"operatingSystem": "Cross-platform, Node.js >=18.18.0",
|
|
28
|
-
"softwareVersion": "1.
|
|
28
|
+
"softwareVersion": "1.23.0",
|
|
29
29
|
"url": "https://thumbgate-production.up.railway.app/numbers",
|
|
30
30
|
"dateModified": "2026-05-07",
|
|
31
31
|
"creator": {
|
|
@@ -202,7 +202,7 @@
|
|
|
202
202
|
<main class="container">
|
|
203
203
|
<h1>The Numbers</h1>
|
|
204
204
|
<p class="subtitle">Generated first-party operational snapshot from the ThumbGate runtime. This is not customer traction, install volume, revenue, or proof that a configured gate has fired.</p>
|
|
205
|
-
<div class="freshness">Updated: 2026-05-07 · Version 1.
|
|
205
|
+
<div class="freshness">Updated: 2026-05-07 · Version 1.23.0</div>
|
|
206
206
|
<div class="truth-note"><strong>Read this first:</strong> configured checks are inventory. Recorded blocks and warnings are usage evidence. This snapshot currently reports 0 recorded hard-block event(s) and 0 recorded warning event(s).</div>
|
|
207
207
|
|
|
208
208
|
<h2>Gate enforcement</h2>
|
package/public/pricing.html
CHANGED
|
@@ -213,7 +213,7 @@ __GA_BOOTSTRAP__
|
|
|
213
213
|
<div class="container">
|
|
214
214
|
<a href="/" class="nav-logo">ThumbGate</a>
|
|
215
215
|
<div class="nav-links">
|
|
216
|
-
<a href="/#
|
|
216
|
+
<a href="/#how-it-works">Features</a>
|
|
217
217
|
<a href="/pricing" style="color:var(--text);">Pricing</a>
|
|
218
218
|
<a href="/guide">Guide</a>
|
|
219
219
|
<a href="/dashboard">Dashboard</a>
|
package/scripts/cli-telemetry.js
CHANGED
|
@@ -10,6 +10,9 @@ const _DEFAULT_TELEMETRY_HOST = 'https://thumbgate-production.up.railway.app';
|
|
|
10
10
|
const TELEMETRY_ENDPOINT = `${process.env.THUMBGATE_API_URL || _DEFAULT_TELEMETRY_HOST}/v1/telemetry/ping`;
|
|
11
11
|
const INSTALL_ID_PATH = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.thumbgate', 'install-id');
|
|
12
12
|
|
|
13
|
+
// Session ID: random per process invocation. Groups all events from one CLI run.
|
|
14
|
+
const SESSION_ID = crypto.randomUUID ? crypto.randomUUID() : crypto.randomBytes(16).toString('hex');
|
|
15
|
+
|
|
13
16
|
/**
|
|
14
17
|
* Get or create a stable anonymous install ID.
|
|
15
18
|
* This is NOT tied to any personal info — it's a random UUID stored locally.
|
|
@@ -61,7 +64,9 @@ function trackEvent(eventType, metadata = {}) {
|
|
|
61
64
|
const payload = JSON.stringify({
|
|
62
65
|
eventType,
|
|
63
66
|
installId: getInstallId(),
|
|
67
|
+
sessionId: SESSION_ID,
|
|
64
68
|
visitorType: classifyInstall(),
|
|
69
|
+
clientType: 'cli',
|
|
65
70
|
platform: os.platform(),
|
|
66
71
|
arch: os.arch(),
|
|
67
72
|
nodeVersion: process.version,
|
|
@@ -87,4 +92,4 @@ function trackEvent(eventType, metadata = {}) {
|
|
|
87
92
|
} catch (_) {} // never crash the CLI
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
module.exports = { trackEvent, getInstallId, classifyInstall, INSTALL_ID_PATH };
|
|
95
|
+
module.exports = { trackEvent, getInstallId, classifyInstall, INSTALL_ID_PATH, SESSION_ID };
|
package/scripts/gates-engine.js
CHANGED
|
@@ -7,7 +7,7 @@ const crypto = require('crypto');
|
|
|
7
7
|
const { execSync, execFileSync } = require('child_process');
|
|
8
8
|
const { loadOptionalModule } = require('./private-core-boundary');
|
|
9
9
|
|
|
10
|
-
const { isProTier, FREE_TIER_MAX_GATES } = require('./rate-limiter');
|
|
10
|
+
const { isProTier, isInTrialPeriod, FREE_TIER_MAX_GATES, FREE_TIER_DAILY_BLOCKS, todayKey } = require('./rate-limiter');
|
|
11
11
|
const {
|
|
12
12
|
DEFAULT_BASE_BRANCH,
|
|
13
13
|
evaluateOperationalIntegrity,
|
|
@@ -466,6 +466,69 @@ function recordStat(gateId, action, gate) {
|
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
470
|
+
// Free-tier daily block cap
|
|
471
|
+
// ---------------------------------------------------------------------------
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Count today's gate blocks from stats. Free tier gets FREE_TIER_DAILY_BLOCKS
|
|
475
|
+
* blocks/day. After the limit, deny → warn + upgrade CTA so the action proceeds
|
|
476
|
+
* but the user sees they lost protection.
|
|
477
|
+
*/
|
|
478
|
+
function getTodayBlockCount() {
|
|
479
|
+
const stats = loadStats();
|
|
480
|
+
const today = todayKey();
|
|
481
|
+
if (!stats.dailyBlocks || !stats.dailyBlocks[today]) return 0;
|
|
482
|
+
return stats.dailyBlocks[today];
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function incrementTodayBlockCount() {
|
|
486
|
+
const stats = loadStats();
|
|
487
|
+
const today = todayKey();
|
|
488
|
+
if (!stats.dailyBlocks) stats.dailyBlocks = {};
|
|
489
|
+
// Clean old dates (keep only last 7 days to prevent unbounded growth)
|
|
490
|
+
const keys = Object.keys(stats.dailyBlocks);
|
|
491
|
+
if (keys.length > 7) {
|
|
492
|
+
keys.sort();
|
|
493
|
+
for (const k of keys.slice(0, keys.length - 7)) {
|
|
494
|
+
delete stats.dailyBlocks[k];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
stats.dailyBlocks[today] = (stats.dailyBlocks[today] || 0) + 1;
|
|
498
|
+
saveStats(stats);
|
|
499
|
+
return stats.dailyBlocks[today];
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* If the user is free-tier and has exceeded daily block limit, downgrade
|
|
504
|
+
* a deny result to a warn with an upgrade CTA. Returns null if no cap applies.
|
|
505
|
+
*/
|
|
506
|
+
function applyDailyBlockCap(denyResult) {
|
|
507
|
+
// Pro, trial, CI, and THUMBGATE_NO_RATE_LIMIT users are uncapped
|
|
508
|
+
if (isProTier()) return null;
|
|
509
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS) return null;
|
|
510
|
+
|
|
511
|
+
const todayCount = getTodayBlockCount();
|
|
512
|
+
if (todayCount < FREE_TIER_DAILY_BLOCKS) {
|
|
513
|
+
// Under limit: allow the block, increment counter
|
|
514
|
+
incrementTodayBlockCount();
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Over limit: downgrade deny → warn with upgrade CTA
|
|
519
|
+
const remaining = 0;
|
|
520
|
+
return {
|
|
521
|
+
decision: 'warn',
|
|
522
|
+
gate: denyResult.gate,
|
|
523
|
+
message: `⚠️ ${denyResult.message}\n\n🔓 Daily protection limit reached (${FREE_TIER_DAILY_BLOCKS}/${FREE_TIER_DAILY_BLOCKS} blocks used). This action was allowed through. Upgrade for unlimited protection: https://thumbgate.ai/go/pro`,
|
|
524
|
+
severity: denyResult.severity,
|
|
525
|
+
reasoning: (denyResult.reasoning || []).concat([
|
|
526
|
+
`Free-tier daily block limit (${FREE_TIER_DAILY_BLOCKS}) exceeded — deny downgraded to warn`,
|
|
527
|
+
]),
|
|
528
|
+
dailyBlockCapApplied: true,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
469
532
|
// ---------------------------------------------------------------------------
|
|
470
533
|
// Reasoning chain builder
|
|
471
534
|
// ---------------------------------------------------------------------------
|
|
@@ -1491,11 +1554,19 @@ async function evaluateGatesAsync(toolName, toolInput, configPath) {
|
|
|
1491
1554
|
});
|
|
1492
1555
|
|
|
1493
1556
|
if (gate.action === 'block') {
|
|
1557
|
+
const denyResult = { decision: 'deny', gate: gate.id, message, severity: gate.severity, reasoning };
|
|
1558
|
+
// Free-tier daily block cap: after N blocks/day, deny → warn + upgrade CTA
|
|
1559
|
+
const cappedResult = applyDailyBlockCap(denyResult);
|
|
1560
|
+
if (cappedResult) {
|
|
1561
|
+
recordStat(gate.id, 'warn', gate);
|
|
1562
|
+
const auditRecord = recordAuditEvent({ toolName, toolInput, decision: 'warn', gateId: gate.id, message: cappedResult.message, severity: gate.severity, source: 'gates-engine', dailyBlockCapApplied: true });
|
|
1563
|
+
auditToFeedback(auditRecord);
|
|
1564
|
+
return cappedResult;
|
|
1565
|
+
}
|
|
1494
1566
|
recordStat(gate.id, 'block', gate);
|
|
1495
|
-
const result = { decision: 'deny', gate: gate.id, message, severity: gate.severity, reasoning };
|
|
1496
1567
|
const auditRecord = recordAuditEvent({ toolName, toolInput, decision: 'deny', gateId: gate.id, message, severity: gate.severity, source: 'gates-engine' });
|
|
1497
1568
|
auditToFeedback(auditRecord);
|
|
1498
|
-
return
|
|
1569
|
+
return denyResult;
|
|
1499
1570
|
}
|
|
1500
1571
|
|
|
1501
1572
|
if (gate.action === 'approve') {
|
|
@@ -1653,11 +1724,19 @@ function evaluateGates(toolName, toolInput, configPath) {
|
|
|
1653
1724
|
const reasoning = buildReasoning(gate, toolName, toolInput, matchDetails);
|
|
1654
1725
|
|
|
1655
1726
|
if (gate.action === 'block') {
|
|
1727
|
+
const denyResult = { decision: 'deny', gate: gate.id, message, severity: gate.severity, reasoning };
|
|
1728
|
+
// Free-tier daily block cap: after N blocks/day, deny → warn + upgrade CTA
|
|
1729
|
+
const cappedResult = applyDailyBlockCap(denyResult);
|
|
1730
|
+
if (cappedResult) {
|
|
1731
|
+
recordStat(gate.id, 'warn', gate);
|
|
1732
|
+
const auditRecord = recordAuditEvent({ toolName, toolInput, decision: 'warn', gateId: gate.id, message: cappedResult.message, severity: gate.severity, source: 'gates-engine', dailyBlockCapApplied: true });
|
|
1733
|
+
auditToFeedback(auditRecord);
|
|
1734
|
+
return cappedResult;
|
|
1735
|
+
}
|
|
1656
1736
|
recordStat(gate.id, 'block', gate);
|
|
1657
|
-
const result = { decision: 'deny', gate: gate.id, message, severity: gate.severity, reasoning };
|
|
1658
1737
|
const auditRecord = recordAuditEvent({ toolName, toolInput, decision: 'deny', gateId: gate.id, message, severity: gate.severity, source: 'gates-engine' });
|
|
1659
1738
|
auditToFeedback(auditRecord);
|
|
1660
|
-
return
|
|
1739
|
+
return denyResult;
|
|
1661
1740
|
}
|
|
1662
1741
|
|
|
1663
1742
|
if (gate.action === 'approve') {
|
|
@@ -1856,6 +1935,35 @@ function buildReminderOutput(context) {
|
|
|
1856
1935
|
};
|
|
1857
1936
|
}
|
|
1858
1937
|
|
|
1938
|
+
// ---------------------------------------------------------------------------
|
|
1939
|
+
// Upgrade nudge: surfaces Pro value at usage milestones and trial expiry.
|
|
1940
|
+
// Block-action Pro CTA: brief upgrade mention after a deny/warn decision.
|
|
1941
|
+
// Highest-intent moment — user just saw ThumbGate save them from a mistake.
|
|
1942
|
+
// ---------------------------------------------------------------------------
|
|
1943
|
+
|
|
1944
|
+
function buildBlockActionProCta() {
|
|
1945
|
+
try {
|
|
1946
|
+
if (process.env.THUMBGATE_NO_NUDGE === '1') return null;
|
|
1947
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS) return null;
|
|
1948
|
+
if (isProTier()) return null;
|
|
1949
|
+
if (isInTrialPeriod()) return null; // Already have full access
|
|
1950
|
+
|
|
1951
|
+
const stats = loadStats();
|
|
1952
|
+
const totalBlocks = stats.blocked || 0;
|
|
1953
|
+
if (totalBlocks < 5) return null; // Too early — let them experience the product
|
|
1954
|
+
|
|
1955
|
+
if (totalBlocks < 25) {
|
|
1956
|
+
return '\n\n💡 Pro: sync rules across machines + dashboard analytics → thumbgate.ai/go/pro';
|
|
1957
|
+
}
|
|
1958
|
+
if (totalBlocks < 100) {
|
|
1959
|
+
return `\n\n💡 ${totalBlocks} actions blocked. Pro keeps rules in sync everywhere → thumbgate.ai/go/pro ($19/mo)`;
|
|
1960
|
+
}
|
|
1961
|
+
return `\n\n💡 ${totalBlocks} mistakes caught. Your team could use this → thumbgate.ai/go/pro`;
|
|
1962
|
+
} catch (_) {
|
|
1963
|
+
return null;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1859
1967
|
function formatOutput(result, behavioralContext) {
|
|
1860
1968
|
if (!result) {
|
|
1861
1969
|
// No gate matched — inject behavioral context if available
|
|
@@ -1874,11 +1982,12 @@ function formatOutput(result, behavioralContext) {
|
|
|
1874
1982
|
if (result.decision === 'deny') {
|
|
1875
1983
|
const reminder = behavioralContext ? buildReminderOutput(behavioralContext) : {};
|
|
1876
1984
|
const reminderSuffix = behavioralContext ? `\n\nSystem reminder:\n${behavioralContext}` : '';
|
|
1985
|
+
const proCta = buildBlockActionProCta() || '';
|
|
1877
1986
|
return JSON.stringify({
|
|
1878
1987
|
hookSpecificOutput: {
|
|
1879
1988
|
...reminder,
|
|
1880
1989
|
permissionDecision: 'deny',
|
|
1881
|
-
permissionDecisionReason: `[GATE:${result.gate}] ${result.message}${reasoningSuffix}${reminderSuffix}`,
|
|
1990
|
+
permissionDecisionReason: `[GATE:${result.gate}] ${result.message}${reasoningSuffix}${reminderSuffix}${proCta}`,
|
|
1882
1991
|
},
|
|
1883
1992
|
});
|
|
1884
1993
|
}
|
|
@@ -2468,6 +2577,10 @@ module.exports = {
|
|
|
2468
2577
|
isRemoteSideEffectCommand,
|
|
2469
2578
|
evaluateLocalOnlyRemoteSideEffectGate,
|
|
2470
2579
|
PR_THREAD_RESOLUTION_ACTION,
|
|
2580
|
+
buildBlockActionProCta,
|
|
2581
|
+
applyDailyBlockCap,
|
|
2582
|
+
getTodayBlockCount,
|
|
2583
|
+
incrementTodayBlockCount,
|
|
2471
2584
|
};
|
|
2472
2585
|
|
|
2473
2586
|
// ---------------------------------------------------------------------------
|