thumbgate 1.27.4 → 1.27.7
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/commands/dashboard.md +15 -0
- package/.claude/commands/thumbgate-blocked.md +27 -0
- package/.claude/commands/thumbgate-dashboard.md +15 -0
- package/.claude/commands/thumbgate-doctor.md +30 -0
- package/.claude/commands/thumbgate-guard.md +36 -0
- package/.claude/commands/thumbgate-protect.md +30 -0
- package/.claude/commands/thumbgate-rules.md +30 -0
- package/.claude-plugin/plugin.json +2 -1
- package/.well-known/llms.txt +6 -2
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +49 -5
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/letta/README.md +41 -0
- package/adapters/letta/thumbgate-letta-adapter.js +133 -0
- package/adapters/mcp/server-stdio.js +16 -1
- package/adapters/opencode/opencode.json +1 -1
- package/adapters/policy-engine/ethicore-guardian-client.js +68 -0
- package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +260 -0
- package/bench/observability-eval-suite.json +26 -0
- package/bin/cli.js +230 -6
- package/bin/postinstall.js +1 -1
- package/commands/dashboard.md +15 -0
- package/commands/thumbgate-dashboard.md +15 -0
- package/config/gate-templates.json +84 -0
- package/config/gates/claim-verification.json +12 -0
- package/config/gates/default.json +20 -0
- package/config/github-about.json +1 -1
- package/config/model-candidates.json +50 -0
- package/config/post-deploy-marketing-pages.json +5 -0
- package/package.json +67 -25
- package/public/agent-manager.html +41 -1
- package/public/agents-cost-savings.html +1 -1
- package/public/ai-malpractice-prevention.html +2 -1
- package/public/assets/brand/github-social-preview.png +0 -0
- package/public/assets/brand/thumbgate-icon-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
- package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
- package/public/assets/brand/thumbgate-mark-inline.svg +15 -0
- package/public/assets/brand/thumbgate-mark-pro.svg +23 -0
- package/public/assets/brand/thumbgate-mark-team.svg +26 -0
- package/public/assets/brand/thumbgate-mark.svg +15 -0
- package/public/assets/brand/thumbgate-wordmark.svg +20 -0
- package/public/assets/claude-thumbgate-statusbar.svg +8 -0
- package/public/assets/codex-thumbgate-statusbar-test.svg +9 -0
- package/public/assets/legal-intake-control-flow.svg +66 -0
- package/public/blog.html +1 -1
- package/public/brand/thumbgate-mark.svg +15 -0
- package/public/brand/thumbgate-og.svg +16 -0
- package/public/codex-enterprise.html +1 -1
- package/public/codex-plugin.html +1 -1
- package/public/compare.html +23 -3
- package/public/dashboard.html +316 -30
- package/public/federal.html +1 -1
- package/public/guide.html +5 -4
- package/public/index.html +167 -49
- package/public/js/buyer-intent.js +672 -0
- package/public/learn.html +88 -7
- package/public/lessons.html +2 -1
- package/public/numbers.html +3 -3
- package/public/pricing.html +63 -15
- package/public/pro.html +7 -7
- package/scripts/activation-quickstart.js +187 -0
- package/scripts/agent-memory-lifecycle.js +211 -0
- package/scripts/async-eval-observability.js +236 -0
- package/scripts/auto-promote-gates.js +75 -4
- package/scripts/billing.js +12 -1
- package/scripts/build-metadata.js +24 -3
- package/scripts/cli-schema.js +42 -10
- package/scripts/dashboard-chat.js +53 -7
- package/scripts/dashboard.js +12 -17
- package/scripts/export-databricks-bundle.js +5 -1
- package/scripts/export-dpo-pairs.js +7 -2
- package/scripts/feedback-aggregate.js +281 -0
- package/scripts/feedback-loop.js +121 -0
- package/scripts/filesystem-search.js +35 -10
- package/scripts/gates-engine.js +234 -7
- package/scripts/gemini-embedding-policy.js +2 -1
- package/scripts/hook-stop-anti-claim.js +227 -0
- package/scripts/hook-thumbgate-cache-updater.js +18 -2
- package/scripts/hybrid-feedback-context.js +1 -0
- package/scripts/lesson-inference.js +8 -3
- package/scripts/lesson-search.js +17 -1
- package/scripts/operational-integrity.js +39 -5
- package/scripts/plausible-domain-config.js +15 -2
- package/scripts/plausible-server-events.js +4 -4
- package/scripts/rate-limiter.js +12 -6
- package/scripts/secret-redaction.js +166 -0
- package/scripts/security-scanner.js +100 -0
- package/scripts/self-distill-agent.js +3 -1
- package/scripts/self-harness-optimizer.js +141 -0
- package/scripts/seo-gsd.js +635 -0
- package/scripts/statusline-cache-path.js +17 -2
- package/scripts/statusline-cache-read.js +57 -0
- package/scripts/statusline-local-stats.js +9 -1
- package/scripts/statusline-meta.js +5 -2
- package/scripts/statusline.sh +13 -1
- package/scripts/sync-telemetry-from-prod.js +374 -0
- package/scripts/telemetry-analytics.js +9 -0
- package/scripts/thumbgate-search.js +85 -19
- package/scripts/tool-contract-validator.js +76 -0
- package/scripts/vector-store.js +44 -0
- package/scripts/workspace-evolver.js +62 -2
- package/src/api/server.js +862 -146
package/public/dashboard.html
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
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">
|
|
12
|
+
<meta name="referrer" content="same-origin">
|
|
12
13
|
<!-- Privacy-friendly analytics by Plausible -->
|
|
13
|
-
<script defer data-domain="thumbgate
|
|
14
|
+
<script defer data-domain="thumbgate.ai" src="https://plausible.io/js/script.js"></script>
|
|
14
15
|
<script type="application/ld+json">
|
|
15
16
|
{
|
|
16
17
|
"@context": "https://schema.org",
|
|
@@ -102,7 +103,7 @@
|
|
|
102
103
|
.result-tags { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
|
|
103
104
|
.tag { font-size: 11px; background: var(--cyan-dim); color: var(--cyan); padding: 2px 8px; border-radius: 4px; cursor: pointer; transition: background 0.15s, transform 0.1s; border: none; font-family: inherit; }
|
|
104
105
|
.tag:hover { background: rgba(34,211,238,0.25); transform: translateY(-1px); }
|
|
105
|
-
mark { background:
|
|
106
|
+
mark { background: #fbbf24; color: #000000; font-weight: bold; border-radius: 2px; padding: 0 3px; }
|
|
106
107
|
|
|
107
108
|
/* GATES */
|
|
108
109
|
.gates-section { margin-bottom: 32px; }
|
|
@@ -178,6 +179,74 @@
|
|
|
178
179
|
.demo-banner a { color: var(--cyan); text-decoration: none; font-weight: 600; }
|
|
179
180
|
.demo-banner a:hover { text-decoration: underline; }
|
|
180
181
|
|
|
182
|
+
/* Tooltips styling */
|
|
183
|
+
.tooltip-trigger {
|
|
184
|
+
display: inline-flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
justify-content: center;
|
|
187
|
+
width: 14px;
|
|
188
|
+
height: 14px;
|
|
189
|
+
border-radius: 50%;
|
|
190
|
+
background: var(--border);
|
|
191
|
+
color: var(--text-muted);
|
|
192
|
+
font-size: 9px;
|
|
193
|
+
font-weight: bold;
|
|
194
|
+
cursor: help;
|
|
195
|
+
margin-left: 6px;
|
|
196
|
+
position: relative;
|
|
197
|
+
vertical-align: middle;
|
|
198
|
+
user-select: none;
|
|
199
|
+
}
|
|
200
|
+
.tooltip-trigger:hover {
|
|
201
|
+
background: var(--cyan);
|
|
202
|
+
color: var(--bg);
|
|
203
|
+
}
|
|
204
|
+
.tooltip-trigger .tooltip-content {
|
|
205
|
+
visibility: hidden;
|
|
206
|
+
opacity: 0;
|
|
207
|
+
width: 220px;
|
|
208
|
+
background-color: #161618;
|
|
209
|
+
color: #e8e8ec;
|
|
210
|
+
text-align: left;
|
|
211
|
+
border: 1px solid var(--border);
|
|
212
|
+
border-radius: 6px;
|
|
213
|
+
padding: 8px 12px;
|
|
214
|
+
position: absolute;
|
|
215
|
+
z-index: 1000;
|
|
216
|
+
bottom: 130%;
|
|
217
|
+
left: 50%;
|
|
218
|
+
transform: translateX(-50%);
|
|
219
|
+
transition: opacity 0.15s, visibility 0.15s;
|
|
220
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
|
|
221
|
+
font-weight: normal;
|
|
222
|
+
font-size: 11px;
|
|
223
|
+
line-height: 1.4;
|
|
224
|
+
white-space: normal;
|
|
225
|
+
pointer-events: none;
|
|
226
|
+
font-family: var(--font);
|
|
227
|
+
text-transform: none;
|
|
228
|
+
letter-spacing: normal;
|
|
229
|
+
}
|
|
230
|
+
.tooltip-trigger .tooltip-content::after {
|
|
231
|
+
content: "";
|
|
232
|
+
position: absolute;
|
|
233
|
+
top: 100%;
|
|
234
|
+
left: 50%;
|
|
235
|
+
transform: translateX(-50%);
|
|
236
|
+
border-width: 5px;
|
|
237
|
+
border-style: solid;
|
|
238
|
+
border-color: #161618 transparent transparent transparent;
|
|
239
|
+
}
|
|
240
|
+
.tooltip-trigger:hover .tooltip-content {
|
|
241
|
+
visibility: visible;
|
|
242
|
+
opacity: 1;
|
|
243
|
+
}
|
|
244
|
+
#disconnectBtn:hover {
|
|
245
|
+
background: rgba(248,113,113,0.1) !important;
|
|
246
|
+
border-color: #f87171 !important;
|
|
247
|
+
color: #f87171 !important;
|
|
248
|
+
}
|
|
249
|
+
|
|
181
250
|
.empty { text-align: center; padding: 48px; color: var(--text-muted); font-size: 15px; }
|
|
182
251
|
.loading { text-align: center; padding: 24px; color: var(--text-muted); }
|
|
183
252
|
h2 { font-size: 20px; font-weight: 700; margin-bottom: 16px; letter-spacing: -0.02em; }
|
|
@@ -197,6 +266,19 @@
|
|
|
197
266
|
.enterprise-source-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
|
198
267
|
.enterprise-source { font-size: 11px; border: 1px solid var(--border); border-radius: 999px; padding: 4px 9px; color: var(--text-muted); background: var(--bg-card); }
|
|
199
268
|
|
|
269
|
+
.quick-nav-link {
|
|
270
|
+
cursor: pointer;
|
|
271
|
+
transition: opacity 0.15s ease, color 0.15s ease;
|
|
272
|
+
}
|
|
273
|
+
.quick-nav-link:hover {
|
|
274
|
+
color: var(--cyan);
|
|
275
|
+
opacity: 0.95;
|
|
276
|
+
}
|
|
277
|
+
.quick-nav-link:hover strong {
|
|
278
|
+
color: var(--cyan) !important;
|
|
279
|
+
text-decoration: underline;
|
|
280
|
+
}
|
|
281
|
+
|
|
200
282
|
@media (max-width: 700px) {
|
|
201
283
|
.stats-grid { grid-template-columns: repeat(2, 1fr); }
|
|
202
284
|
.search-filters { flex-wrap: wrap; }
|
|
@@ -221,13 +303,14 @@
|
|
|
221
303
|
<div class="auth-bar">
|
|
222
304
|
<div class="container">
|
|
223
305
|
<div style="margin-bottom:10px;">
|
|
224
|
-
<span style="font-size:14px;font-weight:600;">Connect your API key</span>
|
|
306
|
+
<span id="authTitle" style="font-size:14px;font-weight:600;">Connect your API key</span>
|
|
225
307
|
<span id="authHelp" style="font-size:13px;color:var(--text-muted);margin-left:8px;">Pro auto-connects locally when you launch with <code style="font-family:var(--mono);font-size:12px;background:var(--cyan-dim);color:var(--cyan);padding:2px 6px;border-radius:4px;">npx thumbgate pro</code>. Otherwise paste your <code style="font-family:var(--mono);font-size:12px;background:var(--cyan-dim);color:var(--cyan);padding:2px 6px;border-radius:4px;">THUMBGATE_API_KEY</code> manually.</span>
|
|
226
308
|
</div>
|
|
227
309
|
<div style="display:flex;gap:12px;align-items:center;">
|
|
228
310
|
<input type="password" class="auth-input" id="apiKey" placeholder="Paste your API key here (THUMBGATE_API_KEY or THUMBGATE_PRO_KEY)..." />
|
|
229
311
|
<button class="btn" id="connectBtn" onclick="connect()">Connect</button>
|
|
230
312
|
<button class="btn-outline" id="demoBtn" onclick="loadDemo()">Try Demo</button>
|
|
313
|
+
<button class="btn-outline" id="disconnectBtn" onclick="disconnectKey()" style="display:none; border-color:#f87171; color:#f87171;">Disconnect Key</button>
|
|
231
314
|
<span class="auth-status" id="authStatus"></span>
|
|
232
315
|
</div>
|
|
233
316
|
</div>
|
|
@@ -240,10 +323,10 @@
|
|
|
240
323
|
<p style="font-size:12px;color:var(--text-muted);margin-bottom:8px;">Updated: <time datetime="2026-04-20">2026-04-20</time> · by <a href="https://github.com/IgorGanapolsky" style="color:inherit;">Igor Ganapolsky</a></p>
|
|
241
324
|
<p style="font-size:14px;color:var(--text-muted);line-height:1.6;max-width:700px;">What's happening right now? Search memories, inspect active checks, manage your team, and export training data. <span style="color:var(--cyan);font-weight:600;">This is your control plane for AI agent behavior.</span></p>
|
|
242
325
|
<div style="display:flex;gap:16px;margin-top:12px;font-size:12px;color:var(--text-muted);">
|
|
243
|
-
<span>🔍 <strong style="color:var(--text);">Search</strong> — find any memory</span>
|
|
244
|
-
<span>🛡️ <strong style="color:var(--text);">Gates</strong> — what's blocking</span>
|
|
245
|
-
<span>👥 <strong style="color:var(--text);">Team</strong> — org metrics</span>
|
|
246
|
-
<span>📤 <strong style="color:var(--text);">Export</strong> — DPO training data</span>
|
|
326
|
+
<span class="quick-nav-link" onclick="switchTab('search')">🔍 <strong style="color:var(--text);">Search</strong> — find any memory</span>
|
|
327
|
+
<span class="quick-nav-link" onclick="switchTab('gates')">🛡️ <strong style="color:var(--text);">Gates</strong> — what's blocking</span>
|
|
328
|
+
<span class="quick-nav-link" onclick="switchTab('team')">👥 <strong style="color:var(--text);">Team</strong> — org metrics</span>
|
|
329
|
+
<span class="quick-nav-link" onclick="switchTab('export')">📤 <strong style="color:var(--text);">Export</strong> — DPO training data</span>
|
|
247
330
|
</div>
|
|
248
331
|
</div>
|
|
249
332
|
|
|
@@ -254,10 +337,10 @@
|
|
|
254
337
|
|
|
255
338
|
<!-- STATS -->
|
|
256
339
|
<div class="stats-grid" id="statsGrid">
|
|
257
|
-
<a class="stat-card" data-card-action="all" onclick="selectCard(this,'all')" href="/lessons?signal=all" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view all feedback → Lessons page"><div class="stat-label">Total Feedback
|
|
258
|
-
<a class="stat-card" data-card-action="up" onclick="selectCard(this,'up')" href="/lessons?signal=up" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view positive feedback → Lessons page"><div class="stat-label">👍 Positive
|
|
259
|
-
<a class="stat-card" data-card-action="down" onclick="selectCard(this,'down')" href="/lessons?signal=down" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view negative feedback → Lessons page"><div class="stat-label">👎 Negative
|
|
260
|
-
<a class="stat-card" data-card-action="gates" onclick="selectCard(this,'gates');return false;" href="#" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view active checks"><div class="stat-label">Active Gates
|
|
340
|
+
<a class="stat-card" data-card-action="all" onclick="selectCard(this,'all')" href="/lessons?signal=all" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view all feedback → Lessons page"><div class="stat-label">Total Feedback <span class="tooltip-trigger" onclick="event.stopPropagation(); event.preventDefault();">?<span class="tooltip-content">Total thumbs-up and thumbs-down signals captured from your AI agent interactions.</span></span></div><div class="stat-value cyan" id="statTotal">—</div></a>
|
|
341
|
+
<a class="stat-card" data-card-action="up" onclick="selectCard(this,'up')" href="/lessons?signal=up" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view positive feedback → Lessons page"><div class="stat-label">👍 Positive <span class="tooltip-trigger" onclick="event.stopPropagation(); event.preventDefault();">?<span class="tooltip-content">Successful outcomes where the agent's work met expectations (thumbs-up).</span></span></div><div class="stat-value green" id="statPositive">—</div></a>
|
|
342
|
+
<a class="stat-card" data-card-action="down" onclick="selectCard(this,'down')" href="/lessons?signal=down" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view negative feedback → Lessons page"><div class="stat-label">👎 Negative <span class="tooltip-trigger" onclick="event.stopPropagation(); event.preventDefault();">?<span class="tooltip-content">Unsuccessful runs or errors that we need to learn from to prevent repeat failures (thumbs-down).</span></span></div><div class="stat-value red" id="statNegative">—</div></a>
|
|
343
|
+
<a class="stat-card" data-card-action="gates" onclick="selectCard(this,'gates');return false;" href="#" style="cursor:pointer;text-decoration:none;color:inherit;display:block;" title="Click to view active checks"><div class="stat-label">Active Gates <span class="tooltip-trigger" onclick="event.stopPropagation(); event.preventDefault();">?<span class="tooltip-content">Pre-action checks actively monitoring and blocking known-bad tool calls before they run.</span></span></div><div class="stat-value cyan" id="statGates">—</div></a>
|
|
261
344
|
</div>
|
|
262
345
|
|
|
263
346
|
<div class="panel" id="chatPanel" style="margin-bottom:20px;">
|
|
@@ -280,7 +363,7 @@
|
|
|
280
363
|
<div class="panel" id="reviewDeltaPanel" style="margin-bottom:20px;">
|
|
281
364
|
<div style="display:flex;justify-content:space-between;gap:16px;align-items:flex-start;flex-wrap:wrap;">
|
|
282
365
|
<div>
|
|
283
|
-
<h3 style="margin:0 0 8px 0;font-size:14px;letter-spacing:0.04em;text-transform:uppercase;color:var(--text-muted);">🆕 Since Last Review
|
|
366
|
+
<h3 style="margin:0 0 8px 0;font-size:14px;letter-spacing:0.04em;text-transform:uppercase;color:var(--text-muted);display:inline-flex;align-items:center;">🆕 Since Last Review <span class="tooltip-trigger" onclick="event.stopPropagation(); event.preventDefault();">?<span class="tooltip-content">Shows new feedback, negative signals, lessons, and blocks registered since your last baseline checkpoint.</span></span></h3>
|
|
284
367
|
<p class="template-summary" id="reviewDeltaHeadline" style="margin-bottom:0;">No review checkpoint yet. Mark the current dashboard as reviewed to start seeing only new changes.</p>
|
|
285
368
|
</div>
|
|
286
369
|
<button class="btn-outline" id="reviewCheckpointBtn" onclick="markReviewed()">Mark Current Dashboard Reviewed</button>
|
|
@@ -319,6 +402,12 @@
|
|
|
319
402
|
|
|
320
403
|
<!-- SEARCH TAB -->
|
|
321
404
|
<div class="tab-content active" id="tab-search">
|
|
405
|
+
<div class="panel" style="margin-bottom:24px;">
|
|
406
|
+
<h3 style="margin-top:0;">📈 Feedback Trend (Last 30 Days)</h3>
|
|
407
|
+
<div style="position:relative;height:180px;margin-top:12px;">
|
|
408
|
+
<canvas id="feedbackTrendChart"></canvas>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
322
411
|
<div class="search-section">
|
|
323
412
|
<div class="search-bar">
|
|
324
413
|
<input type="text" class="search-input" id="searchQuery" placeholder="Search memories... (try: git, test, deploy, secrets, database)" onkeydown="if(event.key==='Enter')search()" />
|
|
@@ -340,6 +429,74 @@
|
|
|
340
429
|
<div class="tab-content" id="tab-gates">
|
|
341
430
|
<div class="gates-section">
|
|
342
431
|
<h2>Active Pre-Action Checks</h2>
|
|
432
|
+
|
|
433
|
+
<!-- Interactive Gating Workflow Diagram -->
|
|
434
|
+
<details class="flow-details" style="margin-bottom: 24px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-card); padding: 16px;" open>
|
|
435
|
+
<summary style="font-weight: 600; cursor: pointer; color: var(--cyan); display: flex; align-items: center; gap: 8px; user-select: none;">
|
|
436
|
+
<span>🛡️ How Pre-Action Gating Works (Interactive Flow Diagram)</span>
|
|
437
|
+
</summary>
|
|
438
|
+
<div style="margin-top:20px; text-align:center;">
|
|
439
|
+
<svg viewBox="0 0 800 220" width="100%" height="auto" style="max-width: 800px; font-family: var(--font); font-size: 11px;">
|
|
440
|
+
<style>
|
|
441
|
+
.flow-node { fill: #1c1c1f; stroke: var(--border); stroke-width: 1.5; rx: 8px; transition: stroke 0.2s, fill 0.2s; }
|
|
442
|
+
.flow-node:hover { fill: rgba(34,211,238,0.06); stroke: var(--cyan); cursor: help; }
|
|
443
|
+
.flow-text { fill: var(--text); font-weight: 600; pointer-events: none; }
|
|
444
|
+
.flow-subtext { fill: var(--text-muted); font-size: 10px; pointer-events: none; }
|
|
445
|
+
.flow-arrow { fill: none; stroke: var(--border); stroke-width: 1.5; marker-end: url(#arrow-head); }
|
|
446
|
+
</style>
|
|
447
|
+
|
|
448
|
+
<defs>
|
|
449
|
+
<marker id="arrow-head" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
|
450
|
+
<path d="M 0 1 L 10 5 L 0 9 z" fill="#8b8b96" />
|
|
451
|
+
</marker>
|
|
452
|
+
</defs>
|
|
453
|
+
|
|
454
|
+
<!-- Step 1: Tool Call -->
|
|
455
|
+
<g>
|
|
456
|
+
<rect x="10" y="80" width="130" height="60" class="flow-node" />
|
|
457
|
+
<text x="75" y="105" text-anchor="middle" class="flow-text">1. Tool Call</text>
|
|
458
|
+
<text x="75" y="125" text-anchor="middle" class="flow-subtext">Agent requests action</text>
|
|
459
|
+
</g>
|
|
460
|
+
|
|
461
|
+
<path d="M 140 110 L 190 110" class="flow-arrow" />
|
|
462
|
+
|
|
463
|
+
<!-- Step 2: Hook Intercept -->
|
|
464
|
+
<g>
|
|
465
|
+
<rect x="200" y="80" width="140" height="60" class="flow-node" />
|
|
466
|
+
<text x="270" y="105" text-anchor="middle" class="flow-text">2. PreToolUse Hook</text>
|
|
467
|
+
<text x="270" y="125" text-anchor="middle" class="flow-subtext">Intercepts tool execution</text>
|
|
468
|
+
</g>
|
|
469
|
+
|
|
470
|
+
<path d="M 340 110 L 390 110" class="flow-arrow" />
|
|
471
|
+
|
|
472
|
+
<!-- Step 3: Policy / DB Lookup -->
|
|
473
|
+
<g>
|
|
474
|
+
<rect x="400" y="80" width="150" height="60" class="flow-node" />
|
|
475
|
+
<text x="475" y="105" text-anchor="middle" class="flow-text">3. Gate Evaluation</text>
|
|
476
|
+
<text x="475" y="125" text-anchor="middle" class="flow-subtext">Validates matching rules</text>
|
|
477
|
+
</g>
|
|
478
|
+
|
|
479
|
+
<!-- Branch Arrows -->
|
|
480
|
+
<path d="M 550 100 L 640 55" class="flow-arrow" />
|
|
481
|
+
<path d="M 550 120 L 640 165" class="flow-arrow" />
|
|
482
|
+
|
|
483
|
+
<!-- Step 4a: Block -->
|
|
484
|
+
<g>
|
|
485
|
+
<rect x="650" y="20" width="130" height="60" class="flow-node" style="stroke:#ef4444;" />
|
|
486
|
+
<text x="715" y="45" text-anchor="middle" class="flow-text" style="fill:#ef4444;">❌ BLOCK</text>
|
|
487
|
+
<text x="715" y="65" text-anchor="middle" class="flow-subtext">Matches rule -> aborted</text>
|
|
488
|
+
</g>
|
|
489
|
+
|
|
490
|
+
<!-- Step 4b: Allow -->
|
|
491
|
+
<g>
|
|
492
|
+
<rect x="650" y="140" width="130" height="60" class="flow-node" style="stroke:#22c55e;" />
|
|
493
|
+
<text x="715" y="165" text-anchor="middle" class="flow-text" style="fill:#22c55e;">✅ ALLOW</text>
|
|
494
|
+
<text x="715" y="185" text-anchor="middle" class="flow-subtext">Runs safely & logs</text>
|
|
495
|
+
</g>
|
|
496
|
+
</svg>
|
|
497
|
+
</div>
|
|
498
|
+
</details>
|
|
499
|
+
|
|
343
500
|
<div id="gatesList"><div class="loading">Loading gates...</div></div>
|
|
344
501
|
</div>
|
|
345
502
|
</div>
|
|
@@ -536,13 +693,7 @@
|
|
|
536
693
|
</div>
|
|
537
694
|
|
|
538
695
|
<!-- Charts -->
|
|
539
|
-
<div
|
|
540
|
-
<div class="panel">
|
|
541
|
-
<h3>Feedback Trend (30 days)</h3>
|
|
542
|
-
<div style="position:relative;height:260px;margin-top:12px;">
|
|
543
|
-
<canvas id="feedbackTrendChart"></canvas>
|
|
544
|
-
</div>
|
|
545
|
-
</div>
|
|
696
|
+
<div style="margin-bottom:24px;">
|
|
546
697
|
<div class="panel">
|
|
547
698
|
<h3>Lessons Generated (30 days)</h3>
|
|
548
699
|
<div style="position:relative;height:260px;margin-top:12px;">
|
|
@@ -848,6 +999,37 @@ function hasBootstrapKey() {
|
|
|
848
999
|
return LOCAL_PRO_BOOTSTRAP && Boolean(BOOTSTRAP_API_KEY);
|
|
849
1000
|
}
|
|
850
1001
|
|
|
1002
|
+
async function checkLocalLlmReachable(url) {
|
|
1003
|
+
try {
|
|
1004
|
+
var controller = new AbortController();
|
|
1005
|
+
var timeout = setTimeout(function() { controller.abort(); }, 400);
|
|
1006
|
+
// Use no-cors mode to bypass CORS block (only care if network request succeeds/fails)
|
|
1007
|
+
await fetch(url, { method: 'GET', signal: controller.signal, mode: 'no-cors' });
|
|
1008
|
+
clearTimeout(timeout);
|
|
1009
|
+
return true;
|
|
1010
|
+
} catch (e) {
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function disconnectKey() {
|
|
1016
|
+
API_KEY = '';
|
|
1017
|
+
isDemo = false;
|
|
1018
|
+
var input = document.getElementById('apiKey');
|
|
1019
|
+
input.value = '';
|
|
1020
|
+
input.disabled = false;
|
|
1021
|
+
input.placeholder = "Paste your API key here (THUMBGATE_API_KEY or THUMBGATE_PRO_KEY)...";
|
|
1022
|
+
var btn = document.getElementById('connectBtn');
|
|
1023
|
+
btn.disabled = false;
|
|
1024
|
+
document.getElementById('demoBtn').style.display = 'inline-block';
|
|
1025
|
+
document.getElementById('disconnectBtn').style.display = 'none';
|
|
1026
|
+
document.getElementById('authStatus').className = 'auth-status';
|
|
1027
|
+
document.getElementById('authStatus').textContent = 'Disconnected';
|
|
1028
|
+
document.getElementById('authTitle').textContent = 'Connect your API key';
|
|
1029
|
+
document.getElementById('authHelp').innerHTML = 'Pro auto-connects locally when you launch with <code style="font-family:var(--mono);font-size:12px;background:var(--cyan-dim);color:var(--cyan);padding:2px 6px;border-radius:4px;">npx thumbgate pro</code>. Otherwise paste your <code style="font-family:var(--mono);font-size:12px;background:var(--cyan-dim);color:var(--cyan);padding:2px 6px;border-radius:4px;">THUMBGATE_API_KEY</code> manually.';
|
|
1030
|
+
document.getElementById('dashboardContent').style.display = 'none';
|
|
1031
|
+
}
|
|
1032
|
+
|
|
851
1033
|
async function connect(options) {
|
|
852
1034
|
var opts = options || {};
|
|
853
1035
|
var input = document.getElementById('apiKey');
|
|
@@ -872,7 +1054,18 @@ async function connect(options) {
|
|
|
872
1054
|
tierName = data.tier;
|
|
873
1055
|
}
|
|
874
1056
|
|
|
875
|
-
if (data.
|
|
1057
|
+
if (data.localLlmConfigured) {
|
|
1058
|
+
var modelLabel = data.localLlmModel ? ' (' + data.localLlmModel + ')' : '';
|
|
1059
|
+
var llmUrl = data.localLlmEndpoint || 'http://localhost:11434/v1';
|
|
1060
|
+
document.getElementById('chatHint').innerHTML = '<span style="color:var(--text-muted)">Checking local LLM reachability...</span>';
|
|
1061
|
+
checkLocalLlmReachable(llmUrl.replace(/\/v1$/, '')).then(function(reachable) {
|
|
1062
|
+
if (reachable) {
|
|
1063
|
+
document.getElementById('chatHint').innerHTML = '<span style="color:var(--green)">✓ Local LLM' + modelLabel + ' ready. Chat answers are generated locally — no cloud calls.</span>';
|
|
1064
|
+
} else {
|
|
1065
|
+
document.getElementById('chatHint').innerHTML = '<span style="color:#f59e0b">⚠ Ollama is offline. Launch it to chat locally: <code style="font-family:var(--mono);font-size:12px;background:rgba(245,158,11,0.1);color:#f59e0b;padding:2px 6px;border-radius:4px;">ollama run qwen2.5-coder:14b</code></span>';
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
} else if (data.geminiKeyStatus === 'validated' || data.geminiValidatedAt) {
|
|
876
1069
|
var when = data.geminiValidatedAt ? ' (validated ' + new Date(data.geminiValidatedAt).toLocaleTimeString() + ')' : '';
|
|
877
1070
|
document.getElementById('chatHint').innerHTML = '<span style="color:var(--green)">✓ Gemini API key validated' + when + '. You can now chat with your data.</span>';
|
|
878
1071
|
} else if (data.perplexityConfigured) {
|
|
@@ -883,10 +1076,15 @@ async function connect(options) {
|
|
|
883
1076
|
|
|
884
1077
|
status.className = 'auth-status ok';
|
|
885
1078
|
status.textContent = opts.localPro ? `✓ Local ${tierName} connected` : '✓ Connected';
|
|
1079
|
+
document.getElementById('disconnectBtn').style.display = 'inline-block';
|
|
1080
|
+
const titleEl = document.getElementById('authTitle');
|
|
1081
|
+
if (titleEl) {
|
|
1082
|
+
titleEl.textContent = opts.localPro ? `${tierName} License Active` : 'API Key Connected';
|
|
1083
|
+
}
|
|
886
1084
|
document.getElementById('dashboardContent').style.display = 'block';
|
|
887
1085
|
if (opts.localPro) {
|
|
888
1086
|
const localProActiveMessage = 'Local Pro is active on this machine. Your dashboard is using the saved license key automatically.';
|
|
889
|
-
const localEnterpriseActiveMessage = 'Local Enterprise is active on this machine. Your dashboard is using the saved license key automatically.';
|
|
1087
|
+
const localEnterpriseActiveMessage = 'Local Enterprise is active on this machine. Your dashboard is using the saved license key automatically. Click Disconnect to switch keys.';
|
|
890
1088
|
input.value = 'local-license';
|
|
891
1089
|
input.disabled = true;
|
|
892
1090
|
input.placeholder = `Local ${tierName} auto-connected`;
|
|
@@ -904,6 +1102,10 @@ async function connect(options) {
|
|
|
904
1102
|
} catch (e) {
|
|
905
1103
|
status.className = 'auth-status err';
|
|
906
1104
|
status.textContent = '✗ ' + e.message;
|
|
1105
|
+
const titleEl = document.getElementById('authTitle');
|
|
1106
|
+
if (titleEl) {
|
|
1107
|
+
titleEl.textContent = 'Connect your API key';
|
|
1108
|
+
}
|
|
907
1109
|
if (opts.localPro) {
|
|
908
1110
|
document.getElementById('authHelp').textContent = `Local ${tierName} bootstrap failed. Paste your THUMBGATE_API_KEY manually or retry the local launcher.`;
|
|
909
1111
|
}
|
|
@@ -969,24 +1171,88 @@ async function search() {
|
|
|
969
1171
|
}
|
|
970
1172
|
|
|
971
1173
|
function renderResult(r) {
|
|
972
|
-
|
|
1174
|
+
let contextText = r.context || r.text || r.content || '';
|
|
1175
|
+
let signal = r.signal || r.type || '';
|
|
1176
|
+
|
|
1177
|
+
if (contextText && contextText.trim().startsWith('{')) {
|
|
1178
|
+
const originalText = contextText;
|
|
1179
|
+
try {
|
|
1180
|
+
let cleanText = contextText.trim();
|
|
1181
|
+
if (cleanText.endsWith('…')) {
|
|
1182
|
+
cleanText = cleanText.slice(0, -1);
|
|
1183
|
+
}
|
|
1184
|
+
if (cleanText.endsWith(',...')) {
|
|
1185
|
+
cleanText = cleanText.slice(0, -4);
|
|
1186
|
+
}
|
|
1187
|
+
let parsed = null;
|
|
1188
|
+
try {
|
|
1189
|
+
parsed = JSON.parse(cleanText);
|
|
1190
|
+
} catch (_) {
|
|
1191
|
+
try { parsed = JSON.parse(cleanText + '"}'); } catch (_) {}
|
|
1192
|
+
try { parsed = JSON.parse(cleanText + '}'); } catch (_) {}
|
|
1193
|
+
try { parsed = JSON.parse(cleanText + '"} }'); } catch (_) {}
|
|
1194
|
+
}
|
|
1195
|
+
if (parsed && (parsed.context || parsed.text || parsed.content || parsed.message)) {
|
|
1196
|
+
contextText = parsed.context || parsed.text || parsed.content || parsed.message;
|
|
1197
|
+
} else {
|
|
1198
|
+
let contextMatch = originalText.match(/"context"\s*:\s*"((?:[^"\\]|\\.)*?)"/);
|
|
1199
|
+
if (!contextMatch) {
|
|
1200
|
+
contextMatch = originalText.match(/"context"\s*:\s*"((?:[^"\\]|\\.)*)/);
|
|
1201
|
+
}
|
|
1202
|
+
if (!contextMatch || !contextMatch[1]) {
|
|
1203
|
+
let otherMatch = originalText.match(/"(?:text|content|message)"\s*:\s*"((?:[^"\\]|\\.)*?)"/);
|
|
1204
|
+
if (!otherMatch) {
|
|
1205
|
+
otherMatch = originalText.match(/"(?:text|content|message)"\s*:\s*"((?:[^"\\]|\\.)*)/);
|
|
1206
|
+
}
|
|
1207
|
+
if (otherMatch && otherMatch[1]) {
|
|
1208
|
+
contextMatch = otherMatch;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
if (contextMatch && contextMatch[1]) {
|
|
1212
|
+
let val = contextMatch[1];
|
|
1213
|
+
val = val.replace(/(?:"\s*,\s*"[^"]*"\s*:?|"\s*,\s*|"\s*\}?\s*|\s*,\s*|\s*\}?\s*)$/, '');
|
|
1214
|
+
val = val.replace(/(?:\.\.\.|…|",\.\.\.|",…|"\s*,\s*\.\.\.|"\s*,\s*…)$/, '');
|
|
1215
|
+
contextText = val.replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (parsed && parsed.signal) {
|
|
1219
|
+
signal = parsed.signal;
|
|
1220
|
+
} else {
|
|
1221
|
+
const signalMatch = originalText.match(/"signal"\s*:\s*"([^"]+)"/);
|
|
1222
|
+
if (signalMatch && !signal) {
|
|
1223
|
+
signal = signalMatch[1];
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
} catch (_) {}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
973
1229
|
const isUp = signal === 'up' || signal === 'positive' || signal === 'thumbs_up';
|
|
974
1230
|
const isDown = signal === 'down' || signal === 'negative' || signal === 'thumbs_down';
|
|
975
1231
|
const signalClass = isUp ? 'up' : isDown ? 'down' : '';
|
|
976
1232
|
const signalLabel = isUp ? '👍 Positive' : isDown ? '👎 Negative' : signal;
|
|
977
|
-
|
|
978
|
-
|
|
1233
|
+
|
|
1234
|
+
let displayTitle = r.title || contextText || '';
|
|
1235
|
+
if (displayTitle && displayTitle.startsWith('feedback_')) {
|
|
1236
|
+
const parts = displayTitle.split('_');
|
|
1237
|
+
if (parts.length >= 3) {
|
|
1238
|
+
const type = parts[1];
|
|
1239
|
+
const id = parts.slice(2).join('_');
|
|
1240
|
+
displayTitle = (type === 'positive' ? '👍' : '👎') + ' Feedback (' + id + ')';
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
979
1244
|
const tags = r.tags || [];
|
|
980
1245
|
const date = r.timestamp ? new Date(r.timestamp).toLocaleDateString() : '';
|
|
1246
|
+
|
|
981
1247
|
return '<div class="result-card">' +
|
|
982
1248
|
'<div class="result-header">' +
|
|
983
1249
|
(signalLabel ? '<span class="result-signal ' + signalClass + '">' + signalLabel + '</span>' : '<span></span>') +
|
|
984
1250
|
'<span class="result-date">' + date + '</span>' +
|
|
985
1251
|
'</div>' +
|
|
986
|
-
(
|
|
987
|
-
(
|
|
1252
|
+
(displayTitle ? '<div class="result-title">' + highlightText(displayTitle) + '</div>' : '') +
|
|
1253
|
+
(contextText && contextText !== displayTitle ? '<div class="result-context">' + highlightText(contextText.slice(0, 300)) + '</div>' : '') +
|
|
988
1254
|
(tags.length ? '<div class="result-tags">' + tags.map(function(t) {
|
|
989
|
-
return '<button type="button" class="tag" data-tag="' + encodeURIComponent(String(t)) + '" title="' + escAttr('Search for ' + t) + '">' +
|
|
1255
|
+
return '<button type="button" class="tag" data-tag="' + encodeURIComponent(String(t)) + '" title="' + escAttr('Search for ' + t) + '">' + highlightText(t) + '</button>';
|
|
990
1256
|
}).join('') + '</div>' : '') +
|
|
991
1257
|
'</div>';
|
|
992
1258
|
}
|
|
@@ -1824,9 +2090,28 @@ function renderEnterpriseStatus(status) {
|
|
|
1824
2090
|
{ name: 'Dialogflow guard adapter', value: dfcx.liveAgentConfigured ? 'Configured' : 'Optional', note: dfcx.verification || 'Only needed for customer-owned Dialogflow CX agent deployments.' },
|
|
1825
2091
|
{ name: 'Legacy gcloud CX command', value: 'Unsupported', note: 'Do not use the old alpha gcloud CX command group; use REST API or console for DFCX proof.' }
|
|
1826
2092
|
];
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
2093
|
+
|
|
2094
|
+
function draw() {
|
|
2095
|
+
target.innerHTML = rows.map(function(row) {
|
|
2096
|
+
return '<div class="inventory-row"><div><div class="inventory-name">' + escHtml(row.name) + '</div><div class="inventory-subtitle">' + escHtml(row.note) + '</div></div><span class="remediation-action">' + escHtml(row.value) + '</span></div>';
|
|
2097
|
+
}).join('');
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
draw();
|
|
2101
|
+
|
|
2102
|
+
if (localLlm) {
|
|
2103
|
+
var llmUrl = 'http://localhost:11434/v1';
|
|
2104
|
+
checkLocalLlmReachable(llmUrl).then(function(reachable) {
|
|
2105
|
+
if (reachable) {
|
|
2106
|
+
rows[0].value = '✓ Local LLM Online';
|
|
2107
|
+
draw();
|
|
2108
|
+
} else {
|
|
2109
|
+
rows[0].value = '⚠ Local LLM Offline';
|
|
2110
|
+
rows[0].note = 'Ollama is offline. Launch it to chat locally: ollama run qwen2.5-coder:14b';
|
|
2111
|
+
draw();
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
1830
2115
|
}
|
|
1831
2116
|
|
|
1832
2117
|
async function loadEnterpriseDataChatStatus() {
|
|
@@ -2478,5 +2763,6 @@ function renderGateAuditChartFromData(gateAudit) {
|
|
|
2478
2763
|
}
|
|
2479
2764
|
|
|
2480
2765
|
</script>
|
|
2766
|
+
<script src="/js/buyer-intent.js"></script>
|
|
2481
2767
|
</body>
|
|
2482
2768
|
</html>
|
package/public/federal.html
CHANGED
|
@@ -16,7 +16,7 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
16
16
|
<meta property="og:image" content="/og.png">
|
|
17
17
|
<meta name="keywords" content="ThumbGate federal, AI agent governance federal, NIST 800-53 AI agent, OMB M-24-10, EO 14110, FedRAMP AI coding agent, federal AI use case inventory, agent audit log, agency AI policy enforcement, Bedrock GovCloud, Azure Government AI, SBIR AI governance">
|
|
18
18
|
|
|
19
|
-
<script defer data-domain="thumbgate
|
|
19
|
+
<script defer data-domain="thumbgate.ai" src="https://plausible.io/js/script.js"></script>
|
|
20
20
|
__GA_BOOTSTRAP__
|
|
21
21
|
|
|
22
22
|
<script>
|
package/public/guide.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>How to Stop AI Coding Agents From Repeating Mistakes — ThumbGate Guide</title>
|
|
7
7
|
<!-- Privacy-friendly analytics by Plausible -->
|
|
8
|
-
<script defer data-domain="thumbgate
|
|
8
|
+
<script defer data-domain="thumbgate.ai" src="https://plausible.io/js/script.js"></script>
|
|
9
9
|
<meta name="description" content="The complete guide to preventing AI coding agent mistakes with pre-action checks, history-aware lesson distillation, and automatic prevention rules.">
|
|
10
10
|
<meta name="keywords" content="AI agent mistakes, Claude Code force push, AI coding agent memory, MCP server guardrails, pre-action checks, vibe coding safety, PreToolUse hooks, ThumbGate, SpecLock alternative, Mem0 alternative">
|
|
11
11
|
<meta property="og:title" content="How to Stop AI Coding Agents From Repeating Mistakes">
|
|
@@ -376,11 +376,12 @@ npx thumbgate init --agent gemini</code></pre>
|
|
|
376
376
|
<pre><code>npx thumbgate init</code></pre>
|
|
377
377
|
<p>One command. Works with Claude Code, Cursor, Codex, Gemini, Amp, and OpenCode. Claude Code can also call Codex for review, adversarial review, and second-pass handoffs through the repo-local bridge plugin.</p>
|
|
378
378
|
<a href="https://thumbgate.ai/checkout/pro?utm_source=guide&utm_medium=cta_button&utm_campaign=pro_pack" class="cta">Get Pro — $19/mo or $149/yr</a>
|
|
379
|
-
<a
|
|
380
|
-
<a
|
|
379
|
+
<a href="__SPRINT_DIAGNOSTIC_CHECKOUT_URL__" class="cta cta-secondary">Pay $499 diagnostic</a>
|
|
380
|
+
<a href="__WORKFLOW_SPRINT_CHECKOUT_URL__" 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: 2 captures/day, 10 total captures, 3 active prevention rules, hook blocking. Pro: hosted sync, dashboard, recall, lesson search, unlimited captures/rules, DPO export. Enterprise: intake first, then custom pricing scoped to your rollout.</p>
|
|
383
383
|
|
|
384
384
|
</div>
|
|
385
|
+
<script src="/js/buyer-intent.js"></script>
|
|
385
386
|
</body>
|
|
386
387
|
</html>
|