thumbgate 1.27.17 → 1.27.18

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.
Files changed (165) hide show
  1. package/.claude-plugin/marketplace.json +85 -0
  2. package/.claude-plugin/plugin.json +3 -4
  3. package/.well-known/llms.txt +12 -33
  4. package/.well-known/mcp/server-card.json +8 -8
  5. package/README.md +30 -221
  6. package/adapters/chatgpt/openapi.yaml +1695 -0
  7. package/adapters/claude/.mcp.json +2 -2
  8. package/adapters/gemini/function-declarations.json +0 -1
  9. package/adapters/mcp/server-stdio.js +11 -263
  10. package/adapters/opencode/opencode.json +1 -1
  11. package/bench/thumbgate-bench.json +2 -2
  12. package/bin/cli.js +122 -1421
  13. package/bin/postinstall.js +27 -11
  14. package/config/gate-templates.json +0 -216
  15. package/config/gates/claim-verification.json +0 -12
  16. package/config/gates/default.json +2 -31
  17. package/config/github-about.json +2 -2
  18. package/config/mcp-allowlists.json +13 -23
  19. package/config/merge-quality-checks.json +1 -0
  20. package/config/model-candidates.json +6 -121
  21. package/openapi/openapi.yaml +0 -12
  22. package/package.json +99 -229
  23. package/public/blog.html +4 -4
  24. package/public/codex-plugin.html +20 -72
  25. package/public/compare.html +8 -31
  26. package/public/dashboard.html +166 -930
  27. package/public/federal.html +2 -2
  28. package/public/guide.html +9 -33
  29. package/public/index.html +116 -495
  30. package/public/learn.html +18 -191
  31. package/public/lessons.html +10 -168
  32. package/public/numbers.html +7 -7
  33. package/public/pro.html +11 -34
  34. package/scripts/agent-memory-lifecycle.js +0 -211
  35. package/scripts/agent-readiness.js +3 -167
  36. package/scripts/agent-reward-model.js +1 -53
  37. package/scripts/auto-promote-gates.js +10 -82
  38. package/scripts/auto-wire-hooks.js +0 -14
  39. package/scripts/billing.js +1 -93
  40. package/scripts/build-metadata.js +10 -50
  41. package/scripts/cli-feedback.js +6 -18
  42. package/scripts/cli-schema.js +0 -90
  43. package/scripts/cli-telemetry.js +1 -6
  44. package/scripts/commercial-offer.js +2 -82
  45. package/scripts/context-manager.js +6 -74
  46. package/scripts/dashboard.js +2 -68
  47. package/scripts/durability/step.js +3 -3
  48. package/scripts/export-databricks-bundle.js +1 -5
  49. package/scripts/export-dpo-pairs.js +2 -7
  50. package/scripts/feedback-loop.js +1 -145
  51. package/scripts/feedback-quality.js +0 -87
  52. package/scripts/filesystem-search.js +10 -35
  53. package/scripts/gate-stats.js +0 -89
  54. package/scripts/gates-engine.js +85 -1105
  55. package/scripts/gemini-embedding-policy.js +1 -2
  56. package/scripts/hook-runtime.js +14 -20
  57. package/scripts/hook-thumbgate-cache-updater.js +2 -18
  58. package/scripts/hybrid-feedback-context.js +7 -142
  59. package/scripts/lesson-inference.js +3 -8
  60. package/scripts/lesson-search.js +1 -17
  61. package/scripts/license.js +1 -0
  62. package/scripts/llm-client.js +9 -390
  63. package/scripts/local-model-profile.js +8 -15
  64. package/scripts/memory-scope-readiness.js +0 -159
  65. package/scripts/meta-agent-loop.js +0 -36
  66. package/scripts/operational-integrity.js +5 -39
  67. package/scripts/oss-pr-opportunity-scout.js +5 -35
  68. package/scripts/plausible-server-events.js +6 -9
  69. package/scripts/pro-local-dashboard.js +4 -4
  70. package/scripts/proxy-pointer-rag-guardrails.js +1 -42
  71. package/scripts/rate-limiter.js +13 -64
  72. package/scripts/secret-scanner.js +5 -44
  73. package/scripts/security-scanner.js +10 -260
  74. package/scripts/self-distill-agent.js +1 -3
  75. package/scripts/seo-gsd.js +2 -916
  76. package/scripts/statusline-cache-path.js +2 -17
  77. package/scripts/statusline-local-stats.js +1 -9
  78. package/scripts/statusline-meta.js +2 -28
  79. package/scripts/statusline.sh +4 -20
  80. package/scripts/telemetry-analytics.js +0 -357
  81. package/scripts/thompson-sampling.js +10 -31
  82. package/scripts/thumbgate-bench.js +1 -16
  83. package/scripts/thumbgate-search.js +19 -85
  84. package/scripts/tool-registry.js +1 -169
  85. package/scripts/vector-store.js +0 -45
  86. package/scripts/workflow-sentinel.js +53 -286
  87. package/scripts/workspace-evolver.js +2 -62
  88. package/src/api/server.js +290 -2578
  89. package/.claude/commands/dashboard.md +0 -15
  90. package/.claude/commands/thumbgate-blocked.md +0 -27
  91. package/.claude/commands/thumbgate-dashboard.md +0 -15
  92. package/.claude/commands/thumbgate-doctor.md +0 -30
  93. package/.claude/commands/thumbgate-guard.md +0 -36
  94. package/.claude/commands/thumbgate-protect.md +0 -30
  95. package/.claude/commands/thumbgate-rules.md +0 -30
  96. package/.well-known/agentic-verify.txt +0 -1
  97. package/adapters/gcp/dfcx-webhook-gate.js +0 -295
  98. package/adapters/letta/README.md +0 -41
  99. package/adapters/letta/thumbgate-letta-adapter.js +0 -133
  100. package/adapters/policy-engine/ethicore-guardian-client.js +0 -68
  101. package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +0 -260
  102. package/bench/observability-eval-suite.json +0 -26
  103. package/bin/dashboard-cli.js +0 -7
  104. package/commands/dashboard.md +0 -15
  105. package/commands/thumbgate-dashboard.md +0 -15
  106. package/config/builtin-lessons.json +0 -23
  107. package/config/gate-classifier-routing.json +0 -98
  108. package/config/post-deploy-marketing-pages.json +0 -61
  109. package/public/about.html +0 -162
  110. package/public/agent-manager.html +0 -179
  111. package/public/agents-cost-savings.html +0 -153
  112. package/public/ai-malpractice-prevention.html +0 -818
  113. package/public/assets/brand/github-social-preview.png +0 -0
  114. package/public/assets/brand/thumbgate-icon-512.png +0 -0
  115. package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
  116. package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
  117. package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
  118. package/public/assets/brand/thumbgate-logo-transparent.svg +0 -22
  119. package/public/assets/brand/thumbgate-mark-inline-v3.svg +0 -19
  120. package/public/assets/brand/thumbgate-mark-pro.svg +0 -23
  121. package/public/assets/brand/thumbgate-mark-team.svg +0 -26
  122. package/public/assets/brand/thumbgate-mark.svg +0 -21
  123. package/public/assets/brand/thumbgate-wordmark.svg +0 -20
  124. package/public/assets/claude-thumbgate-statusbar.svg +0 -8
  125. package/public/assets/codex-thumbgate-statusbar-test.svg +0 -9
  126. package/public/assets/legal-intake-control-flow.svg +0 -66
  127. package/public/brand/thumbgate-mark.svg +0 -19
  128. package/public/brand/thumbgate-og.svg +0 -16
  129. package/public/chatgpt-app.html +0 -330
  130. package/public/codex-enterprise.html +0 -123
  131. package/public/js/buyer-intent.js +0 -672
  132. package/public/pricing.html +0 -399
  133. package/scripts/action-receipts.js +0 -324
  134. package/scripts/activation-quickstart.js +0 -187
  135. package/scripts/agent-operations-planner.js +0 -621
  136. package/scripts/ai-component-inventory.js +0 -367
  137. package/scripts/async-eval-observability.js +0 -236
  138. package/scripts/audit.js +0 -65
  139. package/scripts/aws-blocks-guardrails.js +0 -228
  140. package/scripts/classifier-routing.js +0 -130
  141. package/scripts/dashboard-chat.js +0 -332
  142. package/scripts/feedback-aggregate.js +0 -281
  143. package/scripts/feedback-sanitizer.js +0 -105
  144. package/scripts/hob-pack.js +0 -591
  145. package/scripts/hook-stop-anti-claim.js +0 -301
  146. package/scripts/install-shim.js +0 -87
  147. package/scripts/mcp-oauth.js +0 -293
  148. package/scripts/noop-detect.js +0 -285
  149. package/scripts/omlx-smoke.js +0 -192
  150. package/scripts/parallel-workflow-orchestrator.js +0 -293
  151. package/scripts/plan-gate.js +0 -243
  152. package/scripts/plausible-domain-config.js +0 -99
  153. package/scripts/qa-scenario-planner.js +0 -136
  154. package/scripts/repeat-metric.js +0 -137
  155. package/scripts/secret-fixture-tokens.js +0 -61
  156. package/scripts/secret-redaction.js +0 -166
  157. package/scripts/self-harness-optimizer.js +0 -141
  158. package/scripts/self-healing-check.js +0 -193
  159. package/scripts/silent-failure-cluster.js +0 -531
  160. package/scripts/statusline-cache-read.js +0 -57
  161. package/scripts/sync-telemetry-from-prod.js +0 -374
  162. package/scripts/tool-contract-validator.js +0 -76
  163. package/scripts/trajectory-scorer.js +0 -63
  164. package/scripts/verify-marketing-pages-deployed.js +0 -195
  165. package/scripts/visitor-journey.js +0 -172
