thumbgate 1.23.1 → 1.23.2
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 +5 -5
- package/.claude-plugin/plugin.json +2 -2
- package/.well-known/llms.txt +26 -11
- package/.well-known/mcp/server-card.json +8 -8
- package/README.md +69 -34
- 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 +39 -16
- package/bin/postinstall.js +11 -22
- package/config/gate-templates.json +72 -0
- package/config/github-about.json +1 -1
- package/config/post-deploy-marketing-pages.json +6 -1
- package/package.json +5 -5
- package/public/agent-manager.html +3 -3
- package/public/agents-cost-savings.html +3 -3
- package/public/ai-malpractice-prevention.html +278 -7
- package/public/blog.html +3 -3
- package/public/codex-enterprise.html +3 -3
- package/public/codex-plugin.html +4 -4
- package/public/compare.html +6 -6
- package/public/dashboard.html +211 -126
- package/public/guide.html +5 -5
- package/public/index.html +156 -47
- package/public/learn.html +24 -10
- package/public/lessons.html +2 -2
- package/public/numbers.html +6 -6
- package/public/pricing.html +6 -5
- package/public/pro.html +1 -0
- package/scripts/billing.js +17 -0
- package/scripts/commercial-offer.js +4 -1
- package/scripts/dashboard.js +53 -1
- package/scripts/gates-engine.js +3 -3
- package/scripts/plausible-server-events.js +2 -1
- package/scripts/rate-limiter.js +16 -12
- package/scripts/seo-gsd.js +167 -1
- package/scripts/telemetry-analytics.js +310 -0
- package/scripts/visitor-journey.js +172 -0
- package/src/api/server.js +65 -29
- package/adapters/chatgpt/openapi.yaml +0 -1705
package/public/dashboard.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>ThumbGate Dashboard — Gate Stats, Agent Inventory, Remediations</title>
|
|
7
7
|
<meta name="description" content="Live ThumbGate dashboard showing gate enforcement stats, agent surface inventory, actionable remediations, prevention impact, and system health for pre-action checks.">
|
|
8
|
-
<link rel="canonical" href="https://thumbgate
|
|
8
|
+
<link rel="canonical" href="https://thumbgate.ai/dashboard">
|
|
9
9
|
<link rel="icon" type="image/png" href="/thumbgate-icon.png">
|
|
10
10
|
<link rel="apple-touch-icon" href="/assets/brand/thumbgate-mark.svg">
|
|
11
11
|
<meta name="robots" content="noindex">
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"name": "ThumbGate Dashboard",
|
|
19
19
|
"applicationCategory": "DeveloperApplication",
|
|
20
20
|
"operatingSystem": "Cross-platform",
|
|
21
|
-
"url": "https://thumbgate
|
|
21
|
+
"url": "https://thumbgate.ai/dashboard",
|
|
22
22
|
"dateModified": "2026-04-20",
|
|
23
23
|
"creator": {
|
|
24
24
|
"@type": "Person",
|
|
@@ -539,6 +539,91 @@
|
|
|
539
539
|
</div>
|
|
540
540
|
|
|
541
541
|
<script>
|
|
542
|
+
// TG_TOKEN_SAVINGS — browser shim that mirrors scripts/token-savings.js
|
|
543
|
+
// so loadDemo()'s TG_TOKEN_SAVINGS.compute(blockedCalls, deflectedBots)
|
|
544
|
+
// call resolves. Pre-2026-05-26 the dashboard referenced this global
|
|
545
|
+
// without ever defining it, silently halting loadDemo() at line ~1452.
|
|
546
|
+
var TG_TOKEN_SAVINGS = (function () {
|
|
547
|
+
var DEFAULT_MODEL_MIX = { 'claude-sonnet-4-5': 0.80, 'claude-opus-4-6': 0.15, 'claude-haiku-4-5': 0.05 };
|
|
548
|
+
var DEFAULT_MODEL_PRICES = {
|
|
549
|
+
'claude-sonnet-4-5': { input: 3.0, output: 15.0 },
|
|
550
|
+
'claude-opus-4-6': { input: 5.0, output: 25.0 },
|
|
551
|
+
'claude-haiku-4-5': { input: 1.0, output: 5.0 },
|
|
552
|
+
'gpt-4o': { input: 2.50, output: 10.0 }
|
|
553
|
+
};
|
|
554
|
+
var DEFAULT_AVG_INPUT_TOKENS = 2000;
|
|
555
|
+
var DEFAULT_AVG_OUTPUT_TOKENS = 600;
|
|
556
|
+
function clamp(n, fb) { var v = Number(n); return (isFinite(v) && v >= 0) ? v : fb; }
|
|
557
|
+
function blended(mix, prices) {
|
|
558
|
+
var i = 0, o = 0, w = 0;
|
|
559
|
+
for (var m in mix) {
|
|
560
|
+
var ww = clamp(mix[m], 0); if (ww <= 0) continue;
|
|
561
|
+
var p = prices[m]; if (!p) continue;
|
|
562
|
+
i += clamp(p.input, 0) * ww; o += clamp(p.output, 0) * ww; w += ww;
|
|
563
|
+
}
|
|
564
|
+
return w > 0 ? { input: i / w, output: o / w } : { input: 0, output: 0 };
|
|
565
|
+
}
|
|
566
|
+
function fmtTokens(n) {
|
|
567
|
+
if (!isFinite(n) || n <= 0) return '0';
|
|
568
|
+
if (n >= 1e9) return (n / 1e9).toFixed(1) + 'B';
|
|
569
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
|
|
570
|
+
if (n >= 1e3) return (n / 1e3).toFixed(0) + 'K';
|
|
571
|
+
return String(Math.round(n));
|
|
572
|
+
}
|
|
573
|
+
function fmtDollars(n) {
|
|
574
|
+
if (!isFinite(n)) return '$0.00';
|
|
575
|
+
if (n >= 1000) return '$' + Math.round(n).toLocaleString('en-US');
|
|
576
|
+
if (n >= 100) return '$' + n.toFixed(0);
|
|
577
|
+
if (n >= 10) return '$' + n.toFixed(1);
|
|
578
|
+
if (n >= 0.01) return '$' + n.toFixed(2);
|
|
579
|
+
if (n > 0) return '$' + n.toFixed(4);
|
|
580
|
+
return '$0.00';
|
|
581
|
+
}
|
|
582
|
+
function compute(blockedCalls, deflectedBots, opts) {
|
|
583
|
+
opts = opts || {};
|
|
584
|
+
var total = clamp(blockedCalls, 0) + clamp(deflectedBots, 0);
|
|
585
|
+
var avgIn = clamp(opts.avgInputTokensPerBlock, DEFAULT_AVG_INPUT_TOKENS);
|
|
586
|
+
var avgOut = clamp(opts.avgOutputTokensPerBlock, DEFAULT_AVG_OUTPUT_TOKENS);
|
|
587
|
+
var mix = (opts.modelMix && Object.keys(opts.modelMix).length) ? opts.modelMix : DEFAULT_MODEL_MIX;
|
|
588
|
+
var prices = opts.modelPrices || DEFAULT_MODEL_PRICES;
|
|
589
|
+
var b = blended(mix, prices);
|
|
590
|
+
var ti = total * avgIn, to = total * avgOut;
|
|
591
|
+
var dollars = (ti * b.input + to * b.output) / 1e6;
|
|
592
|
+
return {
|
|
593
|
+
blockedCalls: clamp(blockedCalls, 0),
|
|
594
|
+
deflectedBots: clamp(deflectedBots, 0),
|
|
595
|
+
tokensSavedInput: ti,
|
|
596
|
+
tokensSavedOutput: to,
|
|
597
|
+
tokensSavedTotal: ti + to,
|
|
598
|
+
dollarsSaved: dollars,
|
|
599
|
+
dollarsSavedDisplay: fmtDollars(dollars),
|
|
600
|
+
tokensSavedDisplay: fmtTokens(ti + to),
|
|
601
|
+
blendedPricePer1M: b,
|
|
602
|
+
modelMix: mix
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
return { compute: compute, formatDollars: fmtDollars, formatTokens: fmtTokens };
|
|
606
|
+
})();
|
|
607
|
+
|
|
608
|
+
// renderTopBlockedGates — defensive stub for the loadDemo() legacy direct
|
|
609
|
+
// call. The current dashboard layout surfaces top-blocked gates via the
|
|
610
|
+
// team panel (renderTeam → team.topBlockedGates) and has no dedicated
|
|
611
|
+
// #topBlockedGates element. Stub keeps loadDemo() from throwing while
|
|
612
|
+
// also rendering into a target element if one is ever added.
|
|
613
|
+
function renderTopBlockedGates(gates) {
|
|
614
|
+
if (!Array.isArray(gates)) return;
|
|
615
|
+
var target = document.getElementById('topBlockedGates')
|
|
616
|
+
|| document.getElementById('topBlockedList');
|
|
617
|
+
if (!target || typeof escHtml !== 'function') return;
|
|
618
|
+
target.innerHTML = gates.map(function (g) {
|
|
619
|
+
return '<div class="blocked-row"><div><div class="blocked-name">'
|
|
620
|
+
+ escHtml(g.name || '')
|
|
621
|
+
+ '</div></div><div class="blocked-meta">'
|
|
622
|
+
+ (g.blocked || 0)
|
|
623
|
+
+ '</div></div>';
|
|
624
|
+
}).join('');
|
|
625
|
+
}
|
|
626
|
+
|
|
542
627
|
let API_KEY = '';
|
|
543
628
|
let currentSource = 'all';
|
|
544
629
|
let isDemo = false;
|
|
@@ -1441,71 +1526,65 @@ function loadDemo() {
|
|
|
1441
1526
|
// Demo badge in nav
|
|
1442
1527
|
var logo = document.querySelector('.nav-logo');
|
|
1443
1528
|
if (!document.getElementById('demoBadge')) {
|
|
1444
|
-
logo.insertAdjacentHTML('afterend', '<span class="demo-badge" id="demoBadge">Demo Mode</span>');
|
|
1529
|
+
logo.insertAdjacentHTML('afterend', '<span class="demo-badge" id="demoBadge">Demo Mode — Jamie M., Partner · Litigation Intake</span>');
|
|
1445
1530
|
}
|
|
1446
1531
|
// Stats
|
|
1447
|
-
document.getElementById('statTotal').textContent = '
|
|
1448
|
-
document.getElementById('statPositive').textContent = '
|
|
1449
|
-
document.getElementById('statNegative').textContent = '
|
|
1450
|
-
document.getElementById('statGates').textContent = '
|
|
1532
|
+
document.getElementById('statTotal').textContent = '184';
|
|
1533
|
+
document.getElementById('statPositive').textContent = '91';
|
|
1534
|
+
document.getElementById('statNegative').textContent = '93';
|
|
1535
|
+
document.getElementById('statGates').textContent = '14';
|
|
1451
1536
|
setSelectedCard('all');
|
|
1452
|
-
|
|
1453
|
-
// and ~85 bot deflections on /checkout/pro. Kept as illustrative defaults
|
|
1454
|
-
// so the savings card never reads "0" in demo mode.
|
|
1455
|
-
var demoSavings = TG_TOKEN_SAVINGS.compute(180, 85);
|
|
1537
|
+
var demoSavings = TG_TOKEN_SAVINGS.compute(93, 42);
|
|
1456
1538
|
var tEl = document.getElementById('statTokensSaved');
|
|
1457
1539
|
var dEl = document.getElementById('statDollarsSaved');
|
|
1458
1540
|
if (tEl) tEl.textContent = demoSavings.tokensSavedDisplay;
|
|
1459
1541
|
if (dEl) dEl.textContent = '≈ ' + demoSavings.dollarsSavedDisplay + ' saved';
|
|
1460
1542
|
renderTopBlockedGates([
|
|
1461
|
-
{ name: 'gate:
|
|
1462
|
-
{ name: 'gate:
|
|
1463
|
-
{ name: 'gate:
|
|
1464
|
-
{ name: 'gate:
|
|
1465
|
-
{ name: 'gate:
|
|
1466
|
-
{ name: 'gate:
|
|
1543
|
+
{ name: 'gate:block-unauthorized-practice', blocked: 34 },
|
|
1544
|
+
{ name: 'gate:require-conflict-clearance', blocked: 22 },
|
|
1545
|
+
{ name: 'gate:block-privileged-egress', blocked: 17 },
|
|
1546
|
+
{ name: 'gate:require-approved-disclaimer', blocked: 11 },
|
|
1547
|
+
{ name: 'gate:restrict-model-endpoint', blocked: 6 },
|
|
1548
|
+
{ name: 'gate:require-attorney-review', blocked: 3 },
|
|
1467
1549
|
]);
|
|
1468
1550
|
renderReviewDelta({
|
|
1469
1551
|
hasBaseline: true,
|
|
1470
|
-
reviewedAt: '
|
|
1471
|
-
previousHead: '
|
|
1472
|
-
currentHead: '
|
|
1473
|
-
feedbackAdded:
|
|
1474
|
-
negativeAdded:
|
|
1475
|
-
lessonsAdded:
|
|
1476
|
-
blocksAdded:
|
|
1477
|
-
warnsAdded:
|
|
1478
|
-
headline: 'Since your last review:
|
|
1552
|
+
reviewedAt: '2026-05-22T09:00:00Z',
|
|
1553
|
+
previousHead: 'a4c1e7f2',
|
|
1554
|
+
currentHead: 'b8d3f9a1',
|
|
1555
|
+
feedbackAdded: 8,
|
|
1556
|
+
negativeAdded: 5,
|
|
1557
|
+
lessonsAdded: 3,
|
|
1558
|
+
blocksAdded: 7,
|
|
1559
|
+
warnsAdded: 2,
|
|
1560
|
+
headline: 'Since your last review: 8 intake events · 5 blocked · 3 lessons · 7 gate enforcements.',
|
|
1479
1561
|
latestFeedback: {
|
|
1480
|
-
title: '
|
|
1481
|
-
timestamp: '
|
|
1562
|
+
title: 'Intake agent provided jurisdictional recommendation without attorney review',
|
|
1563
|
+
timestamp: '2026-05-25T14:20:00Z'
|
|
1482
1564
|
},
|
|
1483
1565
|
latestLesson: {
|
|
1484
|
-
title: '
|
|
1485
|
-
timestamp: '
|
|
1566
|
+
title: 'BLOCKED: Advice-shaped response detected — routed to attorney review queue',
|
|
1567
|
+
timestamp: '2026-05-24T11:10:00Z'
|
|
1486
1568
|
}
|
|
1487
1569
|
});
|
|
1488
1570
|
document.getElementById('reviewCheckpointBtn').disabled = true;
|
|
1489
1571
|
document.getElementById('reviewCheckpointBtn').textContent = 'Connect to save your own review checkpoint';
|
|
1490
|
-
// Sample memories —
|
|
1572
|
+
// Sample memories — legal AI intake scenarios for law firm pilot demo
|
|
1491
1573
|
var demoResults = [
|
|
1492
|
-
{ signal: 'down', title: '
|
|
1493
|
-
{ signal: '
|
|
1494
|
-
{ signal: '
|
|
1495
|
-
{ signal: '
|
|
1496
|
-
{ signal: '
|
|
1497
|
-
{ signal: '
|
|
1498
|
-
{ signal: 'down', title: '
|
|
1499
|
-
{ signal: 'up', title: '
|
|
1500
|
-
{ signal: 'down', title: '
|
|
1501
|
-
{ signal: 'up', title: '
|
|
1502
|
-
{ signal: 'down', title: '
|
|
1503
|
-
{ signal: 'up', title: '
|
|
1504
|
-
{ signal: '
|
|
1505
|
-
{ signal: 'down', title: '
|
|
1506
|
-
{ signal: 'up', title: 'Prevention rule stopped repeat mistake', context: 'Gate auto-generated from 3 prior "forgot pod install" failures now blocks any npm install that touches native modules without a follow-up pod install step.', tags: ['prevention', 'ios-build', 'auto-generated', 'metric:ROI'], timestamp: '2025-06-07T11:00:00Z' },
|
|
1507
|
-
{ signal: 'down', title: 'Claimed performance fix without benchmarking', context: 'Said the optimization reduced render time by 40% but never ran a before/after benchmark. Actual measurement showed no improvement. Gate now requires metrics evidence.', tags: ['anti-lying', 'performance', 'verification-gap', 'measure-first'], timestamp: '2025-06-06T14:20:00Z' },
|
|
1508
|
-
{ signal: 'up', title: 'DPO export caught systematic failure pattern', context: 'Exported 47 preference pairs revealing that 80% of negative feedback involved claiming work was done without verification. Led to a new mandatory evidence gate.', tags: ['dpo-export', 'evidence-based', 'prevention', 'metric:ROI'], timestamp: '2025-06-05T10:15:00Z' }
|
|
1574
|
+
{ signal: 'down', title: 'Intake agent provided outcome prediction to prospect', context: 'Prospect asked about wrongful termination in Florida. Agent said "you likely have a strong case." ThumbGate blocked the advice-shaped response before delivery under ABA Rule 5.5.', tags: ['upl-risk', 'aba-rule-5.5', 'auto-blocked', 'critical'], timestamp: '2026-05-25T14:20:00Z' },
|
|
1575
|
+
{ signal: 'up', title: 'Conflict pre-check stopped intake before facts collected', context: 'Adverse party matched the synthetic conflict list. ThumbGate halted intake before privileged facts were disclosed, preventing a Rule 1.7/1.9 conflict issue.', tags: ['conflict-check', 'aba-rule-1.7', 'aba-rule-1.9', 'prevention'], timestamp: '2026-05-25T11:45:00Z' },
|
|
1576
|
+
{ signal: 'down', title: 'Agent attempted to send case summary to external CRM', context: 'Agent tried to push matter IDs and consultation notes to an external CRM. ThumbGate blocked the outbound call for privileged-content markers.', tags: ['privilege-egress', 'aba-rule-1.6', 'auto-blocked', 'critical'], timestamp: '2026-05-24T16:30:00Z' },
|
|
1577
|
+
{ signal: 'up', title: 'Safe handoff collected neutral facts only', context: 'After a UPL block, the agent pivoted to neutral routing facts: name, contact, practice area, and callback time. No legal conclusion was delivered.', tags: ['safe-handoff', 'compliant-response', 'aba-rule-5.5', 'metric:ROI'], timestamp: '2026-05-24T11:10:00Z' },
|
|
1578
|
+
{ signal: 'down', title: 'Agent made jurisdictional recommendation without review', context: 'Prospect asked where to file. Agent gave a venue recommendation. ThumbGate blocked it because jurisdiction guidance requires attorney supervision.', tags: ['upl-risk', 'jurisdiction', 'aba-rule-5.5', 'auto-blocked'], timestamp: '2026-05-23T15:20:00Z' },
|
|
1579
|
+
{ signal: 'up', title: 'Audit export preserved full decision chain', context: 'Export showed matched rule, proposed reply, deny decision, attorney-review route, and UTC timestamp. Compliance can replay the decision.', tags: ['audit-trail', 'compliance', 'aba-opinion-512', 'metric:ROI'], timestamp: '2026-05-23T09:00:00Z' },
|
|
1580
|
+
{ signal: 'down', title: 'Response missing firm-approved disclaimer', context: 'Agent drafted a prospect reply without the required non-engagement disclaimer. ThumbGate blocked delivery until approved text was added.', tags: ['disclaimer-missing', 'non-engagement', 'aba-rule-5.5', 'auto-blocked'], timestamp: '2026-05-22T14:45:00Z' },
|
|
1581
|
+
{ signal: 'up', title: 'Model endpoint restricted to approved provider', context: 'Agent tried a consumer model endpoint. ThumbGate enforced the allowlist and rerouted to the firm-approved Azure OpenAI endpoint.', tags: ['model-governance', 'data-handling', 'aba-opinion-512', 'prevention'], timestamp: '2026-05-22T10:30:00Z' },
|
|
1582
|
+
{ signal: 'down', title: 'Agent disclosed opposing counsel information to prospect', context: 'Agent referenced opposing-counsel information from another matter. ThumbGate blocked the cross-matter leak under Rule 1.6.', tags: ['privilege-egress', 'cross-matter', 'aba-rule-1.6', 'critical'], timestamp: '2026-05-21T16:15:00Z' },
|
|
1583
|
+
{ signal: 'up', title: 'Conflict check cleared — intake proceeded safely', context: 'Adverse party cleared the synthetic fixture. ThumbGate allowed intake and logged timestamp plus rule version.', tags: ['conflict-check', 'cleared', 'aba-rule-1.7', 'compliant-response'], timestamp: '2026-05-21T11:00:00Z' },
|
|
1584
|
+
{ signal: 'down', title: 'Intake agent attempted to schedule consultation directly', context: 'Agent tried to book a consultation without attorney review. ThumbGate blocked the routing action pending supervisor approval.', tags: ['routing-governance', 'attorney-review', 'auto-blocked'], timestamp: '2026-05-20T13:30:00Z' },
|
|
1585
|
+
{ signal: 'up', title: 'Egress rerouted to in-tenant system successfully', context: 'Agent needed to store intake notes. ThumbGate rerouted the write to the firm tenant, keeping privileged content inside the boundary.', tags: ['privilege-egress', 'in-tenant-reroute', 'aba-rule-1.6', 'metric:ROI'], timestamp: '2026-05-20T09:45:00Z' },
|
|
1586
|
+
{ signal: 'up', title: 'DPO export identified systematic UPL pattern', context: 'Exported 34 blocked intake pairs. 76% were outcome predictions after "do I have a case?" prompts, driving revised intake scripts.', tags: ['dpo-export', 'upl-pattern', 'prevention', 'metric:ROI'], timestamp: '2026-05-19T14:00:00Z' },
|
|
1587
|
+
{ signal: 'down', title: 'Agent used non-approved model for privilege-sensitive query', context: 'Privileged intake query was routed to an endpoint without enterprise DPA. ThumbGate blocked and logged it for security review.', tags: ['model-governance', 'privilege-risk', 'aba-opinion-512', 'auto-blocked'], timestamp: '2026-05-18T11:20:00Z' }
|
|
1509
1588
|
];
|
|
1510
1589
|
isDemo = true;
|
|
1511
1590
|
demoData = demoResults;
|
|
@@ -1519,14 +1598,14 @@ function loadDemo() {
|
|
|
1519
1598
|
'style="flex:none;background:#b85c2d;color:#fff;padding:8px 18px;border-radius:8px;text-decoration:none;font-weight:700;font-size:13px;white-space:nowrap;">Go Pro — $19/mo</a>' +
|
|
1520
1599
|
'</div>';
|
|
1521
1600
|
document.getElementById('searchResults').innerHTML = upgradeBanner + teaserHtml;
|
|
1522
|
-
// Sample gates
|
|
1601
|
+
// Sample gates — legal AI intake enforcement
|
|
1523
1602
|
var demoGates = [
|
|
1524
|
-
{ name: '
|
|
1525
|
-
{ name: '
|
|
1526
|
-
{ name: '
|
|
1527
|
-
{ name: '
|
|
1528
|
-
{ name: '
|
|
1529
|
-
{ name: '
|
|
1603
|
+
{ name: 'block-unauthorized-practice', pattern: 'Block advice-shaped intake replies from non-attorney agents (ABA Rule 5.5)', action: 'block' },
|
|
1604
|
+
{ name: 'require-conflict-clearance', pattern: 'Require adverse-party clearance before facts or scheduling (Rules 1.7/1.9/1.10)', action: 'block' },
|
|
1605
|
+
{ name: 'block-privileged-egress', pattern: 'Block outbound calls with matter IDs or attorney-client markers (Rule 1.6)', action: 'block' },
|
|
1606
|
+
{ name: 'require-approved-disclaimer', pattern: 'Require firm-approved non-engagement disclaimer before delivery', action: 'block' },
|
|
1607
|
+
{ name: 'restrict-model-endpoint', pattern: 'Allow only enterprise-approved model endpoints (ABA Opinion 512)', action: 'block' },
|
|
1608
|
+
{ name: 'require-attorney-review', pattern: 'Route case assignment and scheduling through attorney review', action: 'warn' }
|
|
1530
1609
|
];
|
|
1531
1610
|
document.getElementById('gatesList').innerHTML = demoGates.map(function(g) {
|
|
1532
1611
|
return '<div class="gate-card"><div><div class="gate-name">' + escHtml(g.name) + '</div>' +
|
|
@@ -1534,141 +1613,144 @@ function loadDemo() {
|
|
|
1534
1613
|
'<span class="gate-action ' + g.action + '">' + g.action.toUpperCase() + '</span></div>';
|
|
1535
1614
|
}).join('');
|
|
1536
1615
|
renderTeam({
|
|
1537
|
-
totalAgents:
|
|
1538
|
-
activeAgents:
|
|
1616
|
+
totalAgents: 4,
|
|
1617
|
+
activeAgents: 3,
|
|
1539
1618
|
windowHours: 24,
|
|
1540
|
-
orgAdherenceRate:
|
|
1619
|
+
orgAdherenceRate: 96.1,
|
|
1541
1620
|
riskAgents: [
|
|
1542
|
-
{ id: '
|
|
1543
|
-
{ id: '
|
|
1621
|
+
{ id: 'intake-chatbot-prod', project: 'client-intake', branch: 'litigation-practice', adherenceRate: 88.2 },
|
|
1622
|
+
{ id: 'intake-chatbot-staging', project: 'client-intake', branch: 'ip-practice-pilot', adherenceRate: 91.7 }
|
|
1544
1623
|
],
|
|
1545
1624
|
topBlockedGates: [
|
|
1546
|
-
{ gateId: '
|
|
1547
|
-
{ gateId: '
|
|
1625
|
+
{ gateId: 'block-unauthorized-practice', blocked: 34, warned: 0 },
|
|
1626
|
+
{ gateId: 'require-conflict-clearance', blocked: 22, warned: 3 }
|
|
1548
1627
|
]
|
|
1549
1628
|
}, {
|
|
1550
1629
|
northStar: {
|
|
1551
|
-
weeklyTeamsRunningProofBackedWorkflows:
|
|
1630
|
+
weeklyTeamsRunningProofBackedWorkflows: 2,
|
|
1552
1631
|
paidTeamRuns: 1
|
|
1553
1632
|
}
|
|
1554
1633
|
});
|
|
1555
1634
|
renderPredictive({
|
|
1556
1635
|
upgradePropensity: {
|
|
1557
|
-
pro: { band: 'high', score: 0.
|
|
1558
|
-
team: { band: '
|
|
1636
|
+
pro: { band: 'high', score: 0.84 },
|
|
1637
|
+
team: { band: 'high', score: 0.72 }
|
|
1559
1638
|
},
|
|
1560
1639
|
revenueForecast: {
|
|
1561
|
-
predictedBookedRevenueCents:
|
|
1562
|
-
incrementalOpportunityCents:
|
|
1640
|
+
predictedBookedRevenueCents: 8400000,
|
|
1641
|
+
incrementalOpportunityCents: 3200000
|
|
1563
1642
|
},
|
|
1564
1643
|
anomalySummary: {
|
|
1565
1644
|
count: 2,
|
|
1566
1645
|
severity: 'warning'
|
|
1567
1646
|
},
|
|
1568
1647
|
topCreators: [
|
|
1569
|
-
{ key: '
|
|
1648
|
+
{ key: 'innovation_team', opportunityRevenueCents: 4800000 }
|
|
1570
1649
|
],
|
|
1571
1650
|
topSources: [
|
|
1572
|
-
{ key: '
|
|
1651
|
+
{ key: 'direct_outreach', opportunityRevenueCents: 2400000 }
|
|
1573
1652
|
],
|
|
1574
1653
|
anomalies: [
|
|
1575
|
-
{ type: '
|
|
1576
|
-
{ type: '
|
|
1654
|
+
{ type: 'upl_risk_concentration', message: 'UPL blocks concentrate in litigation intake — add practice-area rules.', severity: 'warning' },
|
|
1655
|
+
{ type: 'egress_pattern', message: 'Privilege egress attempts cluster after hours — add reviewer coverage.', severity: 'warning' }
|
|
1577
1656
|
]
|
|
1578
1657
|
});
|
|
1579
1658
|
renderAgentInventory({
|
|
1580
|
-
profile: '
|
|
1581
|
-
tier: '
|
|
1582
|
-
configuredServerCount:
|
|
1583
|
-
observedToolCount:
|
|
1659
|
+
profile: 'legal-intake',
|
|
1660
|
+
tier: 'enterprise',
|
|
1661
|
+
configuredServerCount: 2,
|
|
1662
|
+
observedToolCount: 5,
|
|
1584
1663
|
observedTools: [
|
|
1585
|
-
{ toolName: '
|
|
1586
|
-
{ toolName: '
|
|
1587
|
-
{ toolName: '
|
|
1664
|
+
{ toolName: 'intake_reply', evaluations: 89, intercepted: 34, allow: 55, deny: 34, warn: 0 },
|
|
1665
|
+
{ toolName: 'conflict_lookup', evaluations: 67, intercepted: 22, allow: 45, deny: 19, warn: 3 },
|
|
1666
|
+
{ toolName: 'send_to_crm', evaluations: 41, intercepted: 17, allow: 24, deny: 14, warn: 3 },
|
|
1667
|
+
{ toolName: 'schedule_consultation', evaluations: 28, intercepted: 3, allow: 25, deny: 0, warn: 3 },
|
|
1668
|
+
{ toolName: 'export_audit', evaluations: 19, intercepted: 0, allow: 19, deny: 0, warn: 0 }
|
|
1588
1669
|
],
|
|
1589
1670
|
policySources: [
|
|
1590
|
-
{ source: '
|
|
1591
|
-
{ source: '
|
|
1592
|
-
{ source: '
|
|
1671
|
+
{ source: 'legal-intake-rules', count: 14 },
|
|
1672
|
+
{ source: 'privilege-guard', count: 6 },
|
|
1673
|
+
{ source: 'conflict-sentinel', count: 4 }
|
|
1593
1674
|
]
|
|
1594
1675
|
});
|
|
1595
1676
|
renderActionableRemediations([
|
|
1596
1677
|
{
|
|
1597
|
-
title: '
|
|
1598
|
-
rationale: '
|
|
1599
|
-
action: '
|
|
1678
|
+
title: 'add practice-area-specific UPL rules · litigation-intake',
|
|
1679
|
+
rationale: 'Litigation intake generates 76% of UPL blocks. Add targeted rules to reduce false positives.',
|
|
1680
|
+
action: 'add-practice-area-rules',
|
|
1600
1681
|
},
|
|
1601
1682
|
{
|
|
1602
|
-
title: '
|
|
1603
|
-
rationale: '
|
|
1604
|
-
action: '
|
|
1683
|
+
title: 'extend conflict check to affiliated entities · conflict-detection',
|
|
1684
|
+
rationale: 'Conflict fixture covers named parties only. Add affiliates and parents.',
|
|
1685
|
+
action: 'extend-conflict-coverage',
|
|
1605
1686
|
},
|
|
1606
1687
|
{
|
|
1607
|
-
title: '
|
|
1608
|
-
rationale: '
|
|
1609
|
-
action: '
|
|
1688
|
+
title: 'schedule attorney reviewer for off-hours intake · privilege-egress',
|
|
1689
|
+
rationale: 'Egress attempts cluster after 6pm. Add a backup reviewer queue.',
|
|
1690
|
+
action: 'schedule-off-hours-reviewer',
|
|
1610
1691
|
}
|
|
1611
1692
|
]);
|
|
1612
1693
|
renderSettingsStatus({
|
|
1613
1694
|
activeLayers: [
|
|
1614
1695
|
{ scope: 'defaults', exists: true, leafCount: 7, sourcePath: null },
|
|
1615
|
-
{ scope: '
|
|
1616
|
-
{ scope: '
|
|
1617
|
-
{ scope: '
|
|
1618
|
-
{ scope: 'managed', exists: true, leafCount:
|
|
1696
|
+
{ scope: 'firm', exists: true, leafCount: 6, sourcePath: 'config/firm-intake-policy.json' },
|
|
1697
|
+
{ scope: 'practice-area', exists: true, leafCount: 4, sourcePath: 'config/litigation-rules.json' },
|
|
1698
|
+
{ scope: 'pilot', exists: true, leafCount: 3, sourcePath: '.thumbgate/pilot-settings.json' },
|
|
1699
|
+
{ scope: 'managed', exists: true, leafCount: 5, sourcePath: 'config/thumbgate-settings.managed.json' }
|
|
1619
1700
|
],
|
|
1620
1701
|
resolvedSettings: {
|
|
1621
|
-
|
|
1702
|
+
intake: { defaultProfile: 'legal-intake', enforcementMode: 'strict' },
|
|
1622
1703
|
harnesses: { enabled: true, allowRuntimeExecution: true }
|
|
1623
1704
|
},
|
|
1624
1705
|
routingPreview: {
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1706
|
+
intakeAgent: { profile: 'legal-intake', reason: 'all intake agents use strict legal gates' },
|
|
1707
|
+
auditExport: { profile: 'readonly', reason: 'audit export has no firm-system write access' },
|
|
1708
|
+
reviewQueue: { profile: 'attorney-review', reason: 'blocked responses route to attorney review' }
|
|
1628
1709
|
},
|
|
1629
1710
|
origins: [
|
|
1630
|
-
{ path: '
|
|
1631
|
-
{ path: '
|
|
1632
|
-
{ path: '
|
|
1633
|
-
{ path: '
|
|
1711
|
+
{ path: 'intake.defaultProfile', value: 'legal-intake', scope: 'firm', sourcePath: 'config/firm-intake-policy.json' },
|
|
1712
|
+
{ path: 'intake.enforcementMode', value: 'strict', scope: 'firm', sourcePath: 'config/firm-intake-policy.json' },
|
|
1713
|
+
{ path: 'gates.conflictFixturePath', value: 'fixtures/adverse-parties.json', scope: 'pilot', sourcePath: '.thumbgate/pilot-settings.json' },
|
|
1714
|
+
{ path: 'gates.privilegeMarkers', value: ['attorney-client', 'work-product', 'matter-id'], scope: 'firm', sourcePath: 'config/firm-intake-policy.json' }
|
|
1634
1715
|
]
|
|
1635
1716
|
});
|
|
1636
1717
|
renderTemplates({
|
|
1637
1718
|
total: 6,
|
|
1638
1719
|
categories: {
|
|
1639
|
-
'
|
|
1640
|
-
'
|
|
1641
|
-
'
|
|
1642
|
-
'
|
|
1643
|
-
'
|
|
1644
|
-
'Positive Reinforcement': 1
|
|
1720
|
+
'UPL Prevention': 2,
|
|
1721
|
+
'Conflict Detection': 1,
|
|
1722
|
+
'Privilege Protection': 1,
|
|
1723
|
+
'Model Governance': 1,
|
|
1724
|
+
'Intake Routing': 1
|
|
1645
1725
|
},
|
|
1646
1726
|
templates: [
|
|
1647
|
-
{ name: '
|
|
1648
|
-
{ name: '
|
|
1649
|
-
{ name: 'Require
|
|
1650
|
-
{ name: '
|
|
1727
|
+
{ name: 'Block unauthorized practice of law', category: 'UPL Prevention', signal: '👎', defaultAction: 'block', severity: 'critical', problem: 'Stops advice-shaped intake replies under ABA Rule 5.5.', roi: 'Prevents malpractice exposure before delivery.', rollout: 'Enable on every AI intake channel.', pattern: '(outcome prediction|jurisdictional recommendation|you (should|could|might) (file|sue|claim))' },
|
|
1728
|
+
{ name: 'Require approved disclaimer', category: 'UPL Prevention', signal: '👎', defaultAction: 'block', severity: 'high', problem: 'Requires firm-approved non-engagement language.', roi: 'Reduces inadvertent client-relationship risk.', rollout: 'Load approved disclaimer during pilot.', pattern: '(intake_response|client_reply).*(missing|no).*(disclaimer)' },
|
|
1729
|
+
{ name: 'Require conflict check before intake', category: 'Conflict Detection', signal: '👎', defaultAction: 'block', severity: 'critical', problem: 'Requires adverse-party clearance before sensitive facts.', roi: 'Catches conflicts before privileged facts cross walls.', rollout: 'Use conflicts API or synthetic pilot fixture.', pattern: '(collect_case_facts|intake_continue).*(missing|no).*(conflict|clearance)' },
|
|
1730
|
+
{ name: 'Block privileged content egress', category: 'Privilege Protection', signal: '👎', defaultAction: 'block', severity: 'critical', problem: 'Blocks outbound actions with privilege markers.', roi: 'Prevents single-action privilege waiver.', rollout: 'Define markers; start with hard block.', pattern: '(send_email|api_call|external_request).*(privileged|attorney[_-]client|matter[_-]id)' },
|
|
1731
|
+
{ name: 'Restrict model endpoint to approved list', category: 'Model Governance', signal: '👎', defaultAction: 'block', severity: 'high', problem: 'Blocks unapproved model endpoints.', roi: 'Keeps data inside approved vendor boundaries.', rollout: 'Allowlist endpoints during pilot.', pattern: '(model_call|llm_request).*(unapproved|unknown|consumer)' },
|
|
1732
|
+
{ name: 'Require attorney review before routing', category: 'Intake Routing', signal: '👎', defaultAction: 'warn', severity: 'high', problem: 'Requires review before routing or scheduling.', roi: 'Prevents bad prospect routing.', rollout: 'Start strict; relax after pilot evidence.', pattern: '(route_to_attorney|schedule_consultation).*(no|missing).*(review|approval)' }
|
|
1651
1733
|
]
|
|
1652
1734
|
});
|
|
1653
1735
|
renderGeneratedView(buildDemoGeneratedViewSpec(currentGeneratedView));
|
|
1654
1736
|
renderInsights({
|
|
1655
|
-
gateStats: { blocked:
|
|
1737
|
+
gateStats: { blocked: 93 },
|
|
1656
1738
|
tokenSavings: {
|
|
1657
1739
|
dollarsSaved: 0,
|
|
1658
1740
|
dollarsSavedDisplay: '$0.00',
|
|
1659
1741
|
tokensSavedDisplay: '0',
|
|
1660
1742
|
blockedCalls: 0,
|
|
1661
|
-
modelMix: { '
|
|
1662
|
-
blendedPricePer1M: { input:
|
|
1743
|
+
modelMix: { 'gpt-4o': 0.5, 'claude-sonnet-4.5': 0.35, 'azure-openai': 0.15 },
|
|
1744
|
+
blendedPricePer1M: { input: 5.0, output: 15.0 }
|
|
1663
1745
|
},
|
|
1664
1746
|
lessonPipeline: {
|
|
1665
1747
|
stages: [
|
|
1666
|
-
{ id: 'feedback', label: '
|
|
1667
|
-
{ id: 'lessons', label: '
|
|
1668
|
-
{ id: 'gates', label: 'Gates
|
|
1669
|
-
{ id: 'blocked', label: '
|
|
1748
|
+
{ id: 'feedback', label: 'Intake Events', count: 184, detail: '91 safe / 93 blocked' },
|
|
1749
|
+
{ id: 'lessons', label: 'Rules Refined', count: 42, detail: '28 UPL patterns / 14 egress patterns' },
|
|
1750
|
+
{ id: 'gates', label: 'Gates Active', count: 14, detail: '33% of rules become enforcement gates' },
|
|
1751
|
+
{ id: 'blocked', label: 'Risks Prevented', count: 93, detail: 'UPL, conflict, and privilege violations stopped' }
|
|
1670
1752
|
],
|
|
1671
|
-
rates: { feedbackToLesson:
|
|
1753
|
+
rates: { feedbackToLesson: 23, lessonToGate: 33 }
|
|
1672
1754
|
},
|
|
1673
1755
|
feedbackTimeSeries: {
|
|
1674
1756
|
days: Array.from({ length: 30 }, function(_, index) {
|
|
@@ -1692,14 +1774,14 @@ function loadDemo() {
|
|
|
1692
1774
|
},
|
|
1693
1775
|
actionableRemediations: [
|
|
1694
1776
|
{
|
|
1695
|
-
title: '
|
|
1696
|
-
rationale: '
|
|
1697
|
-
action: '
|
|
1777
|
+
title: 'add litigation-specific UPL rules',
|
|
1778
|
+
rationale: '76% of UPL blocks come from litigation intake. Add targeted rules.',
|
|
1779
|
+
action: 'add-practice-area-rules'
|
|
1698
1780
|
},
|
|
1699
1781
|
{
|
|
1700
|
-
title: '
|
|
1701
|
-
rationale: '
|
|
1702
|
-
action: '
|
|
1782
|
+
title: 'extend conflict checks to affiliates',
|
|
1783
|
+
rationale: 'Named-party fixture misses affiliates and parents.',
|
|
1784
|
+
action: 'extend-conflict-coverage'
|
|
1703
1785
|
}
|
|
1704
1786
|
]
|
|
1705
1787
|
});
|
|
@@ -1861,6 +1943,7 @@ function chartDefaults() {
|
|
|
1861
1943
|
function renderFeedbackTrendChart(ts) {
|
|
1862
1944
|
var canvas = document.getElementById('feedbackTrendChart');
|
|
1863
1945
|
if (!canvas) return;
|
|
1946
|
+
if (typeof Chart === 'undefined') return;
|
|
1864
1947
|
var days = ts.days || [];
|
|
1865
1948
|
if (feedbackChart) feedbackChart.destroy();
|
|
1866
1949
|
|
|
@@ -1911,6 +1994,7 @@ function renderFeedbackTrendChart(ts) {
|
|
|
1911
1994
|
function renderLessonTrendChart(ts) {
|
|
1912
1995
|
var canvas = document.getElementById('lessonTrendChart');
|
|
1913
1996
|
if (!canvas) return;
|
|
1997
|
+
if (typeof Chart === 'undefined') return;
|
|
1914
1998
|
var days = ts.days || [];
|
|
1915
1999
|
if (lessonChart) lessonChart.destroy();
|
|
1916
2000
|
|
|
@@ -1969,6 +2053,7 @@ function renderLessonTrendChart(ts) {
|
|
|
1969
2053
|
function renderGateAuditChartFromData(gateAudit) {
|
|
1970
2054
|
var canvas = document.getElementById('gateAuditChart');
|
|
1971
2055
|
if (!canvas) return;
|
|
2056
|
+
if (typeof Chart === 'undefined') return;
|
|
1972
2057
|
var days = gateAudit.days || [];
|
|
1973
2058
|
if (gateAuditChart) gateAuditChart.destroy();
|
|
1974
2059
|
|
package/public/guide.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<meta property="og:title" content="How to Stop AI Coding Agents From Repeating Mistakes">
|
|
12
12
|
<meta property="og:description" content="Pre-action checks that physically block AI agents from repeating known mistakes. The complete guide.">
|
|
13
13
|
<meta property="og:type" content="article">
|
|
14
|
-
<meta property="og:url" content="https://thumbgate
|
|
15
|
-
<link rel="canonical" href="https://thumbgate
|
|
14
|
+
<meta property="og:url" content="https://thumbgate.ai/guide">
|
|
15
|
+
<link rel="canonical" href="https://thumbgate.ai/guide">
|
|
16
16
|
|
|
17
17
|
<script type="application/ld+json">
|
|
18
18
|
{
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"publisher": {
|
|
29
29
|
"@type": "Organization",
|
|
30
30
|
"name": "ThumbGate",
|
|
31
|
-
"url": "https://thumbgate
|
|
31
|
+
"url": "https://thumbgate.ai"
|
|
32
32
|
},
|
|
33
33
|
"datePublished": "2026-03-27",
|
|
34
34
|
"dateModified": "2026-04-25",
|
|
35
|
-
"mainEntityOfPage": "https://thumbgate
|
|
35
|
+
"mainEntityOfPage": "https://thumbgate.ai/guide",
|
|
36
36
|
"about": [
|
|
37
37
|
{"@type": "Thing", "name": "AI coding agents"},
|
|
38
38
|
{"@type": "Thing", "name": "Model Context Protocol"},
|
|
@@ -379,7 +379,7 @@ npx thumbgate init --agent gemini</code></pre>
|
|
|
379
379
|
<a rel="nofollow noopener noreferrer" target="_blank" href="https://buy.stripe.com/00w14neyUcXA5pL5e33sI0e" class="cta cta-secondary">Pay $499 diagnostic</a>
|
|
380
380
|
<a rel="nofollow noopener noreferrer" target="_blank" href="https://buy.stripe.com/fZu9AT76saPsg4pbCr3sI0f" class="cta cta-secondary">Pay $1500 sprint</a>
|
|
381
381
|
<a href="https://thumbgate.ai/#workflow-sprint-intake" class="cta cta-secondary">Send workflow first</a>
|
|
382
|
-
<p style="color:var(--muted); font-size:0.85rem;">Free:
|
|
382
|
+
<p style="color:var(--muted); font-size:0.85rem;">Free: 5 captures/day, 25 total captures, 3 active prevention rules, hook blocking. Pro: hosted sync, dashboard, recall, lesson search, unlimited captures/rules, DPO export. Team: intake first, then $49/seat/mo with a 3-seat minimum.</p>
|
|
383
383
|
|
|
384
384
|
</div>
|
|
385
385
|
</body>
|