thumbgate 1.27.17 → 1.27.19
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 +85 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +33 -5
- package/adapters/chatgpt/openapi.yaml +1707 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +63 -54
- package/bin/postinstall.js +23 -14
- package/config/post-deploy-marketing-pages.json +19 -0
- package/config/tessl-tiles.json +1 -3
- package/package.json +99 -229
- package/public/guide.html +0 -4
- package/public/index.html +28 -49
- package/public/learn.html +0 -8
- package/public/numbers.html +2 -2
- package/scripts/agent-readiness.js +0 -147
- package/scripts/bot-detection.js +61 -3
- package/scripts/cli-feedback.js +4 -14
- package/scripts/cli-schema.js +7 -0
- package/scripts/durability/step.js +3 -3
- package/scripts/feedback-loop.js +10 -32
- package/scripts/gates-engine.js +72 -1
- package/scripts/hook-runtime.js +2 -2
- package/scripts/license.js +10 -9
- package/scripts/llm-client.js +44 -260
- package/scripts/mcp-config.js +7 -1
- package/scripts/published-cli.js +0 -8
- package/scripts/seo-gsd.js +0 -5
- package/src/api/server.js +111 -35
- package/.claude/commands/dashboard.md +0 -15
- package/.claude/commands/thumbgate-blocked.md +0 -27
- package/.claude/commands/thumbgate-dashboard.md +0 -15
- package/.claude/commands/thumbgate-doctor.md +0 -30
- package/.claude/commands/thumbgate-guard.md +0 -36
- package/.claude/commands/thumbgate-protect.md +0 -30
- package/.claude/commands/thumbgate-rules.md +0 -30
- package/adapters/gcp/dfcx-webhook-gate.js +0 -295
- package/adapters/letta/README.md +0 -41
- package/adapters/letta/thumbgate-letta-adapter.js +0 -133
- package/adapters/policy-engine/ethicore-guardian-client.js +0 -68
- package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +0 -260
- package/bench/observability-eval-suite.json +0 -26
- package/bin/dashboard-cli.js +0 -7
- package/commands/dashboard.md +0 -15
- package/commands/thumbgate-dashboard.md +0 -15
- package/config/builtin-lessons.json +0 -23
- package/public/about.html +0 -162
- package/public/agent-manager.html +0 -179
- package/public/agents-cost-savings.html +0 -153
- package/public/ai-malpractice-prevention.html +0 -818
- 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-logo-transparent.svg +0 -22
- package/public/assets/brand/thumbgate-mark-inline-v3.svg +0 -19
- package/public/assets/brand/thumbgate-mark-pro.svg +0 -23
- package/public/assets/brand/thumbgate-mark-team.svg +0 -26
- package/public/assets/brand/thumbgate-mark.svg +0 -21
- package/public/assets/brand/thumbgate-wordmark.svg +0 -20
- package/public/assets/claude-thumbgate-statusbar.svg +0 -8
- package/public/assets/codex-thumbgate-statusbar-test.svg +0 -9
- package/public/assets/legal-intake-control-flow.svg +0 -66
- package/public/brand/thumbgate-mark.svg +0 -19
- package/public/brand/thumbgate-og.svg +0 -16
- package/public/chatgpt-app.html +0 -330
- package/public/codex-enterprise.html +0 -123
- package/public/js/buyer-intent.js +0 -672
- package/public/pricing.html +0 -399
- package/scripts/action-receipts.js +0 -324
- package/scripts/activation-quickstart.js +0 -187
- package/scripts/agent-operations-planner.js +0 -621
- package/scripts/ai-component-inventory.js +0 -367
- package/scripts/async-eval-observability.js +0 -236
- package/scripts/audit.js +0 -65
- package/scripts/aws-blocks-guardrails.js +0 -228
- package/scripts/bot-detector.js +0 -50
- package/scripts/classifier-routing.js +0 -130
- package/scripts/dashboard-chat.js +0 -332
- package/scripts/feedback-aggregate.js +0 -281
- package/scripts/feedback-sanitizer.js +0 -105
- package/scripts/hob-pack.js +0 -591
- package/scripts/hook-stop-anti-claim.js +0 -301
- package/scripts/install-shim.js +0 -87
- package/scripts/mcp-oauth.js +0 -293
- package/scripts/noop-detect.js +0 -285
- package/scripts/omlx-smoke.js +0 -192
- package/scripts/parallel-workflow-orchestrator.js +0 -293
- package/scripts/plan-gate.js +0 -243
- package/scripts/plausible-domain-config.js +0 -99
- package/scripts/qa-scenario-planner.js +0 -136
- package/scripts/repeat-metric.js +0 -137
- package/scripts/secret-fixture-tokens.js +0 -61
- package/scripts/secret-redaction.js +0 -166
- package/scripts/self-harness-optimizer.js +0 -141
- package/scripts/self-healing-check.js +0 -193
- package/scripts/silent-failure-cluster.js +0 -531
- package/scripts/statusline-cache-read.js +0 -57
- package/scripts/sync-telemetry-from-prod.js +0 -374
- package/scripts/tool-contract-validator.js +0 -76
- package/scripts/trajectory-scorer.js +0 -63
- package/scripts/verify-marketing-pages-deployed.js +0 -195
- package/scripts/visitor-journey.js +0 -172
package/public/guide.html
CHANGED
|
@@ -366,10 +366,6 @@ npx thumbgate init --agent gemini</code></pre>
|
|
|
366
366
|
<strong>Verification Evidence</strong>
|
|
367
367
|
<span>Human-readable proof log for the engineering and workflow claims used across the site.</span>
|
|
368
368
|
</a>
|
|
369
|
-
<a href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/proof/automation/report.json" target="_blank" rel="noopener">
|
|
370
|
-
<strong>Automation Proof</strong>
|
|
371
|
-
<span>Machine-readable report for the feedback, enforcement, and automation surfaces behind ThumbGate.</span>
|
|
372
|
-
</a>
|
|
373
369
|
</div>
|
|
374
370
|
|
|
375
371
|
<h2>Get Started</h2>
|
package/public/index.html
CHANGED
|
@@ -20,7 +20,7 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
20
20
|
<meta property="og:image" content="https://thumbgate.ai/og.png">
|
|
21
21
|
<meta name="twitter:card" content="summary_large_image">
|
|
22
22
|
<meta name="twitter:image" content="https://thumbgate.ai/og.png">
|
|
23
|
-
<meta name="thumbgate-version" content="1.27.
|
|
23
|
+
<meta name="thumbgate-version" content="1.27.15">
|
|
24
24
|
<meta name="keywords" content="ThumbGate, thumbgate, AI agent orchestration, AI experience orchestration, agentic development cycle, AC/DC framework, Guide Generate Verify Solve, 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">
|
|
25
25
|
<link rel="canonical" href="__APP_ORIGIN__/">
|
|
26
26
|
<link rel="alternate" type="text/markdown" title="ThumbGate LLM context" href="__APP_ORIGIN__/llm-context.md">
|
|
@@ -411,9 +411,10 @@ __GA_BOOTSTRAP__
|
|
|
411
411
|
/* NAV */
|
|
412
412
|
nav { position: sticky; top: 0; z-index: 50; background: rgba(10,10,11,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid var(--border); padding: 14px 0; }
|
|
413
413
|
nav .container { display: flex; justify-content: space-between; align-items: center; }
|
|
414
|
-
.nav-logo { color: var(--text); text-decoration: none; display: inline-flex; align-items: center;
|
|
415
|
-
.nav-logo .logo-mark { width:
|
|
416
|
-
.nav-logo .logo-text {
|
|
414
|
+
.nav-logo { font-weight: 700; font-size: 15px; letter-spacing: -0.02em; color: var(--text); text-decoration: none; display: inline-flex; align-items: center; gap: 8px; }
|
|
415
|
+
.nav-logo .logo-mark { width: 28px; height: 28px; display: block; }
|
|
416
|
+
.nav-logo .logo-text { color: var(--text); }
|
|
417
|
+
.nav-logo span { color: var(--cyan); }
|
|
417
418
|
.nav-links { display: flex; gap: 24px; align-items: center; }
|
|
418
419
|
.nav-links a { color: var(--text-muted); text-decoration: none; font-size: 13px; transition: color 0.15s; }
|
|
419
420
|
.nav-links a:hover { color: var(--text); }
|
|
@@ -446,20 +447,13 @@ __GA_BOOTSTRAP__
|
|
|
446
447
|
.hero-actions { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 12px; margin: 0 auto 16px; max-width: 900px; }
|
|
447
448
|
.hero-actions a, .hero-actions .hero-install { min-height: 48px; border-radius: 8px; }
|
|
448
449
|
.hero-actions .hero-install { margin-bottom: 0; font-size: 18px; padding: 14px 22px; border: 1px solid rgba(34,211,238,0.55); background: rgba(34,211,238,0.07); box-shadow: none; }
|
|
449
|
-
.hero-actions .btn-install-hero { font-size:
|
|
450
|
+
.hero-actions .btn-install-hero { font-size: 16px; padding: 14px 22px; }
|
|
450
451
|
.hero-actions .hero-diagnostic { font-size: 16px; padding: 14px 22px; background: var(--green); color: #06120a; font-weight: 800; box-shadow: none; }
|
|
451
452
|
.offer-router{max-width:940px;margin:18px auto 22px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;text-align:left}
|
|
452
453
|
.offer-route{border:1px solid rgba(34,211,238,.18);background:rgba(17,17,19,.72);border-radius:8px;padding:14px}
|
|
453
454
|
.offer-route strong{display:block;color:var(--text);font-size:14px;margin-bottom:6px}.offer-route p{margin:0 0 10px;color:var(--text-muted);font-size:13px}
|
|
454
455
|
.offer-route a,.offer-route button{color:var(--cyan);font-weight:800;font-size:13px;text-decoration:none;background:transparent;border:0;padding:0;cursor:pointer}.offer-route.primary{background:rgba(34,211,238,.07)}
|
|
455
|
-
.repeat-proof-strip { max-width: 760px; margin: 0 auto 24px; display: grid; grid-template-columns: 1fr auto 1fr; gap: 10px; align-items: stretch; text-align: left; }
|
|
456
|
-
.repeat-proof-step { border: 1px solid rgba(34,211,238,0.18); background: rgba(17,17,19,0.76); border-radius: 8px; padding: 12px 14px; min-height: 74px; }
|
|
457
|
-
.repeat-proof-step strong { display: block; color: var(--text); font-size: 13px; margin-bottom: 4px; }
|
|
458
|
-
.repeat-proof-step code { display: block; color: var(--text-muted); font-family: var(--mono); font-size: 12px; line-height: 1.45; white-space: normal; }
|
|
459
|
-
.repeat-proof-step.blocked { border-color: rgba(248,113,113,0.34); background: rgba(248,113,113,0.08); }
|
|
460
|
-
.repeat-proof-arrow { display: flex; align-items: center; justify-content: center; color: var(--cyan); font-weight: 800; }
|
|
461
456
|
.btn-pro-page { display: inline-flex; align-items: center; justify-content: center; padding: 11px 18px; background: var(--cyan); color: var(--bg); border-radius: 999px; text-decoration: none; font-size: 14px; font-weight: 700; box-shadow: 0 0 0 1px rgba(34,211,238,0.28), 0 12px 32px rgba(34,211,238,0.18); transition: opacity 0.15s, transform 0.15s; }
|
|
462
|
-
.btn-pro-page.hero-secondary { background: rgba(17,17,19,0.72); color: var(--cyan); border: 1px solid rgba(34,211,238,0.32); box-shadow: none; }
|
|
463
457
|
.btn-pro-page:hover { opacity: 0.9; transform: translateY(-1px); }
|
|
464
458
|
.btn-gpt-page { display: inline-flex; align-items: center; justify-content: center; padding: 14px 32px; background: var(--green); color: #06120a; border-radius: 8px; text-decoration: none; font-size: 16px; font-weight: 800; box-shadow: 0 0 0 1px rgba(74,222,128,0.32), 0 12px 32px rgba(74,222,128,0.18); transition: opacity 0.15s, transform 0.15s; }
|
|
465
459
|
.btn-gpt-page:hover { opacity: 0.92; transform: translateY(-1px); }
|
|
@@ -712,8 +706,6 @@ __GA_BOOTSTRAP__
|
|
|
712
706
|
.hero-actions { flex-direction: column; }
|
|
713
707
|
.hero-actions a { width: 100%; }
|
|
714
708
|
.offer-router { grid-template-columns: 1fr; }
|
|
715
|
-
.repeat-proof-strip { grid-template-columns: 1fr; }
|
|
716
|
-
.repeat-proof-arrow { min-height: 20px; }
|
|
717
709
|
.hero-paid-actions { justify-content: center; }
|
|
718
710
|
.hero-paid-actions a { width: 100%; }
|
|
719
711
|
.nav-links a:not(.nav-cta) { display: none; }
|
|
@@ -738,7 +730,7 @@ __GA_BOOTSTRAP__
|
|
|
738
730
|
<!-- NAV -->
|
|
739
731
|
<nav>
|
|
740
732
|
<div class="container">
|
|
741
|
-
<a href="/" class="nav-logo"
|
|
733
|
+
<a href="/" class="nav-logo"><img src="/assets/brand/thumbgate-mark-inline-v3.svg" alt="ThumbGate" class="logo-mark" width="28" height="28"><span class="logo-text">ThumbGate</span></a>
|
|
742
734
|
<div class="nav-links">
|
|
743
735
|
<a href="#how-it-works">How It Works</a>
|
|
744
736
|
<a href="/pricing">Pricing</a>
|
|
@@ -763,27 +755,15 @@ __GA_BOOTSTRAP__
|
|
|
763
755
|
<!-- HERO -->
|
|
764
756
|
<section class="hero">
|
|
765
757
|
<div class="container">
|
|
766
|
-
<div class="hero-badge"
|
|
767
|
-
<h1>Stop
|
|
768
|
-
<p class="hero-lede">
|
|
769
|
-
|
|
770
|
-
<div class="hero-signals" aria-label="
|
|
771
|
-
<a class="signal-pill signal-down" href="#
|
|
772
|
-
<a class="signal-pill" href="#
|
|
773
|
-
<a class="signal-pill" href="#
|
|
774
|
-
<a class="signal-pill signal-up" href="#pricing">
|
|
775
|
-
</div>
|
|
776
|
-
|
|
777
|
-
<div class="repeat-proof-strip" aria-label="ThumbGate thumbs-down to blocked repeat example">
|
|
778
|
-
<div class="repeat-proof-step">
|
|
779
|
-
<strong>1. You reject it once</strong>
|
|
780
|
-
<code>thumbs down: never force-push main without explicit approval</code>
|
|
781
|
-
</div>
|
|
782
|
-
<div class="repeat-proof-arrow">→</div>
|
|
783
|
-
<div class="repeat-proof-step blocked">
|
|
784
|
-
<strong>2. The repeat is stopped</strong>
|
|
785
|
-
<code>agent: git push --force origin main<br>BLOCKED before execution</code>
|
|
786
|
-
</div>
|
|
758
|
+
<div class="hero-badge">👍 👎 Local-first · open-source · Pro proof when it matters</div>
|
|
759
|
+
<h1>Stop AI agents before they spend money, leak secrets, or break production.</h1>
|
|
760
|
+
<p class="hero-lede">Claude Code, Cursor, Codex, Gemini CLI, and MCP tools now touch terminals, repos, databases, SaaS APIs, and cloud systems. ThumbGate checks the next tool call before it runs, blocks repeated failures from your thumbs-down feedback, and gives paid operators proof, sync, recall, and exports when local rules become business-critical.</p>
|
|
761
|
+
|
|
762
|
+
<div class="hero-signals" aria-label="June 2026 buyer signals">
|
|
763
|
+
<a class="signal-pill signal-down" href="#june-2026-proof">MCP tool risk</a>
|
|
764
|
+
<a class="signal-pill" href="#june-2026-proof">Managed-agent rollout</a>
|
|
765
|
+
<a class="signal-pill" href="#june-2026-proof">Token spend pressure</a>
|
|
766
|
+
<a class="signal-pill signal-up" href="#pricing">Free wedge, paid proof</a>
|
|
787
767
|
</div>
|
|
788
768
|
|
|
789
769
|
<div class="hero-proof-card" aria-label="ThumbGate blocking dangerous AI agent commands in real time">
|
|
@@ -791,26 +771,26 @@ __GA_BOOTSTRAP__
|
|
|
791
771
|
</div>
|
|
792
772
|
|
|
793
773
|
<div class="hero-actions">
|
|
774
|
+
<a href="/checkout/pro?utm_source=website&utm_medium=hero_cta&utm_campaign=pro_upgrade&cta_id=hero_start_pro&cta_placement=hero&plan_id=pro&landing_path=%2F" data-revenue-cta data-cta-id="hero_start_pro" data-cta-placement="hero" data-tier="pro" data-plan-id="pro" data-price="19" onclick="trackRevenueCta(this);try{posthog.capture('hero_pro_checkout_click',{cta:'hero_start_pro',tier:'pro',price:19})}catch(_){}" class="btn-pro-page hero-pro hero-pro-primary">Start Pro — $19/mo</a>
|
|
775
|
+
<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">Workflow Hardening Sprint →</a>
|
|
794
776
|
<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-free btn-install-hero" title="Click to copy: npx thumbgate init">Install Free CLI</a>
|
|
795
|
-
<a href="/checkout/pro?utm_source=website&utm_medium=hero_cta&utm_campaign=pro_upgrade&cta_id=hero_start_pro&cta_placement=hero&plan_id=pro&landing_path=%2F" data-revenue-cta data-cta-id="hero_start_pro" data-cta-placement="hero" data-tier="pro" data-plan-id="pro" data-price="19" onclick="trackRevenueCta(this);try{posthog.capture('hero_pro_checkout_click',{cta:'hero_start_pro',tier:'pro',price:19})}catch(_){}" class="btn-pro-page hero-pro hero-pro-primary hero-secondary">Upgrade after one blocked repeat</a>
|
|
796
|
-
<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 hero-secondary">Team workflow intake →</a>
|
|
797
777
|
</div>
|
|
798
778
|
|
|
799
779
|
<div class="offer-router" aria-label="Choose the right ThumbGate path">
|
|
800
780
|
<div class="offer-route primary">
|
|
801
|
-
<strong>
|
|
802
|
-
<p>
|
|
803
|
-
<
|
|
781
|
+
<strong>Solo operator: Start Pro</strong>
|
|
782
|
+
<p>Unlimited rules, recall, sync, proof, exports.</p>
|
|
783
|
+
<a href="/checkout/pro?utm_source=website&utm_medium=offer_router&cta_id=router_start_pro&cta_placement=offer_router&plan_id=pro" data-revenue-cta data-cta-id="router_start_pro" data-cta-placement="offer_router" data-tier="pro" data-plan-id="pro" data-price="19" onclick="trackRevenueCta(this);">Pay $19/mo with Stripe →</a>
|
|
804
784
|
</div>
|
|
805
785
|
<div class="offer-route">
|
|
806
|
-
<strong>
|
|
807
|
-
<p>
|
|
808
|
-
<a href="
|
|
786
|
+
<strong>Enterprise workflow: Start with intake</strong>
|
|
787
|
+
<p>One repeated failure, one owner, one proof plan.</p>
|
|
788
|
+
<a href="#workflow-sprint-intake" onclick="sendFirstPartyTelemetry('workflow_sprint_intake_started',{ctaId:'router_workflow_sprint',ctaPlacement:'offer_router',offer:'workflow_sprint'});">Talk to us →</a>
|
|
809
789
|
</div>
|
|
810
790
|
<div class="offer-route">
|
|
811
|
-
<strong>
|
|
812
|
-
<p>
|
|
813
|
-
<
|
|
791
|
+
<strong>Still evaluating: Free CLI</strong>
|
|
792
|
+
<p>2 captures/day, 3 active rules. Enough to prove one blocked repeat.</p>
|
|
793
|
+
<button data-router-install onclick="copyInstall(this)"><span class="copy-hint">Copy npx thumbgate init</span></button>
|
|
814
794
|
</div>
|
|
815
795
|
</div>
|
|
816
796
|
|
|
@@ -1287,7 +1267,6 @@ __GA_BOOTSTRAP__
|
|
|
1287
1267
|
<a href="/guides/relational-knowledge-ai-recommendations">Relational knowledge</a>
|
|
1288
1268
|
<a href="/guides/claude-code-feedback">Claude Code feedback</a>
|
|
1289
1269
|
<a href="/guides/autoresearch-agent-safety">Autoresearch safety</a>
|
|
1290
|
-
<a href="/guides/aws-blocks-agent-guardrails">AWS Blocks guardrails</a>
|
|
1291
1270
|
</div>
|
|
1292
1271
|
</div>
|
|
1293
1272
|
</div>
|
|
@@ -1815,7 +1794,7 @@ __GA_BOOTSTRAP__
|
|
|
1815
1794
|
<a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
|
|
1816
1795
|
<a href="/blog">Blog</a>
|
|
1817
1796
|
</div>
|
|
1818
|
-
<span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.27.
|
|
1797
|
+
<span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.27.15</span>
|
|
1819
1798
|
</div>
|
|
1820
1799
|
</footer>
|
|
1821
1800
|
|
package/public/learn.html
CHANGED
|
@@ -457,14 +457,6 @@
|
|
|
457
457
|
<span class="article-tag">Sprint</span>
|
|
458
458
|
</a>
|
|
459
459
|
|
|
460
|
-
<a href="/guides/aws-blocks-agent-guardrails" class="article-card">
|
|
461
|
-
<h3>AWS Blocks Agent Guardrails</h3>
|
|
462
|
-
<p>Guard the local-to-cloud jump before AWS Blocks agents deploy, destroy resources, mutate data, expand IAM, or call Bedrock tools.</p>
|
|
463
|
-
<span class="article-tag">AWS Blocks</span>
|
|
464
|
-
<span class="article-tag">Cloud Safety</span>
|
|
465
|
-
<span class="article-tag">Pre-action Gates</span>
|
|
466
|
-
</a>
|
|
467
|
-
|
|
468
460
|
<a href="/guides/ai-search-topical-presence" class="article-card">
|
|
469
461
|
<h3>AI Search Topical Presence</h3>
|
|
470
462
|
<p>Why AI assistants recommend the tools they repeatedly see tied to a buyer problem, and how ThumbGate builds that association with proof-backed pages.</p>
|
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.27.
|
|
28
|
+
"softwareVersion": "1.27.15",
|
|
29
29
|
"url": "https://thumbgate.ai/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.27.
|
|
205
|
+
<div class="freshness">Updated: 2026-05-07 · Version 1.27.15</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>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const fs = require('fs');
|
|
5
|
-
const os = require('os');
|
|
6
5
|
const path = require('path');
|
|
7
6
|
const {
|
|
8
7
|
getActiveMcpProfile,
|
|
@@ -166,141 +165,6 @@ function summarizePermissionTier(profileName = getActiveMcpProfile()) {
|
|
|
166
165
|
};
|
|
167
166
|
}
|
|
168
167
|
|
|
169
|
-
function flattenHookCommands(value, commands = []) {
|
|
170
|
-
if (!value) return commands;
|
|
171
|
-
if (typeof value === 'string') {
|
|
172
|
-
commands.push(value);
|
|
173
|
-
return commands;
|
|
174
|
-
}
|
|
175
|
-
if (Array.isArray(value)) {
|
|
176
|
-
value.forEach((item) => flattenHookCommands(item, commands));
|
|
177
|
-
return commands;
|
|
178
|
-
}
|
|
179
|
-
if (typeof value === 'object') {
|
|
180
|
-
if (typeof value.command === 'string') commands.push(value.command);
|
|
181
|
-
Object.entries(value)
|
|
182
|
-
.filter(([key]) => key !== 'command')
|
|
183
|
-
.forEach(([, item]) => flattenHookCommands(item, commands));
|
|
184
|
-
}
|
|
185
|
-
return commands;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function readJsonIfExists(filePath) {
|
|
189
|
-
try {
|
|
190
|
-
if (!fs.existsSync(filePath)) return null;
|
|
191
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
192
|
-
} catch (_) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function detectHookAutoCapture(projectRoot) {
|
|
198
|
-
const effectiveRoot = projectRoot || findProjectRoot();
|
|
199
|
-
const settingsFiles = ['.claude/settings.json', '.claude/settings.local.json']
|
|
200
|
-
.map((file) => path.join(effectiveRoot, file))
|
|
201
|
-
.filter((file) => fs.existsSync(file));
|
|
202
|
-
const commands = settingsFiles.flatMap((file) => {
|
|
203
|
-
const parsed = readJsonIfExists(file);
|
|
204
|
-
return flattenHookCommands(parsed && parsed.hooks && parsed.hooks.UserPromptSubmit);
|
|
205
|
-
});
|
|
206
|
-
const wiredCommands = commands.filter((command) => /hook-auto-capture(\.sh)?\b/.test(command));
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
ready: wiredCommands.length > 0,
|
|
210
|
-
settingsFiles: settingsFiles.map((file) => path.relative(effectiveRoot, file)),
|
|
211
|
-
wiredCommands,
|
|
212
|
-
recommendation: wiredCommands.length > 0
|
|
213
|
-
? 'UserPromptSubmit auto-capture hook is wired for typed thumbs feedback.'
|
|
214
|
-
: 'Run `npx thumbgate init --wire-hooks` so typed thumbs up/down feedback is captured automatically.',
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function probeFeedbackCapture(projectRoot) {
|
|
219
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'thumbgate-doctor-feedback-'));
|
|
220
|
-
const previousFeedbackDir = process.env.THUMBGATE_FEEDBACK_DIR;
|
|
221
|
-
const previousProjectDir = process.env.THUMBGATE_PROJECT_DIR;
|
|
222
|
-
const previousNoNudge = process.env.THUMBGATE_NO_NUDGE;
|
|
223
|
-
|
|
224
|
-
process.env.THUMBGATE_FEEDBACK_DIR = tempDir;
|
|
225
|
-
// Keep this probe isolated. Direct project scope intentionally overrides
|
|
226
|
-
// THUMBGATE_FEEDBACK_DIR in the runtime resolver, which is correct for real
|
|
227
|
-
// project writes but wrong for a doctor self-test.
|
|
228
|
-
delete process.env.THUMBGATE_PROJECT_DIR;
|
|
229
|
-
process.env.THUMBGATE_NO_NUDGE = '1';
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
const {
|
|
233
|
-
captureFeedback,
|
|
234
|
-
getFeedbackPaths,
|
|
235
|
-
readJSONL,
|
|
236
|
-
} = require('./feedback-loop');
|
|
237
|
-
const result = captureFeedback({
|
|
238
|
-
signal: 'down',
|
|
239
|
-
context: 'doctor feedback loop probe: prove thumbs feedback persists to durable logs',
|
|
240
|
-
whatWentWrong: 'A runtime claimed thumbs feedback worked without verifying durable capture',
|
|
241
|
-
whatToChange: 'Run thumbgate doctor or feedback-self-test before claiming the loop is wired',
|
|
242
|
-
tags: 'doctor,feedback-loop-probe',
|
|
243
|
-
});
|
|
244
|
-
const paths = getFeedbackPaths();
|
|
245
|
-
const feedbackId = result.feedbackEvent && result.feedbackEvent.id;
|
|
246
|
-
const memoryId = result.memoryRecord && result.memoryRecord.id;
|
|
247
|
-
const feedbackStored = Boolean(feedbackId && readJSONL(paths.FEEDBACK_LOG_PATH).some((row) => row.id === feedbackId));
|
|
248
|
-
const memoryStored = Boolean(memoryId && readJSONL(paths.MEMORY_LOG_PATH).some((row) => row.id === memoryId));
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
ready: Boolean(result.accepted && feedbackStored && memoryStored),
|
|
252
|
-
accepted: Boolean(result.accepted),
|
|
253
|
-
feedbackStored,
|
|
254
|
-
memoryStored,
|
|
255
|
-
feedbackId: feedbackId || null,
|
|
256
|
-
memoryId: memoryId || null,
|
|
257
|
-
mode: 'isolated-temp-store',
|
|
258
|
-
feedbackDir: paths.FEEDBACK_DIR,
|
|
259
|
-
recommendation: (result.accepted && feedbackStored && memoryStored)
|
|
260
|
-
? 'Feedback capture writes durable feedback and memory records.'
|
|
261
|
-
: 'Feedback capture did not persist both feedback and memory records; run `npx thumbgate feedback-self-test --json`.',
|
|
262
|
-
};
|
|
263
|
-
} catch (error) {
|
|
264
|
-
return {
|
|
265
|
-
ready: false,
|
|
266
|
-
accepted: false,
|
|
267
|
-
feedbackStored: false,
|
|
268
|
-
memoryStored: false,
|
|
269
|
-
feedbackId: null,
|
|
270
|
-
memoryId: null,
|
|
271
|
-
mode: 'isolated-temp-store',
|
|
272
|
-
feedbackDir: tempDir,
|
|
273
|
-
error: error && error.message ? error.message : String(error),
|
|
274
|
-
recommendation: 'Feedback capture probe failed; run `npx thumbgate feedback-self-test --json` for details.',
|
|
275
|
-
};
|
|
276
|
-
} finally {
|
|
277
|
-
if (previousFeedbackDir === undefined) delete process.env.THUMBGATE_FEEDBACK_DIR;
|
|
278
|
-
else process.env.THUMBGATE_FEEDBACK_DIR = previousFeedbackDir;
|
|
279
|
-
if (previousProjectDir === undefined) delete process.env.THUMBGATE_PROJECT_DIR;
|
|
280
|
-
else process.env.THUMBGATE_PROJECT_DIR = previousProjectDir;
|
|
281
|
-
if (previousNoNudge === undefined) delete process.env.THUMBGATE_NO_NUDGE;
|
|
282
|
-
else process.env.THUMBGATE_NO_NUDGE = previousNoNudge;
|
|
283
|
-
try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch (_) { /* ignore cleanup failure */ }
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function assessFeedbackLoop(projectRoot) {
|
|
288
|
-
const hookAutoCapture = detectHookAutoCapture(projectRoot);
|
|
289
|
-
const captureProbe = probeFeedbackCapture(projectRoot);
|
|
290
|
-
const ready = hookAutoCapture.ready && captureProbe.ready;
|
|
291
|
-
|
|
292
|
-
return {
|
|
293
|
-
ready,
|
|
294
|
-
automatic: hookAutoCapture.ready,
|
|
295
|
-
durableCapture: captureProbe.ready,
|
|
296
|
-
hookAutoCapture,
|
|
297
|
-
captureProbe,
|
|
298
|
-
recommendation: ready
|
|
299
|
-
? 'Typed thumbs feedback is automatically detected and durable capture is verified.'
|
|
300
|
-
: 'Install hooks and verify capture before claiming thumbs feedback improves future runs.',
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
|
|
304
168
|
function generateAgentReadinessReport({
|
|
305
169
|
projectRoot = PROJECT_ROOT,
|
|
306
170
|
mcpProfile = null,
|
|
@@ -308,13 +172,11 @@ function generateAgentReadinessReport({
|
|
|
308
172
|
const runtime = detectRuntimeIsolation();
|
|
309
173
|
const bootstrap = collectBootstrapFiles(projectRoot);
|
|
310
174
|
const permissions = summarizePermissionTier(mcpProfile || getActiveMcpProfile());
|
|
311
|
-
const feedbackLoop = assessFeedbackLoop(projectRoot);
|
|
312
175
|
|
|
313
176
|
const warnings = [];
|
|
314
177
|
if (!runtime.isolated) warnings.push(runtime.recommendation);
|
|
315
178
|
if (!bootstrap.ready) warnings.push(bootstrap.recommendation);
|
|
316
179
|
if (!permissions.ready) warnings.push(permissions.recommendation);
|
|
317
|
-
if (!feedbackLoop.ready) warnings.push(feedbackLoop.recommendation);
|
|
318
180
|
|
|
319
181
|
return {
|
|
320
182
|
generatedAt: new Date().toISOString(),
|
|
@@ -323,12 +185,10 @@ function generateAgentReadinessReport({
|
|
|
323
185
|
runtime,
|
|
324
186
|
bootstrap,
|
|
325
187
|
permissions,
|
|
326
|
-
feedbackLoop,
|
|
327
188
|
articleAlignment: {
|
|
328
189
|
runtimeIsolation: runtime.isolated,
|
|
329
190
|
contextConditioning: bootstrap.ready,
|
|
330
191
|
permissionEnvelope: permissions.ready,
|
|
331
|
-
feedbackLoop: feedbackLoop.ready,
|
|
332
192
|
},
|
|
333
193
|
warnings,
|
|
334
194
|
};
|
|
@@ -348,10 +208,6 @@ function reportToText(report) {
|
|
|
348
208
|
lines.push(`Permissions: ${report.permissions.profile} (${report.permissions.tier})`);
|
|
349
209
|
lines.push(` Write-capable tools: ${report.permissions.writeCapableTools.length}`);
|
|
350
210
|
lines.push(` Recommendation: ${report.permissions.recommendation}`);
|
|
351
|
-
lines.push(`Feedback loop: ${report.feedbackLoop.ready ? 'ready' : 'needs_attention'}`);
|
|
352
|
-
lines.push(` Auto-capture hook: ${report.feedbackLoop.automatic ? 'wired' : 'missing'}`);
|
|
353
|
-
lines.push(` Durable capture: ${report.feedbackLoop.durableCapture ? 'verified' : 'failed'}`);
|
|
354
|
-
lines.push(` Recommendation: ${report.feedbackLoop.recommendation}`);
|
|
355
211
|
|
|
356
212
|
if (report.warnings.length > 0) {
|
|
357
213
|
lines.push('');
|
|
@@ -370,9 +226,6 @@ module.exports = {
|
|
|
370
226
|
detectRuntimeIsolation,
|
|
371
227
|
collectBootstrapFiles,
|
|
372
228
|
summarizePermissionTier,
|
|
373
|
-
detectHookAutoCapture,
|
|
374
|
-
probeFeedbackCapture,
|
|
375
|
-
assessFeedbackLoop,
|
|
376
229
|
generateAgentReadinessReport,
|
|
377
230
|
reportToText,
|
|
378
231
|
};
|
package/scripts/bot-detection.js
CHANGED
|
@@ -36,13 +36,18 @@ const BOT_PATTERNS = [
|
|
|
36
36
|
/\bbaiduspider\b/i,
|
|
37
37
|
/\bduckduckbot\b/i,
|
|
38
38
|
/\bapplebot\b/i,
|
|
39
|
+
/slurp/i,
|
|
40
|
+
/petalbot/i,
|
|
41
|
+
/mediapartners/i,
|
|
39
42
|
// LLM / AI crawlers — these started exploding in 2024+
|
|
40
43
|
/\bgptbot\b/i,
|
|
41
|
-
|
|
44
|
+
/chatgpt/i,
|
|
42
45
|
/\boai-searchbot\b/i,
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
/perplexity/i,
|
|
47
|
+
/anthropic/i,
|
|
45
48
|
/\bclaude(?:bot|-web)\b/i,
|
|
49
|
+
/claude-searchbot/i,
|
|
50
|
+
/google-extended/i,
|
|
46
51
|
/\bccbot\b/i,
|
|
47
52
|
/\bcohere-ai\b/i,
|
|
48
53
|
/\bbytespider\b/i,
|
|
@@ -86,6 +91,8 @@ const BOT_PATTERNS = [
|
|
|
86
91
|
/\blibwww-perl\b/i,
|
|
87
92
|
/\bjava\//i,
|
|
88
93
|
/\bgo-http-client\b/i,
|
|
94
|
+
/scrapy/i,
|
|
95
|
+
/httpclient/i,
|
|
89
96
|
/\bruby\b/i,
|
|
90
97
|
// API/test tools
|
|
91
98
|
/\bpostman(?:runtime)?\b/i,
|
|
@@ -158,8 +165,59 @@ function isProbablyBot(headers) {
|
|
|
158
165
|
return classifyRequester(headers).isBot;
|
|
159
166
|
}
|
|
160
167
|
|
|
168
|
+
// --- Visitor classification (analytics filtering) ---------------------------
|
|
169
|
+
// Classifies telemetry/analytics traffic as bot / owner / real_user.
|
|
170
|
+
// Owner traffic is identified via the THUMBGATE_OWNER_EMAILS env var
|
|
171
|
+
// (comma-separated, case-insensitive). There is no built-in default:
|
|
172
|
+
// operators opt in via config, so the shipped artifact carries no
|
|
173
|
+
// personal data.
|
|
174
|
+
|
|
175
|
+
function getOwnerEmails(env = process.env) {
|
|
176
|
+
const raw = env.THUMBGATE_OWNER_EMAILS;
|
|
177
|
+
if (!raw) return [];
|
|
178
|
+
return raw
|
|
179
|
+
.split(',')
|
|
180
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
181
|
+
.filter(Boolean);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function classifyVisitor(req) {
|
|
185
|
+
const ua = (req.headers && req.headers['user-agent']) || '';
|
|
186
|
+
const email = req.email || (req.query && req.query.email) || '';
|
|
187
|
+
|
|
188
|
+
for (const pattern of BOT_PATTERNS) {
|
|
189
|
+
if (pattern.test(ua)) {
|
|
190
|
+
return { type: 'bot', reason: `UA matches: ${pattern}`, userAgent: ua };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!ua || ua.length < 10) {
|
|
194
|
+
return { type: 'bot', reason: 'Empty or short user-agent', userAgent: ua };
|
|
195
|
+
}
|
|
196
|
+
if (email) {
|
|
197
|
+
const normalized = email.toLowerCase();
|
|
198
|
+
if (getOwnerEmails().some((ownerEmail) => normalized.includes(ownerEmail))) {
|
|
199
|
+
return { type: 'owner', reason: 'Email matches configured owner list', userAgent: ua };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { type: 'real_user', reason: 'No bot pattern matched', userAgent: ua };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function shouldExcludeFromAnalytics(req) {
|
|
206
|
+
const classification = req.visitorClass || classifyVisitor(req);
|
|
207
|
+
return classification.type === 'bot';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function botFilterMiddleware(req, res, next) {
|
|
211
|
+
req.visitorClass = classifyVisitor(req);
|
|
212
|
+
next();
|
|
213
|
+
}
|
|
214
|
+
|
|
161
215
|
module.exports = {
|
|
162
216
|
classifyRequester,
|
|
163
217
|
isProbablyBot,
|
|
218
|
+
classifyVisitor,
|
|
219
|
+
shouldExcludeFromAnalytics,
|
|
220
|
+
botFilterMiddleware,
|
|
221
|
+
getOwnerEmails,
|
|
164
222
|
BOT_PATTERNS,
|
|
165
223
|
};
|
package/scripts/cli-feedback.js
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
const { captureFeedback } = require('./feedback-loop');
|
|
17
|
-
const { isGenericFeedbackText, normalizeFeedbackSignal } = require('./feedback-quality');
|
|
18
17
|
const { loadOptionalModule } = require('./private-core-boundary');
|
|
19
18
|
// `history-distiller` is a PRIVATE_CORE_MODULE — present in this checkout and
|
|
20
19
|
// in ThumbGate-Core, but intentionally excluded from the public npm tarball.
|
|
@@ -48,21 +47,15 @@ const RST = '\x1b[0m';
|
|
|
48
47
|
*/
|
|
49
48
|
function processInlineFeedback({ signal, context, chatHistory, whatWentWrong, whatWorked } = {}) {
|
|
50
49
|
const isDown = signal === 'down' || signal === 'negative';
|
|
51
|
-
const normalizedQualitySignal = normalizeFeedbackSignal(isDown ? 'down' : 'up');
|
|
52
|
-
const providedContext = String(context || '').trim();
|
|
53
|
-
const genericContext = isGenericFeedbackText(providedContext, normalizedQualitySignal);
|
|
54
|
-
const effectiveWhatWentWrong = whatWentWrong || (isDown && providedContext && !genericContext ? providedContext : undefined);
|
|
55
|
-
const effectiveWhatWorked = whatWorked || (!isDown && providedContext && !genericContext ? providedContext : undefined);
|
|
56
50
|
|
|
57
51
|
// 1. Capture the feedback
|
|
58
52
|
let feedbackResult;
|
|
59
53
|
try {
|
|
60
54
|
feedbackResult = captureFeedback({
|
|
61
55
|
signal: isDown ? 'down' : 'up',
|
|
62
|
-
context:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
whatWorked: effectiveWhatWorked,
|
|
56
|
+
context: context || (isDown ? 'Thumbs down from CLI' : 'Thumbs up from CLI'),
|
|
57
|
+
whatWentWrong: whatWentWrong || undefined,
|
|
58
|
+
whatWorked: whatWorked || undefined,
|
|
66
59
|
});
|
|
67
60
|
} catch (err) {
|
|
68
61
|
feedbackResult = { accepted: false, reason: err.message };
|
|
@@ -104,10 +97,7 @@ function formatCliOutput(result) {
|
|
|
104
97
|
process.stderr.write(`✅ Feedback captured (${feedbackId}${memoryId ? `, ${memoryId}` : ''})\n`);
|
|
105
98
|
}
|
|
106
99
|
} else {
|
|
107
|
-
|
|
108
|
-
? ` ${result.feedbackResult.prompt || result.feedbackResult.message || ''}`.trimEnd()
|
|
109
|
-
: '';
|
|
110
|
-
lines.push(`${R}Feedback not accepted: ${(result.feedbackResult && result.feedbackResult.reason) || 'unknown'}${clarification ? ` — ${clarification}` : ''}${RST}`);
|
|
100
|
+
lines.push(`${R}Feedback not accepted: ${(result.feedbackResult && result.feedbackResult.reason) || 'unknown'}${RST}`);
|
|
111
101
|
}
|
|
112
102
|
|
|
113
103
|
// Distilled lesson (if thumbs down)
|
package/scripts/cli-schema.js
CHANGED
|
@@ -642,6 +642,13 @@ const CLI_COMMANDS = [
|
|
|
642
642
|
{ name: 'info', type: 'boolean', description: 'Show Pro feature list' },
|
|
643
643
|
],
|
|
644
644
|
},
|
|
645
|
+
{
|
|
646
|
+
name: 'diagnostic',
|
|
647
|
+
aliases: ['workflow-diagnostic', 'sprint-diagnostic'],
|
|
648
|
+
description: '$499 Workflow Hardening Diagnostic for one repeated AI-agent workflow failure',
|
|
649
|
+
group: 'ops',
|
|
650
|
+
flags: [],
|
|
651
|
+
},
|
|
645
652
|
|
|
646
653
|
{
|
|
647
654
|
name: 'workflow',
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* the full durable-execution runtime. Gives each external call (HTTP,
|
|
8
8
|
* LanceDB, LLM) a uniform retry + idempotency wrapper:
|
|
9
9
|
*
|
|
10
|
-
* const result = await runStep('
|
|
10
|
+
* const result = await runStep('zernio.publishPost', {
|
|
11
11
|
* retries: 3,
|
|
12
12
|
* idempotencyKey: idempotencyKey(content, platforms),
|
|
13
13
|
* }, async ({ attempt }) => {
|
|
14
|
-
* return
|
|
14
|
+
* return zernioFetch('POST', '/posts', body, { idempotencyKey: ... });
|
|
15
15
|
* });
|
|
16
16
|
*
|
|
17
17
|
* Why a custom helper instead of Vercel Workflows / Temporal / Inngest?
|
|
@@ -98,7 +98,7 @@ function idempotencyKey(...parts) {
|
|
|
98
98
|
* `fn` resolves to, or throws the last error after exhausting retries /
|
|
99
99
|
* hitting a non-retryable verdict.
|
|
100
100
|
*
|
|
101
|
-
* @param {string} name Step name, used in logs. e.g. '
|
|
101
|
+
* @param {string} name Step name, used in logs. e.g. 'zernio.publishPost'.
|
|
102
102
|
* @param {object|function} options { retries, backoffMs, classify, onRetry, onFail, logger }
|
|
103
103
|
* (may be passed directly as the callback shorthand)
|
|
104
104
|
* @param {function({attempt:number}):Promise} fn The actual work.
|
package/scripts/feedback-loop.js
CHANGED
|
@@ -315,34 +315,6 @@ function getMemoryFirewallModule() {
|
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
function launchSelfHarnessOptimizer() {
|
|
319
|
-
if (process.env.THUMBGATE_DISABLE_SELF_HARNESS_OPTIMIZER === '1') {
|
|
320
|
-
return { launched: false, reason: 'disabled' };
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const explicitOptimizerPath = process.env.THUMBGATE_SELF_HARNESS_OPTIMIZER_PATH
|
|
324
|
-
? path.resolve(process.env.THUMBGATE_SELF_HARNESS_OPTIMIZER_PATH)
|
|
325
|
-
: null;
|
|
326
|
-
const localOptimizerPath = path.join(process.cwd(), 'scripts', 'self-harness-optimizer.js');
|
|
327
|
-
const packageOptimizerPath = path.join(__dirname, 'self-harness-optimizer.js');
|
|
328
|
-
const optimizerPath = explicitOptimizerPath
|
|
329
|
-
|| (fs.existsSync(localOptimizerPath) ? localOptimizerPath : null)
|
|
330
|
-
|| (fs.existsSync(packageOptimizerPath) ? packageOptimizerPath : null);
|
|
331
|
-
|
|
332
|
-
if (!optimizerPath || !fs.existsSync(optimizerPath)) {
|
|
333
|
-
return { launched: false, reason: 'missing' };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const { spawn } = require('child_process');
|
|
337
|
-
const child = spawn(process.execPath, [optimizerPath], {
|
|
338
|
-
detached: true,
|
|
339
|
-
stdio: 'ignore',
|
|
340
|
-
env: process.env,
|
|
341
|
-
});
|
|
342
|
-
child.unref();
|
|
343
|
-
return { launched: true, path: optimizerPath };
|
|
344
|
-
}
|
|
345
|
-
|
|
346
318
|
|
|
347
319
|
function appendJSONL(filePath, record) {
|
|
348
320
|
ensureDir(path.dirname(filePath));
|
|
@@ -1528,11 +1500,17 @@ function captureFeedback(params) {
|
|
|
1528
1500
|
});
|
|
1529
1501
|
} catch { /* activation telemetry is non-critical */ }
|
|
1530
1502
|
|
|
1531
|
-
// Trigger Self-Harness Optimizer to propagate the new rules to prompt
|
|
1532
|
-
// files & validate. Use spawn instead of fork: fork creates an IPC
|
|
1533
|
-
// pipe that keeps CLI feedback capture alive after the durable write.
|
|
1503
|
+
// Trigger Self-Harness Optimizer to propagate the new rules to prompt files & validate
|
|
1534
1504
|
try {
|
|
1535
|
-
|
|
1505
|
+
const { fork } = require('child_process');
|
|
1506
|
+
const localOptimizerPath = path.join(process.cwd(), 'scripts', 'self-harness-optimizer.js');
|
|
1507
|
+
const packageOptimizerPath = path.join(__dirname, 'self-harness-optimizer.js');
|
|
1508
|
+
|
|
1509
|
+
if (fs.existsSync(localOptimizerPath)) {
|
|
1510
|
+
fork(localOptimizerPath, [], { stdio: 'ignore', detached: true }).unref();
|
|
1511
|
+
} else if (fs.existsSync(packageOptimizerPath)) {
|
|
1512
|
+
fork(packageOptimizerPath, [], { stdio: 'ignore', detached: true }).unref();
|
|
1513
|
+
}
|
|
1536
1514
|
} catch (err) {
|
|
1537
1515
|
console.error('Failed to trigger self-harness optimizer:', err);
|
|
1538
1516
|
}
|