@@ -1,672 +0,0 @@
1
- (function(global) {
2
- var BUYER_EMAIL_STORAGE_KEY = 'thumbgateBuyerEmail';
3
- var CHECKOUT_LINK_SELECTOR = 'a[href*="/checkout/pro"], a[href*="/go/pro"]';
4
- var BUYER_EMAIL_SELECTOR = '[data-buyer-email]';
5
-
6
- function normalizeBuyerEmail(value) {
7
- return String(value || '').trim().toLowerCase();
8
- }
9
-
10
- function isValidBuyerEmail(value) {
11
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizeBuyerEmail(value));
12
- }
13
-
14
- function getStorage() {
15
- return global.localStorage && typeof global.localStorage.getItem === 'function'
16
- ? global.localStorage
17
- : null;
18
- }
19
-
20
- function getStoredBuyerEmail() {
21
- var storage = getStorage();
22
- if (!storage) {
23
- return '';
24
- }
25
- try {
26
- return normalizeBuyerEmail(storage.getItem(BUYER_EMAIL_STORAGE_KEY));
27
- } catch (_error) {
28
- return '';
29
- }
30
- }
31
-
32
- function storeBuyerEmail(email) {
33
- var storage = getStorage();
34
- if (!storage) {
35
- return false;
36
- }
37
- try {
38
- storage.setItem(BUYER_EMAIL_STORAGE_KEY, normalizeBuyerEmail(email));
39
- return true;
40
- } catch (_error) {
41
- return false;
42
- }
43
- }
44
-
45
- function resolveCheckoutUrl(urlValue, email) {
46
- var origin = global.location && global.location.origin
47
- ? global.location.origin
48
- : 'https://thumbgate.invalid';
49
- var checkoutUrl = new URL(String(urlValue || '/checkout/pro'), origin);
50
- var isHostedProRoute = checkoutUrl.origin === origin
51
- && (checkoutUrl.pathname === '/checkout/pro' || checkoutUrl.pathname === '/go/pro');
52
- if (!isHostedProRoute) {
53
- checkoutUrl = new URL('/checkout/pro', origin);
54
- }
55
- checkoutUrl.pathname = '/checkout/pro';
56
- checkoutUrl.searchParams.set('confirm', '1');
57
- if (isValidBuyerEmail(email)) {
58
- checkoutUrl.searchParams.set('customer_email', normalizeBuyerEmail(email));
59
- } else {
60
- checkoutUrl.searchParams.delete('customer_email');
61
- }
62
- return checkoutUrl;
63
- }
64
-
65
- function getCheckoutLinks(selector) {
66
- if (!global.document || typeof global.document.querySelectorAll !== 'function') {
67
- return [];
68
- }
69
- return Array.from(global.document.querySelectorAll(selector || CHECKOUT_LINK_SELECTOR));
70
- }
71
-
72
- function getBaseCheckoutHref(link) {
73
- if (!link.dataset.baseHref) {
74
- link.dataset.baseHref = link.getAttribute('href') || link.href || '/checkout/pro';
75
- }
76
- return link.dataset.baseHref;
77
- }
78
-
79
- function applyBuyerEmailToCheckoutLinks(email, selector) {
80
- getCheckoutLinks(selector).forEach(function(link) {
81
- link.href = resolveCheckoutUrl(getBaseCheckoutHref(link), email).toString();
82
- });
83
- }
84
-
85
- function hydrateBuyerEmailInputs(email, selector) {
86
- if (!global.document || typeof global.document.querySelectorAll !== 'function') {
87
- return;
88
- }
89
- Array.from(global.document.querySelectorAll(selector || BUYER_EMAIL_SELECTOR)).forEach(function(input) {
90
- if (!input.value) {
91
- input.value = normalizeBuyerEmail(email);
92
- }
93
- });
94
- }
95
-
96
- function getNewsletterStatusElement(form) {
97
- if (!form) {
98
- return null;
99
- }
100
- return form.querySelector('[data-newsletter-status]')
101
- || (form.parentElement ? form.parentElement.querySelector('[data-newsletter-status]') : null);
102
- }
103
-
104
- function setNewsletterStatus(form, message, ok) {
105
- var statusEl = getNewsletterStatusElement(form);
106
- if (!statusEl) {
107
- return;
108
- }
109
- statusEl.textContent = message;
110
- statusEl.style.color = ok ? 'var(--cyan)' : 'var(--red, #f87171)';
111
- }
112
-
113
- async function submitNewsletterSignup(email, form) {
114
- var action = form && form.action ? form.action : '/api/newsletter';
115
- var response = await fetch(action, {
116
- method: 'POST',
117
- headers: {
118
- 'Content-Type': 'application/x-www-form-urlencoded',
119
- Accept: 'application/json',
120
- 'X-Requested-With': 'fetch',
121
- },
122
- body: new URLSearchParams({ email: normalizeBuyerEmail(email) }).toString(),
123
- credentials: 'same-origin',
124
- });
125
- if (!response.ok) {
126
- var errorMessage = 'Unable to save your email right now.';
127
- try {
128
- var errorBody = await response.json();
129
- if (errorBody && errorBody.error) {
130
- errorMessage = errorBody.error;
131
- }
132
- } catch (_error) {
133
- // Keep the default error message when the response is not JSON.
134
- }
135
- throw new Error(errorMessage);
136
- }
137
- try {
138
- return await response.json();
139
- } catch (_error) {
140
- return { accepted: true, duplicate: false };
141
- }
142
- }
143
-
144
- function trackEvent(eventName, props) {
145
- if (typeof global.plausible === 'function') {
146
- global.plausible(eventName, { props: props || {} });
147
- return;
148
- }
149
- global.plausible = global.plausible || function() {
150
- (global.plausible.q = global.plausible.q || []).push(arguments);
151
- };
152
- if (typeof global.plausible === 'function') {
153
- global.plausible(eventName, { props: props || {} });
154
- }
155
- }
156
-
157
- function getCurrentPathname() {
158
- return global.location && global.location.pathname
159
- ? global.location.pathname.replace(/\/$/, '') || '/'
160
- : '/';
161
- }
162
-
163
- function normalizePlacement(pathname) {
164
- return String(pathname || '/')
165
- .replace(/\.html$/, '')
166
- .replace(/^\//, '')
167
- .replace(/[^a-z0-9]+/gi, '_')
168
- .replace(/^_+|_+$/g, '')
169
- .toLowerCase() || 'home';
170
- }
171
-
172
- function isRevenueAssistEligible(pathname) {
173
- var path = String(pathname || '/');
174
- if (path === '/checkout/pro' || path.indexOf('/go/pro') === 0) return false;
175
- if (path === '/' || path === '/guide' || path === '/guide.html') return true;
176
- if (path === '/dashboard' || path === '/dashboard.html') return true;
177
- if (path === '/learn' || path === '/learn.html' || path.indexOf('/learn/') === 0) return true;
178
- if (path === '/lessons' || path === '/lessons.html') return true;
179
- if (path === '/ai-malpractice-prevention' || path === '/ai-malpractice-prevention.html') return true;
180
- if (path.indexOf('/guides/') === 0) return true;
181
- return false;
182
- }
183
-
184
- function appendCampaignParams(href, params) {
185
- var origin = global.location && global.location.origin
186
- ? global.location.origin
187
- : 'https://thumbgate.ai';
188
- var url = new URL(href, origin);
189
- Object.keys(params || {}).forEach(function(key) {
190
- if (params[key] !== null && params[key] !== undefined) {
191
- url.searchParams.set(key, params[key]);
192
- }
193
- });
194
- return url.toString();
195
- }
196
-
197
- function injectRevenueAssistStyles() {
198
- if (!global.document || global.document.getElementById('thumbgate-revenue-assist-style')) {
199
- return;
200
- }
201
- var style = global.document.createElement('style');
202
- style.id = 'thumbgate-revenue-assist-style';
203
- style.textContent = [
204
- '[data-thumbgate-revenue-assist]{position:fixed;right:18px;bottom:18px;z-index:2147483000;width:min(360px,calc(100vw - 32px));background:#0f172a;color:#f8fafc;border:1px solid rgba(148,163,184,.35);border-radius:8px;box-shadow:0 20px 60px rgba(2,6,23,.38);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;padding:14px}',
205
- '[data-thumbgate-revenue-assist] strong{display:block;font-size:15px;margin:0 0 5px}',
206
- '[data-thumbgate-revenue-assist] p{margin:0 0 10px;color:#cbd5e1}',
207
- '[data-thumbgate-revenue-assist] nav{display:flex;gap:8px;flex-wrap:wrap}',
208
- '[data-thumbgate-revenue-assist] a,[data-thumbgate-revenue-assist] button{border-radius:6px;border:1px solid rgba(148,163,184,.45);padding:8px 10px;font-weight:700;text-decoration:none;cursor:pointer}',
209
- '[data-thumbgate-revenue-assist] a:first-child{background:#22d3ee;color:#082f49;border-color:#22d3ee}',
210
- '[data-thumbgate-revenue-assist] a:nth-child(2){background:#f8fafc;color:#0f172a;border-color:#f8fafc}',
211
- '[data-thumbgate-revenue-assist] button{background:transparent;color:#cbd5e1}',
212
- '[data-thumbgate-abandon-survey]{position:fixed;right:18px;bottom:18px;z-index:2147483001;width:min(380px,calc(100vw - 32px));background:#111827;color:#f9fafb;border:1px solid rgba(148,163,184,.4);border-radius:8px;box-shadow:0 20px 60px rgba(2,6,23,.42);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;padding:14px}',
213
- '[data-thumbgate-abandon-survey] strong{display:block;font-size:15px;margin-bottom:8px}',
214
- '[data-thumbgate-abandon-survey] div{display:grid;gap:7px}',
215
- '[data-thumbgate-abandon-survey] button{border-radius:6px;border:1px solid rgba(148,163,184,.45);background:#1f2937;color:#f9fafb;padding:8px 10px;text-align:left;cursor:pointer}',
216
- '@media (max-width:640px){[data-thumbgate-revenue-assist],[data-thumbgate-abandon-survey]{right:12px;bottom:12px;width:calc(100vw - 24px)}}'
217
- ].join('');
218
- global.document.head.appendChild(style);
219
- }
220
-
221
- function initializeRevenueAssist(options) {
222
- var settings = options || {};
223
- if (!global.document || typeof global.document.querySelector !== 'function') return null;
224
- if (global.document.querySelector('[data-revenue-assist="off"]')) return null;
225
- if (global.document.querySelector('[data-thumbgate-revenue-assist]')) return null;
226
-
227
- var pathname = settings.pathname || getCurrentPathname();
228
- if (!isRevenueAssistEligible(pathname)) return null;
229
-
230
- injectRevenueAssistStyles();
231
-
232
- var placement = settings.placement || normalizePlacement(pathname);
233
- var campaign = settings.campaign || 'high_traffic_pages';
234
- var proHref = appendCampaignParams(settings.proHref || '/checkout/pro', {
235
- confirm: '1',
236
- utm_source: 'owned_site',
237
- utm_medium: 'sticky_cta',
238
- utm_campaign: campaign,
239
- cta_id: 'assist_pro_checkout',
240
- cta_placement: placement,
241
- plan_id: 'pro',
242
- });
243
- var intakeHref = appendCampaignParams(settings.intakeHref || '/#workflow-sprint-intake', {
244
- utm_source: 'owned_site',
245
- utm_medium: 'sticky_cta',
246
- utm_campaign: campaign,
247
- cta_id: 'assist_workflow_intake',
248
- cta_placement: placement,
249
- client_reference_id: 'thumbgate_assist_' + placement,
250
- });
251
-
252
- var panel = global.document.createElement('aside');
253
- panel.setAttribute('data-thumbgate-revenue-assist', placement);
254
- panel.setAttribute('aria-label', 'ThumbGate paid help');
255
- panel.innerHTML = [
256
- '<strong>Stop the repeated agent failure?</strong>',
257
- '<p>Use Pro for self-serve proof, or send the workflow first when one failure is already costing time.</p>',
258
- '<nav>',
259
- '<a data-assist-cta="assist_pro_checkout" href="' + proHref + '">Get Pro</a>',
260
- '<a data-assist-cta="assist_workflow_intake" href="' + intakeHref + '">Send workflow first</a>',
261
- '<button type="button" data-assist-dismiss>Not now</button>',
262
- '</nav>'
263
- ].join('');
264
-
265
- panel.querySelectorAll('[data-assist-cta]').forEach(function(link) {
266
- link.addEventListener('click', function() {
267
- try {
268
- global.sessionStorage.setItem('thumbgateRevenueAssistCheckoutSeen', '1');
269
- } catch (_error) {}
270
- trackEvent('assist_cta_click', {
271
- ctaId: link.getAttribute('data-assist-cta'),
272
- ctaPlacement: placement,
273
- page: pathname,
274
- });
275
- });
276
- });
277
-
278
- var dismissButton = panel.querySelector('[data-assist-dismiss]');
279
- if (dismissButton) {
280
- dismissButton.addEventListener('click', function() {
281
- trackEvent('assist_cta_dismiss', {
282
- ctaPlacement: placement,
283
- page: pathname,
284
- });
285
- panel.remove();
286
- showAbandonSurvey('cta_dismiss');
287
- });
288
- }
289
-
290
- global.document.body.appendChild(panel);
291
- trackEvent('assist_cta_impression', {
292
- ctaPlacement: placement,
293
- page: pathname,
294
- });
295
-
296
- function wasSurveyShown() {
297
- try {
298
- return global.sessionStorage.getItem('thumbgateRevenueAssistSurveyShown') === '1';
299
- } catch (_error) {
300
- return false;
301
- }
302
- }
303
-
304
- function markSurveyShown() {
305
- try {
306
- global.sessionStorage.setItem('thumbgateRevenueAssistSurveyShown', '1');
307
- } catch (_error) {}
308
- }
309
-
310
- function checkoutWasSeen() {
311
- try {
312
- return global.sessionStorage.getItem('thumbgateRevenueAssistCheckoutSeen') === '1';
313
- } catch (_error) {
314
- return false;
315
- }
316
- }
317
-
318
- function showAbandonSurvey(trigger) {
319
- if (wasSurveyShown() || checkoutWasSeen() || global.document.querySelector('[data-thumbgate-abandon-survey]')) {
320
- return;
321
- }
322
- markSurveyShown();
323
- var survey = global.document.createElement('aside');
324
- survey.setAttribute('data-thumbgate-abandon-survey', trigger || 'unknown');
325
- survey.setAttribute('aria-label', 'ThumbGate checkout feedback');
326
- survey.innerHTML = [
327
- '<strong>What stopped you from buying today?</strong>',
328
- '<div>',
329
- '<button type="button" data-abandon-reason="fit_unclear">Not sure it fits my agent stack</button>',
330
- '<button type="button" data-abandon-reason="need_proof">Need proof before paying</button>',
331
- '<button type="button" data-abandon-reason="price_scope_unclear">Price or scope is unclear</button>',
332
- '<button type="button" data-abandon-reason="researching">Just researching</button>',
333
- '</div>'
334
- ].join('');
335
- survey.querySelectorAll('[data-abandon-reason]').forEach(function(button) {
336
- button.addEventListener('click', function() {
337
- trackEvent('checkout_abandon_reason', {
338
- reason: button.getAttribute('data-abandon-reason'),
339
- trigger: trigger || 'unknown',
340
- ctaPlacement: placement,
341
- page: pathname,
342
- });
343
- survey.remove();
344
- });
345
- });
346
- global.document.body.appendChild(survey);
347
- trackEvent('checkout_abandon_prompt', {
348
- trigger: trigger || 'unknown',
349
- ctaPlacement: placement,
350
- page: pathname,
351
- });
352
- }
353
-
354
- if (global.setTimeout) {
355
- global.setTimeout(function() {
356
- showAbandonSurvey('dwell_45s');
357
- }, settings.surveyDelayMs || 45000);
358
- }
359
- if (global.document && global.document.addEventListener) {
360
- global.document.addEventListener('mouseleave', function(event) {
361
- if (event && event.clientY <= 0) {
362
- showAbandonSurvey('exit_intent');
363
- }
364
- });
365
- }
366
-
367
- return {
368
- panel: panel,
369
- showAbandonSurvey: showAbandonSurvey,
370
- placement: placement,
371
- };
372
- }
373
-
374
- function normalizeInteger(value) {
375
- var parsed = Number(value);
376
- return Number.isFinite(parsed) ? Math.round(parsed) : null;
377
- }
378
-
379
- function bucketDwellMs(value) {
380
- var ms = normalizeInteger(value) || 0;
381
- if (ms < 10000) return 'under_10s';
382
- if (ms < 30000) return '10s_to_30s';
383
- if (ms < 60000) return '30s_to_60s';
384
- if (ms < 180000) return '1m_to_3m';
385
- return 'over_3m';
386
- }
387
-
388
- function bucketScrollPercent(value) {
389
- var pct = normalizeInteger(value);
390
- if (pct === null) return 'unknown';
391
- if (pct < 25) return 'under_25';
392
- if (pct < 50) return '25_to_49';
393
- if (pct < 75) return '50_to_74';
394
- if (pct < 100) return '75_to_99';
395
- return '100';
396
- }
397
-
398
- function initializeBehaviorAnalytics(options) {
399
- var settings = options || {};
400
- var sendTelemetry = typeof settings.sendTelemetry === 'function'
401
- ? settings.sendTelemetry
402
- : function() {};
403
- var state = {
404
- startedAt: Date.now(),
405
- maxScrollPercent: 0,
406
- lastVisibleSection: settings.initialSectionId || null,
407
- emailFocused: false,
408
- emailCaptured: false,
409
- sectionSeen: Object.create(null),
410
- ctaSeen: Object.create(null),
411
- exitSent: false,
412
- };
413
-
414
- function emit(eventType, extra) {
415
- sendTelemetry(eventType, Object.assign({
416
- pageType: settings.pageType || 'marketing',
417
- page: settings.pagePath || (global.location ? global.location.pathname : null),
418
- landingPath: settings.landingPath || (global.location ? global.location.pathname : null),
419
- }, extra || {}));
420
- }
421
-
422
- function observeTargets(targets, callback, threshold) {
423
- if (!global.IntersectionObserver || !Array.isArray(targets) || !targets.length || !global.document) {
424
- return null;
425
- }
426
- var observer = new global.IntersectionObserver(function(entries) {
427
- entries.forEach(function(entry) {
428
- if (!entry.isIntersecting) return;
429
- callback(entry.target);
430
- });
431
- }, { threshold: threshold || 0.45 });
432
-
433
- targets.forEach(function(target) {
434
- if (target && target.element) {
435
- observer.observe(target.element);
436
- }
437
- });
438
- return observer;
439
- }
440
-
441
- function resolveTargets(items) {
442
- if (!global.document || typeof global.document.querySelector !== 'function') {
443
- return [];
444
- }
445
- return (items || []).map(function(item) {
446
- var element = global.document.querySelector(item.selector);
447
- if (!element) return null;
448
- return Object.assign({ element: element }, item);
449
- }).filter(Boolean);
450
- }
451
-
452
- function markEmailCaptured() {
453
- state.emailCaptured = true;
454
- }
455
-
456
- var sectionTargets = resolveTargets(settings.sections);
457
- observeTargets(sectionTargets, function(target) {
458
- var sectionId = target.sectionId || target.id || target.selector || 'unknown';
459
- state.lastVisibleSection = sectionId;
460
- if (state.sectionSeen[sectionId]) return;
461
- state.sectionSeen[sectionId] = true;
462
- emit('section_view', {
463
- sectionId: sectionId,
464
- sectionLabel: target.sectionLabel || sectionId,
465
- });
466
- }, 0.35);
467
-
468
- var ctaTargets = resolveTargets(settings.ctaImpressions);
469
- observeTargets(ctaTargets, function(target) {
470
- var ctaId = target.ctaId || target.selector || 'unknown_cta';
471
- if (state.ctaSeen[ctaId]) return;
472
- state.ctaSeen[ctaId] = true;
473
- emit('cta_impression', {
474
- ctaId: ctaId,
475
- ctaPlacement: target.ctaPlacement || null,
476
- planId: target.planId || null,
477
- });
478
- }, 0.6);
479
-
480
- if (global.addEventListener) {
481
- global.addEventListener('scroll', function() {
482
- if (!global.document || !global.document.documentElement) return;
483
- var docHeight = global.document.documentElement.scrollHeight - (global.innerHeight || 0);
484
- if (docHeight <= 0) {
485
- state.maxScrollPercent = 100;
486
- return;
487
- }
488
- var nextPercent = Math.max(0, Math.min(100, Math.round(((global.scrollY || 0) / docHeight) * 100)));
489
- if (nextPercent > state.maxScrollPercent) {
490
- state.maxScrollPercent = nextPercent;
491
- }
492
- }, { passive: true });
493
- }
494
-
495
- if (global.document && typeof global.document.querySelectorAll === 'function') {
496
- var emailSelector = settings.emailSelector || '[data-buyer-email]';
497
- Array.from(global.document.querySelectorAll(emailSelector)).forEach(function(input) {
498
- input.addEventListener('focus', function() {
499
- if (state.emailFocused) return;
500
- state.emailFocused = true;
501
- emit('buyer_email_focus', {
502
- ctaId: settings.emailCtaId || 'buyer_email',
503
- ctaPlacement: settings.emailCtaPlacement || null,
504
- });
505
- });
506
- });
507
-
508
- Array.from(global.document.querySelectorAll(settings.newsletterFormSelector || '[data-newsletter-form]')).forEach(function(form) {
509
- form.addEventListener('submit', function() {
510
- var input = form.querySelector(settings.formEmailSelector || 'input[name="email"]');
511
- if (isValidBuyerEmail(getEmailFromInput(input))) {
512
- markEmailCaptured();
513
- }
514
- });
515
- });
516
- }
517
-
518
- function sendExitSignals() {
519
- if (state.exitSent) return;
520
- state.exitSent = true;
521
- var engagementMs = Math.max(0, Date.now() - state.startedAt);
522
- emit('page_exit', {
523
- lastVisibleSection: state.lastVisibleSection || 'unknown',
524
- engagementMs: engagementMs,
525
- dwellBucket: bucketDwellMs(engagementMs),
526
- maxScrollPercent: state.maxScrollPercent,
527
- scrollBucket: bucketScrollPercent(state.maxScrollPercent),
528
- buyerEmailFocused: state.emailFocused,
529
- buyerEmailCaptured: state.emailCaptured,
530
- });
531
- if (state.emailFocused && !state.emailCaptured) {
532
- emit('buyer_email_abandon', {
533
- lastVisibleSection: state.lastVisibleSection || 'unknown',
534
- engagementMs: engagementMs,
535
- dwellBucket: bucketDwellMs(engagementMs),
536
- });
537
- }
538
- }
539
-
540
- if (global.addEventListener) {
541
- global.addEventListener('pagehide', sendExitSignals);
542
- global.addEventListener('beforeunload', sendExitSignals);
543
- }
544
-
545
- return {
546
- markEmailCaptured: markEmailCaptured,
547
- sendExitSignals: sendExitSignals,
548
- };
549
- }
550
-
551
- function getEmailFromInput(input) {
552
- return normalizeBuyerEmail(input && input.value);
553
- }
554
-
555
- function initializeBuyerIntent(options) {
556
- var settings = options || {};
557
- var storedEmail = getStoredBuyerEmail();
558
- if (storedEmail) {
559
- hydrateBuyerEmailInputs(storedEmail, settings.emailSelector);
560
- applyBuyerEmailToCheckoutLinks(storedEmail, settings.checkoutSelector);
561
- }
562
-
563
- if (!global.document || typeof global.document.querySelectorAll !== 'function') {
564
- return;
565
- }
566
-
567
- Array.from(global.document.querySelectorAll(settings.formSelector || '[data-newsletter-form]')).forEach(function(form) {
568
- form.addEventListener('submit', async function(event) {
569
- event.preventDefault();
570
- var input = form.querySelector(settings.formEmailSelector || 'input[name="email"]');
571
- var email = getEmailFromInput(input);
572
- if (!isValidBuyerEmail(email)) {
573
- setNewsletterStatus(form, settings.invalidEmailMessage || 'Enter a valid work email.', false);
574
- if (input) {
575
- input.focus();
576
- }
577
- return;
578
- }
579
-
580
- storeBuyerEmail(email);
581
- hydrateBuyerEmailInputs(email, settings.emailSelector);
582
- applyBuyerEmailToCheckoutLinks(email, settings.checkoutSelector);
583
-
584
- try {
585
- var result = await submitNewsletterSignup(email, form);
586
- var successMessage = result && result.duplicate
587
- ? (settings.duplicateMessage || 'You are already on the list. Checkout on this device is now prefilled.')
588
- : (settings.successMessage || 'Saved. We will keep checkout prefilled on this device.');
589
- setNewsletterStatus(form, successMessage, true);
590
- trackEvent('newsletter_signup', {
591
- page: form.dataset.page || settings.page || 'homepage',
592
- intent: form.dataset.intent || settings.intent || 'buyer_follow_up',
593
- });
594
- } catch (error) {
595
- setNewsletterStatus(
596
- form,
597
- error && error.message ? error.message : 'Unable to save your email right now.',
598
- false
599
- );
600
- }
601
- });
602
- });
603
- }
604
-
605
- function initializeEmailCheckoutButtons(options) {
606
- var settings = options || {};
607
- if (!global.document || typeof global.document.querySelectorAll !== 'function') {
608
- return;
609
- }
610
-
611
- Array.from(global.document.querySelectorAll(settings.buttonSelector || '.btn-email-checkout')).forEach(function(button) {
612
- button.addEventListener('click', async function() {
613
- var form = button.closest('form');
614
- var input = form ? form.querySelector(settings.formEmailSelector || 'input[name="email"]') : null;
615
- var email = getEmailFromInput(input) || getStoredBuyerEmail();
616
- if (!isValidBuyerEmail(email)) {
617
- setNewsletterStatus(form, settings.invalidCheckoutMessage || 'Enter a valid work email before checkout.', false);
618
- if (input) {
619
- input.focus();
620
- }
621
- return;
622
- }
623
-
624
- storeBuyerEmail(email);
625
- hydrateBuyerEmailInputs(email, settings.emailSelector);
626
- applyBuyerEmailToCheckoutLinks(email, settings.checkoutSelector);
627
- trackEvent(settings.eventName || 'pro_checkout_email_start', settings.eventProps || { page: 'pro', intent: 'checkout' });
628
-
629
- try {
630
- await submitNewsletterSignup(email, form);
631
- } catch (_error) {
632
- // Continue to checkout even if signup persistence fails.
633
- }
634
-
635
- var checkoutLink = global.document.querySelector(settings.checkoutLinkSelector || '.btn-pro-checkout');
636
- if (checkoutLink) {
637
- global.location.assign(checkoutLink.href);
638
- }
639
- });
640
- });
641
- }
642
-
643
- global.ThumbGateBuyerIntent = {
644
- normalizeBuyerEmail: normalizeBuyerEmail,
645
- isValidBuyerEmail: isValidBuyerEmail,
646
- getStoredBuyerEmail: getStoredBuyerEmail,
647
- storeBuyerEmail: storeBuyerEmail,
648
- resolveCheckoutUrl: resolveCheckoutUrl,
649
- applyBuyerEmailToCheckoutLinks: applyBuyerEmailToCheckoutLinks,
650
- hydrateBuyerEmailInputs: hydrateBuyerEmailInputs,
651
- setNewsletterStatus: setNewsletterStatus,
652
- submitNewsletterSignup: submitNewsletterSignup,
653
- initializeBuyerIntent: initializeBuyerIntent,
654
- initializeEmailCheckoutButtons: initializeEmailCheckoutButtons,
655
- initializeRevenueAssist: initializeRevenueAssist,
656
- isRevenueAssistEligible: isRevenueAssistEligible,
657
- trackEvent: trackEvent,
658
- initializeBehaviorAnalytics: initializeBehaviorAnalytics,
659
- bucketDwellMs: bucketDwellMs,
660
- bucketScrollPercent: bucketScrollPercent,
661
- };
662
-
663
- if (global.document) {
664
- if (global.document.readyState === 'loading') {
665
- global.document.addEventListener('DOMContentLoaded', function() {
666
- initializeRevenueAssist();
667
- });
668
- } else {
669
- initializeRevenueAssist();
670
- }
671
- }
672
- })(globalThis);