thumbgate 1.16.13 → 1.16.20

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 (64) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.well-known/mcp/server-card.json +1 -1
  4. package/README.md +3 -1
  5. package/adapters/claude/.mcp.json +2 -2
  6. package/adapters/mcp/server-stdio.js +26 -1
  7. package/adapters/opencode/opencode.json +1 -1
  8. package/bin/cli.js +420 -1
  9. package/bin/postinstall.js +2 -2
  10. package/config/gate-templates.json +372 -0
  11. package/config/mcp-allowlists.json +25 -0
  12. package/config/model-candidates.json +59 -2
  13. package/config/model-tiers.json +4 -1
  14. package/package.json +79 -22
  15. package/public/compare.html +6 -0
  16. package/public/index.html +153 -20
  17. package/public/numbers.html +6 -6
  18. package/public/pro.html +25 -27
  19. package/scripts/agent-design-governance.js +211 -0
  20. package/scripts/agent-reasoning-traces.js +683 -0
  21. package/scripts/agent-reward-model.js +438 -0
  22. package/scripts/agent-stack-survival-audit.js +231 -0
  23. package/scripts/ai-engineering-stack-guardrails.js +256 -0
  24. package/scripts/billing.js +33 -5
  25. package/scripts/chatgpt-ads-readiness-pack.js +195 -0
  26. package/scripts/cli-schema.js +277 -0
  27. package/scripts/code-graph-guardrails.js +176 -0
  28. package/scripts/commercial-offer.js +1 -1
  29. package/scripts/deepseek-v4-runtime-guardrails.js +253 -0
  30. package/scripts/gemini-embedding-policy.js +198 -0
  31. package/scripts/inference-cache-policy.js +39 -0
  32. package/scripts/judge-reward-function.js +396 -0
  33. package/scripts/llm-behavior-monitor.js +251 -0
  34. package/scripts/long-running-agent-context-guardrails.js +176 -0
  35. package/scripts/multimodal-retrieval-plan.js +31 -11
  36. package/scripts/oss-pr-opportunity-scout.js +240 -0
  37. package/scripts/proactive-agent-eval-guardrails.js +230 -0
  38. package/scripts/profile-router.js +5 -4
  39. package/scripts/prompting-operating-system.js +273 -0
  40. package/scripts/proxy-pointer-rag-guardrails.js +189 -0
  41. package/scripts/rag-precision-guardrails.js +202 -0
  42. package/scripts/rate-limiter.js +1 -1
  43. package/scripts/reasoning-efficiency-guardrails.js +176 -0
  44. package/scripts/reward-hacking-guardrails.js +251 -0
  45. package/scripts/seo-gsd.js +1201 -11
  46. package/scripts/single-use-credential-gate.js +182 -0
  47. package/scripts/structured-prompt-driven.js +226 -0
  48. package/scripts/telemetry-analytics.js +108 -6
  49. package/scripts/tool-registry.js +92 -0
  50. package/scripts/upstream-contribution-engine.js +379 -0
  51. package/scripts/vector-store.js +119 -4
  52. package/src/api/server.js +455 -143
  53. package/scripts/agents-sdk-sandbox-plan.js +0 -57
  54. package/scripts/ai-org-governance.js +0 -98
  55. package/scripts/artifact-agent-plan.js +0 -81
  56. package/scripts/enterprise-agent-rollout.js +0 -34
  57. package/scripts/experience-replay-governance.js +0 -69
  58. package/scripts/inference-economics.js +0 -53
  59. package/scripts/knowledge-layer-plan.js +0 -108
  60. package/scripts/memory-store-governance.js +0 -60
  61. package/scripts/post-training-governance.js +0 -34
  62. package/scripts/production-agent-readiness.js +0 -40
  63. package/scripts/scaling-law-claims.js +0 -60
  64. package/scripts/student-consistent-training.js +0 -73
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Single-Use Credential Gate
6
+ *
7
+ * Converts the Link CLI pattern into local ThumbGate policy: risky agent
8
+ * actions should request narrow, one-time credentials with synchronous
9
+ * approval instead of reusing long-lived secrets.
10
+ */
11
+
12
+ const crypto = require('node:crypto');
13
+ const path = require('node:path');
14
+
15
+ const DEFAULT_TTL_SECONDS = 300;
16
+ const RISK_PATTERNS = [
17
+ { tag: 'purchase', pattern: /\b(buy|buys|buying|purchase|purchases|checkout|payment|gumroad|stripe|card)\b/i },
18
+ { tag: 'credential', pattern: /\b(token|secret|credential|api[_-]?key|oauth|login)\b/i },
19
+ { tag: 'deploy', pattern: /\b(deploy|production|railway|release)\b/i },
20
+ { tag: 'external-write', pattern: /\b(post|reply|send|email|upload|publish|create order)\b/i },
21
+ ];
22
+
23
+ function planSingleUseCredentialRequest(action = {}, options = {}) {
24
+ const text = buildActionText(action);
25
+ const riskTags = RISK_PATTERNS.filter((item) => item.pattern.test(text)).map((item) => item.tag);
26
+ const highRisk = riskTags.length > 0 || Boolean(action.requiresCredential);
27
+ const scope = normalizeScope(action.scope || inferScope(text));
28
+ const ttlSeconds = clamp(Number(action.ttlSeconds || options.ttlSeconds || DEFAULT_TTL_SECONDS), 30, 900);
29
+
30
+ return {
31
+ required: highRisk,
32
+ riskTags,
33
+ scope,
34
+ ttlSeconds,
35
+ singleUse: true,
36
+ approvalMode: highRisk ? 'synchronous' : 'not-required',
37
+ approvalPrompt: highRisk
38
+ ? `Approve one-time credential for ${scope.resource} (${scope.operation})? Expires in ${ttlSeconds}s and cannot be reused.`
39
+ : 'No credential approval required.',
40
+ deniedReasons: buildDeniedReasons(action, scope),
41
+ };
42
+ }
43
+
44
+ function mintCredentialGrant(request = {}, approval = {}) {
45
+ const approved = Boolean(approval.approved);
46
+ return {
47
+ grantId: `cred_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`,
48
+ approved,
49
+ singleUse: request.singleUse !== false,
50
+ scope: normalizeScope(request.scope),
51
+ issuedAt: new Date().toISOString(),
52
+ expiresAt: new Date(Date.now() + (Number(request.ttlSeconds || DEFAULT_TTL_SECONDS) * 1000)).toISOString(),
53
+ approvedBy: approval.approvedBy || null,
54
+ approvalEvidence: approval.evidence || null,
55
+ usedAt: null,
56
+ };
57
+ }
58
+
59
+ function evaluateCredentialUse(grant = {}, action = {}, now = new Date()) {
60
+ const reasons = [];
61
+ if (!grant.approved) reasons.push('credential_not_approved');
62
+ if (!grant.singleUse) reasons.push('credential_not_single_use');
63
+ if (grant.usedAt) reasons.push('credential_already_used');
64
+ if (grant.expiresAt && new Date(grant.expiresAt).getTime() < now.getTime()) reasons.push('credential_expired');
65
+
66
+ const actionScope = normalizeScope(action.scope || inferScope(buildActionText(action)));
67
+ const grantScope = normalizeScope(grant.scope);
68
+ if (!scopeAllows(grantScope, actionScope)) reasons.push('credential_scope_mismatch');
69
+
70
+ return {
71
+ allowed: reasons.length === 0,
72
+ reasons,
73
+ grantId: grant.grantId || null,
74
+ requiredScope: actionScope,
75
+ grantedScope: grantScope,
76
+ };
77
+ }
78
+
79
+ function markCredentialUsed(grant = {}, now = new Date()) {
80
+ return {
81
+ ...grant,
82
+ usedAt: now.toISOString(),
83
+ };
84
+ }
85
+
86
+ function buildActionText(action = {}) {
87
+ return [
88
+ action.command,
89
+ action.intent,
90
+ action.description,
91
+ action.url,
92
+ ...(action.tags || []),
93
+ ].filter(Boolean).join(' ');
94
+ }
95
+
96
+ function inferScope(text = '') {
97
+ if (/\b(stripe|checkout|payment|card)\b/i.test(text)) return { resource: 'payments', operation: 'write' };
98
+ if (/\b(gumroad|buy|buys|buying|purchase|purchases)\b/i.test(text)) return { resource: 'purchase', operation: 'create' };
99
+ if (/\b(deploy|railway|production)\b/i.test(text)) return { resource: 'deployment', operation: 'write' };
100
+ if (/\b(post|reply|email|send|publish)\b/i.test(text)) return { resource: 'external-message', operation: 'send' };
101
+ return { resource: 'local', operation: 'read' };
102
+ }
103
+
104
+ function normalizeScope(scope = {}) {
105
+ if (typeof scope === 'string') {
106
+ const [resource, operation = 'use'] = scope.split(':');
107
+ return { resource: resource || 'local', operation };
108
+ }
109
+ return {
110
+ resource: String(scope.resource || 'local'),
111
+ operation: String(scope.operation || 'read'),
112
+ };
113
+ }
114
+
115
+ function scopeAllows(granted, required) {
116
+ if (granted.resource === '*') return true;
117
+ if (granted.resource !== required.resource) return false;
118
+ return granted.operation === '*' || granted.operation === required.operation;
119
+ }
120
+
121
+ function buildDeniedReasons(action, scope) {
122
+ const reasons = [];
123
+ if (action.persistent === true) reasons.push('persistent_credentials_not_allowed');
124
+ if (scope.resource === '*' || scope.operation === '*') reasons.push('credential_scope_too_broad');
125
+ return reasons;
126
+ }
127
+
128
+ function clamp(value, min, max) {
129
+ if (!Number.isFinite(value)) return min;
130
+ return Math.min(max, Math.max(min, value));
131
+ }
132
+
133
+ function formatCredentialPlan(plan = {}) {
134
+ return [
135
+ '# Single-Use Credential Plan',
136
+ '',
137
+ `Required: ${plan.required ? 'yes' : 'no'}`,
138
+ `Approval mode: ${plan.approvalMode}`,
139
+ `Scope: ${plan.scope?.resource}:${plan.scope?.operation}`,
140
+ `TTL seconds: ${plan.ttlSeconds}`,
141
+ `Denied reasons: ${(plan.deniedReasons || []).join(', ') || 'none'}`,
142
+ '',
143
+ plan.approvalPrompt || '',
144
+ '',
145
+ ].join('\n');
146
+ }
147
+
148
+ function parseArgs(argv = process.argv.slice(2)) {
149
+ const args = { command: argv[0] || 'plan', intent: '' };
150
+ for (const arg of argv.slice(1)) {
151
+ if (arg.startsWith('--intent=')) args.intent = arg.slice('--intent='.length);
152
+ if (arg.startsWith('--action=')) args.intent = arg.slice('--action='.length);
153
+ if (arg.startsWith('--description=')) args.description = arg.slice('--description='.length);
154
+ if (arg.startsWith('--scope=')) args.scope = arg.slice('--scope='.length);
155
+ }
156
+ return args;
157
+ }
158
+
159
+ function isCliInvocation(argv = process.argv) {
160
+ return Boolean(argv[1] && path.resolve(argv[1]) === __filename);
161
+ }
162
+
163
+ if (isCliInvocation()) {
164
+ const args = parseArgs();
165
+ const plan = planSingleUseCredentialRequest(args);
166
+ if (args.command === 'json') {
167
+ console.log(JSON.stringify(plan, null, 2));
168
+ } else if (args.command === 'plan') {
169
+ console.log(formatCredentialPlan(plan));
170
+ } else {
171
+ console.error(`Unknown command: ${args.command}. Use: plan, json`);
172
+ process.exit(1);
173
+ }
174
+ }
175
+
176
+ module.exports = {
177
+ evaluateCredentialUse,
178
+ formatCredentialPlan,
179
+ markCredentialUsed,
180
+ mintCredentialGrant,
181
+ planSingleUseCredentialRequest,
182
+ };
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Structured Prompt-Driven Development (SPDD) Gate
6
+ *
7
+ * Makes code-generation prompts governable artifacts by requiring a compact
8
+ * REASONS canvas before risky implementation work proceeds.
9
+ */
10
+
11
+ const path = require('node:path');
12
+
13
+ const FIELD_DEFINITIONS = [
14
+ ['requirements', 'Problem, business value, scope, and definition of done.'],
15
+ ['entities', 'Domain nouns, relationships, and data contracts.'],
16
+ ['approach', 'Strategy for satisfying the requirements.'],
17
+ ['structure', 'Files, modules, dependencies, and integration boundaries.'],
18
+ ['operations', 'Concrete, testable implementation steps.'],
19
+ ['norms', 'Reusable engineering standards and team conventions.'],
20
+ ['safeguards', 'Non-negotiable constraints, risks, and verification gates.'],
21
+ ];
22
+
23
+ const FIELD_KEYS = FIELD_DEFINITIONS.map(([key]) => key);
24
+
25
+ function buildReasonsCanvas(input = {}) {
26
+ const source = typeof input === 'string' ? { request: input } : input;
27
+ const request = String(source.request || source.story || source.task || '').trim();
28
+ const canvas = {};
29
+
30
+ for (const key of FIELD_KEYS) {
31
+ canvas[key] = normalizeList(source[key]);
32
+ }
33
+
34
+ if (request && canvas.requirements.length === 0) {
35
+ canvas.requirements.push(request);
36
+ }
37
+ if (source.acceptanceCriteria) {
38
+ canvas.requirements.push(...normalizeList(source.acceptanceCriteria));
39
+ }
40
+ if (source.files || source.changedFiles) {
41
+ canvas.structure.push(...normalizeList(source.files || source.changedFiles));
42
+ }
43
+ if (source.tests || source.verification) {
44
+ canvas.safeguards.push(...normalizeList(source.tests || source.verification).map((item) => `Verification: ${item}`));
45
+ }
46
+ if (canvas.norms.length === 0) {
47
+ canvas.norms.push('Keep prompt, code, and tests synchronized in version control.');
48
+ }
49
+ if (canvas.safeguards.length === 0) {
50
+ canvas.safeguards.push('Do not claim completion without passing verification evidence.');
51
+ }
52
+
53
+ return {
54
+ title: source.title || inferTitle(request),
55
+ canvas,
56
+ source: {
57
+ request,
58
+ artifactPath: source.artifactPath || 'docs/prompts/<feature>.reasons.md',
59
+ },
60
+ };
61
+ }
62
+
63
+ function evaluateReasonsCanvas(document = {}, options = {}) {
64
+ const canvas = document.canvas || document;
65
+ const gates = [];
66
+ const missing = FIELD_KEYS.filter((key) => normalizeList(canvas[key]).length === 0);
67
+
68
+ for (const key of missing) {
69
+ gates.push({
70
+ id: `missing-${key}`,
71
+ severity: key === 'requirements' || key === 'safeguards' ? 'block' : 'warn',
72
+ reason: `${labelFor(key)} is empty; the agent lacks a governed ${key} boundary.`,
73
+ });
74
+ }
75
+
76
+ const operations = normalizeList(canvas.operations);
77
+ if (operations.length > 0 && !operations.some(isTestableOperation)) {
78
+ gates.push({
79
+ id: 'operations-not-testable',
80
+ severity: 'block',
81
+ reason: 'At least one operation must be concrete and testable before code generation.',
82
+ });
83
+ }
84
+
85
+ const safeguards = normalizeList(canvas.safeguards).join('\n');
86
+ if (!/\b(tests?|verify|verification|evidence|gate|security|privacy|rollback|performance)\b/i.test(safeguards)) {
87
+ gates.push({
88
+ id: 'safeguards-without-verification',
89
+ severity: 'block',
90
+ reason: 'Safeguards must name verification, evidence, or non-negotiable risk controls.',
91
+ });
92
+ }
93
+
94
+ const changedFiles = normalizeList(options.changedFiles);
95
+ const structure = normalizeList(canvas.structure).join('\n');
96
+ if (changedFiles.length > 0 && !changedFiles.some((file) => structure.includes(file) || structure.includes(path.basename(file)))) {
97
+ gates.push({
98
+ id: 'code-prompt-drift',
99
+ severity: 'warn',
100
+ reason: 'Changed files are not represented in the prompt structure; sync the canvas before review.',
101
+ });
102
+ }
103
+
104
+ const hardBlocks = gates.filter((gate) => gate.severity === 'block');
105
+ const warnings = gates.filter((gate) => gate.severity === 'warn');
106
+ return {
107
+ allowed: hardBlocks.length === 0,
108
+ score: Math.max(0, 100 - (hardBlocks.length * 30) - (warnings.length * 10)),
109
+ gates,
110
+ missing,
111
+ recommendation: hardBlocks.length
112
+ ? 'Fix the structured prompt before generating or merging code.'
113
+ : warnings.length
114
+ ? 'Proceed only after syncing prompt drift and documenting review evidence.'
115
+ : 'Structured prompt is ready for code generation and review.',
116
+ };
117
+ }
118
+
119
+ function buildPromptSyncPlan(document = {}, changes = {}) {
120
+ const evaluation = evaluateReasonsCanvas(document, changes);
121
+ const changedFiles = normalizeList(changes.changedFiles);
122
+ const verification = normalizeList(changes.verification || changes.tests);
123
+ return {
124
+ promptFirst: evaluation.allowed,
125
+ artifactPath: document.source?.artifactPath || changes.artifactPath || 'docs/prompts/<feature>.reasons.md',
126
+ requiredUpdates: [
127
+ ...(evaluation.gates || []).map((gate) => gate.id),
128
+ ...(changedFiles.length ? ['sync-structure-with-changed-files'] : []),
129
+ ...(verification.length ? ['attach-verification-evidence'] : ['add-verification-evidence']),
130
+ ],
131
+ reviewChecklist: [
132
+ 'Review intent and scope before reviewing code diff.',
133
+ 'Confirm operations map to focused tests.',
134
+ 'Update the canvas when implementation reality diverges.',
135
+ 'Store prompt artifact beside the feature or PR evidence.',
136
+ ],
137
+ };
138
+ }
139
+
140
+ function formatReasonsCanvas(document = {}, evaluation = evaluateReasonsCanvas(document)) {
141
+ const canvas = document.canvas || document;
142
+ return [
143
+ `# ${document.title || 'Structured Prompt Canvas'}`,
144
+ '',
145
+ `Artifact: ${document.source?.artifactPath || 'docs/prompts/<feature>.reasons.md'}`,
146
+ `Readiness: ${evaluation.allowed ? 'ready' : 'blocked'} (${evaluation.score}/100)`,
147
+ '',
148
+ ...FIELD_DEFINITIONS.flatMap(([key, description]) => [
149
+ `## ${labelFor(key)}`,
150
+ '',
151
+ `_${description}_`,
152
+ '',
153
+ ...renderList(normalizeList(canvas[key])),
154
+ '',
155
+ ]),
156
+ '## Gates',
157
+ '',
158
+ ...(evaluation.gates.length ? evaluation.gates.map((gate) => `- ${gate.severity}: ${gate.id} — ${gate.reason}`) : ['- pass: canvas-ready — Structured prompt is complete enough to govern generation.']),
159
+ '',
160
+ `Recommendation: ${evaluation.recommendation}`,
161
+ '',
162
+ ].join('\n');
163
+ }
164
+
165
+ function normalizeList(value) {
166
+ if (!value) return [];
167
+ if (Array.isArray(value)) return value.map((item) => String(item).trim()).filter(Boolean);
168
+ return String(value)
169
+ .split(/\n|;/)
170
+ .map((item) => item.replace(/^[-*]\s*/, '').trim())
171
+ .filter(Boolean);
172
+ }
173
+
174
+ function renderList(items) {
175
+ return items.length ? items.map((item) => `- ${item}`) : ['- <missing>'];
176
+ }
177
+
178
+ function labelFor(key) {
179
+ return key.charAt(0).toUpperCase() + key.slice(1);
180
+ }
181
+
182
+ function isTestableOperation(operation) {
183
+ return /\b(add|update|remove|implement|verify|test|run|assert|block|allow|return|emit)\b/i.test(operation);
184
+ }
185
+
186
+ function inferTitle(request) {
187
+ if (!request) return 'Structured Prompt Canvas';
188
+ return request.length > 70 ? `${request.slice(0, 67)}...` : request;
189
+ }
190
+
191
+ function parseArgs(argv = process.argv.slice(2)) {
192
+ const args = { command: argv[0] || 'canvas', request: '' };
193
+ for (const arg of argv.slice(1)) {
194
+ if (arg.startsWith('--request=')) args.request = arg.slice('--request='.length);
195
+ if (arg.startsWith('--file=')) args.files = [...(args.files || []), arg.slice('--file='.length)];
196
+ if (arg.startsWith('--test=')) args.tests = [...(args.tests || []), arg.slice('--test='.length)];
197
+ if (arg.startsWith('--operation=')) args.operations = [...(args.operations || []), arg.slice('--operation='.length)];
198
+ if (arg.startsWith('--safeguard=')) args.safeguards = [...(args.safeguards || []), arg.slice('--safeguard='.length)];
199
+ }
200
+ return args;
201
+ }
202
+
203
+ function isCliInvocation(argv = process.argv) {
204
+ return Boolean(argv[1] && path.resolve(argv[1]) === __filename);
205
+ }
206
+
207
+ if (isCliInvocation()) {
208
+ const args = parseArgs();
209
+ const document = buildReasonsCanvas(args);
210
+ const evaluation = evaluateReasonsCanvas(document, { changedFiles: args.files });
211
+ if (args.command === 'json') {
212
+ console.log(JSON.stringify({ document, evaluation, syncPlan: buildPromptSyncPlan(document, { changedFiles: args.files, tests: args.tests }) }, null, 2));
213
+ } else if (args.command === 'canvas') {
214
+ console.log(formatReasonsCanvas(document, evaluation));
215
+ } else {
216
+ console.error(`Unknown command: ${args.command}. Use: canvas, json`);
217
+ process.exit(1);
218
+ }
219
+ }
220
+
221
+ module.exports = {
222
+ buildPromptSyncPlan,
223
+ buildReasonsCanvas,
224
+ evaluateReasonsCanvas,
225
+ formatReasonsCanvas,
226
+ };
@@ -19,6 +19,7 @@ const MARKETING_CLICK_EVENT_TYPES = new Set([
19
19
  'cta_click',
20
20
  'checkout_start',
21
21
  'checkout_bootstrap',
22
+ 'checkout_interstitial_cta_clicked',
22
23
  'chatgpt_gpt_open',
23
24
  'chatgpt_gpt_click',
24
25
  'install_guide_click',
@@ -324,6 +325,7 @@ function sanitizeTelemetryPayload(payload = {}, headers = {}) {
324
325
  maxScrollPercent: normalizeInteger(raw.maxScrollPercent ?? raw.scrollPercent),
325
326
  buyerEmailFocused: Boolean(raw.buyerEmailFocused),
326
327
  buyerEmailCaptured: Boolean(raw.buyerEmailCaptured),
328
+ checkoutIntentClassification: pickFirstText(raw.checkoutIntentClassification),
327
329
  trafficChannel: inferTrafficChannel(raw, referrerHost),
328
330
  failureCode: pickFirstText(raw.failureCode),
329
331
  httpStatus: normalizeInteger(raw.httpStatus),
@@ -344,9 +346,31 @@ function appendTelemetryEvent(feedbackDir, payload = {}, headers = {}) {
344
346
  return entry;
345
347
  }
346
348
 
347
- function loadTelemetryEventsFromPath(filePath) {
348
- if (!fs.existsSync(filePath)) return [];
349
- const raw = fs.readFileSync(filePath, 'utf-8').trim();
349
+ const DEFAULT_BOUNDED_TELEMETRY_TAIL_BYTES = 8 * 1024 * 1024;
350
+
351
+ function readTelemetryText(filePath, options = {}) {
352
+ if (!fs.existsSync(filePath)) return '';
353
+ const maxBytes = Number(options.maxBytes || 0);
354
+ if (maxBytes > 0) {
355
+ const stats = fs.statSync(filePath);
356
+ if (stats.size > maxBytes) {
357
+ const fd = fs.openSync(filePath, 'r');
358
+ try {
359
+ const buffer = Buffer.alloc(maxBytes);
360
+ fs.readSync(fd, buffer, 0, maxBytes, stats.size - maxBytes);
361
+ const text = buffer.toString('utf-8');
362
+ const firstNewline = text.indexOf('\n');
363
+ return firstNewline >= 0 ? text.slice(firstNewline + 1) : text;
364
+ } finally {
365
+ fs.closeSync(fd);
366
+ }
367
+ }
368
+ }
369
+ return fs.readFileSync(filePath, 'utf-8');
370
+ }
371
+
372
+ function loadTelemetryEventsFromPath(filePath, options = {}) {
373
+ const raw = readTelemetryText(filePath, options).trim();
350
374
  if (!raw) return [];
351
375
  return raw
352
376
  .split('\n')
@@ -365,13 +389,13 @@ function loadTelemetryEventsFromPath(filePath) {
365
389
  .filter(Boolean);
366
390
  }
367
391
 
368
- function loadTelemetryEvents(feedbackDir) {
392
+ function loadTelemetryEvents(feedbackDir, options = {}) {
369
393
  const diagnostics = getTelemetrySourceDiagnostics(feedbackDir);
370
394
  const merged = [];
371
395
  const seen = new Set();
372
396
 
373
397
  for (const filePath of diagnostics.activePaths) {
374
- const rows = loadTelemetryEventsFromPath(filePath);
398
+ const rows = loadTelemetryEventsFromPath(filePath, options);
375
399
  for (const row of rows) {
376
400
  const key = JSON.stringify(row);
377
401
  if (seen.has(key)) continue;
@@ -406,8 +430,11 @@ function summarizeRecentEvents(events) {
406
430
 
407
431
  function getTelemetrySummary(feedbackDir, options = {}) {
408
432
  const analyticsWindow = resolveAnalyticsWindow(options);
433
+ const telemetryLoadOptions = analyticsWindow.bounded
434
+ ? { maxBytes: Number(options.telemetryTailBytes || DEFAULT_BOUNDED_TELEMETRY_TAIL_BYTES) }
435
+ : {};
409
436
  const events = filterEntriesForWindow(
410
- loadTelemetryEvents(feedbackDir),
437
+ loadTelemetryEvents(feedbackDir, telemetryLoadOptions),
411
438
  analyticsWindow,
412
439
  (entry) => entry && (entry.receivedAt || entry.timestamp)
413
440
  );
@@ -465,6 +492,14 @@ function getTelemetrySummary(feedbackDir, options = {}) {
465
492
  let ctaClicks = 0;
466
493
  let ctaImpressions = 0;
467
494
  let checkoutStarts = 0;
495
+ let checkoutInterstitialViews = 0;
496
+ let checkoutBotDeflections = 0;
497
+ let checkoutInterstitialClicks = 0;
498
+ let checkoutInterstitialProConfirms = 0;
499
+ let checkoutInterstitialWorkflowIntakeClicks = 0;
500
+ let checkoutInterstitialTeamPathClicks = 0;
501
+ let checkoutInterstitialDiagnosticCheckoutClicks = 0;
502
+ let checkoutInterstitialWorkflowSprintCheckoutClicks = 0;
468
503
  let checkoutFailures = 0;
469
504
  let checkoutCancelled = 0;
470
505
  let checkoutAbandoned = 0;
@@ -536,6 +571,29 @@ function getTelemetrySummary(feedbackDir, options = {}) {
536
571
  }
537
572
  }
538
573
 
574
+ if ((entry.eventType || entry.event) === 'checkout_interstitial_view') {
575
+ checkoutInterstitialViews += 1;
576
+ }
577
+
578
+ if ((entry.eventType || entry.event) === 'checkout_bot_deflected') {
579
+ checkoutBotDeflections += 1;
580
+ }
581
+
582
+ if ((entry.eventType || entry.event) === 'checkout_interstitial_cta_clicked') {
583
+ checkoutInterstitialClicks += 1;
584
+ if (entry.ctaId === 'pro_checkout_confirmed') {
585
+ checkoutInterstitialProConfirms += 1;
586
+ } else if (entry.ctaId === 'workflow_sprint_intake') {
587
+ checkoutInterstitialWorkflowIntakeClicks += 1;
588
+ } else if (entry.ctaId === 'team_paid_path') {
589
+ checkoutInterstitialTeamPathClicks += 1;
590
+ } else if (entry.ctaId === 'sprint_diagnostic_checkout') {
591
+ checkoutInterstitialDiagnosticCheckoutClicks += 1;
592
+ } else if (entry.ctaId === 'workflow_sprint_checkout') {
593
+ checkoutInterstitialWorkflowSprintCheckoutClicks += 1;
594
+ }
595
+ }
596
+
539
597
  if ((entry.eventType || entry.event) === 'checkout_start' || (entry.eventType || entry.event) === 'checkout_bootstrap') {
540
598
  checkoutStarts += 1;
541
599
  incrementCounter(checkoutStartsBySource, entry.source);
@@ -712,11 +770,15 @@ function getTelemetrySummary(feedbackDir, options = {}) {
712
770
  installCopies,
713
771
  gptOpens,
714
772
  checkoutStarts,
773
+ checkoutInterstitialViews,
774
+ checkoutInterstitialClicks,
715
775
  trialEmails,
716
776
  proConversions,
717
777
  landingToInstallCopyRate: safeRate(installCopies, pageViews),
718
778
  landingToGptOpenRate: safeRate(gptOpens, pageViews),
719
779
  landingToCheckoutRate: safeRate(checkoutStarts, pageViews),
780
+ checkoutInterstitialClickRate: safeRate(checkoutInterstitialClicks, checkoutInterstitialViews),
781
+ checkoutInterstitialProConfirmRate: safeRate(checkoutInterstitialProConfirms, checkoutInterstitialViews),
720
782
  checkoutToTrialEmailRate: safeRate(trialEmails, checkoutStarts),
721
783
  checkoutToProConversionRate: safeRate(proConversions, checkoutStarts),
722
784
  },
@@ -728,6 +790,14 @@ function getTelemetrySummary(feedbackDir, options = {}) {
728
790
  pageViews,
729
791
  ctaClicks,
730
792
  checkoutStarts,
793
+ checkoutInterstitialViews,
794
+ checkoutBotDeflections,
795
+ checkoutInterstitialClicks,
796
+ checkoutInterstitialProConfirms,
797
+ checkoutInterstitialWorkflowIntakeClicks,
798
+ checkoutInterstitialTeamPathClicks,
799
+ checkoutInterstitialDiagnosticCheckoutClicks,
800
+ checkoutInterstitialWorkflowSprintCheckoutClicks,
731
801
  checkoutFailures,
732
802
  checkoutCancelled,
733
803
  checkoutAbandoned,
@@ -890,6 +960,14 @@ function getTelemetryAnalytics(feedbackDir, options = {}) {
890
960
  ctas: {
891
961
  totalClicks: summary.web.ctaClicks,
892
962
  checkoutStarts: summary.web.checkoutStarts,
963
+ checkoutInterstitialViews: summary.web.checkoutInterstitialViews,
964
+ checkoutBotDeflections: summary.web.checkoutBotDeflections,
965
+ checkoutInterstitialClicks: summary.web.checkoutInterstitialClicks,
966
+ checkoutInterstitialProConfirms: summary.web.checkoutInterstitialProConfirms,
967
+ checkoutInterstitialWorkflowIntakeClicks: summary.web.checkoutInterstitialWorkflowIntakeClicks,
968
+ checkoutInterstitialTeamPathClicks: summary.web.checkoutInterstitialTeamPathClicks,
969
+ checkoutInterstitialDiagnosticCheckoutClicks: summary.web.checkoutInterstitialDiagnosticCheckoutClicks,
970
+ checkoutInterstitialWorkflowSprintCheckoutClicks: summary.web.checkoutInterstitialWorkflowSprintCheckoutClicks,
893
971
  uniqueCheckoutStarters: summary.web.uniqueCheckoutStarters,
894
972
  checkoutFailures: summary.web.checkoutFailures,
895
973
  checkoutCancelled: summary.web.checkoutCancelled,
@@ -925,6 +1003,30 @@ function getTelemetryAnalytics(feedbackDir, options = {}) {
925
1003
  pageViewToCheckoutRate: summary.web.pageViewToCheckoutRate,
926
1004
  visitorToCheckoutRate: summary.web.visitorToCheckoutRate,
927
1005
  clickToCheckoutRate: safeRate(summary.web.checkoutStarts, summary.web.ctaClicks),
1006
+ checkoutInterstitialClickRate: safeRate(
1007
+ summary.web.checkoutInterstitialClicks,
1008
+ summary.web.checkoutInterstitialViews
1009
+ ),
1010
+ checkoutInterstitialProConfirmRate: safeRate(
1011
+ summary.web.checkoutInterstitialProConfirms,
1012
+ summary.web.checkoutInterstitialViews
1013
+ ),
1014
+ checkoutInterstitialWorkflowIntakeRate: safeRate(
1015
+ summary.web.checkoutInterstitialWorkflowIntakeClicks,
1016
+ summary.web.checkoutInterstitialViews
1017
+ ),
1018
+ checkoutInterstitialTeamPathRate: safeRate(
1019
+ summary.web.checkoutInterstitialTeamPathClicks,
1020
+ summary.web.checkoutInterstitialViews
1021
+ ),
1022
+ checkoutInterstitialDiagnosticCheckoutRate: safeRate(
1023
+ summary.web.checkoutInterstitialDiagnosticCheckoutClicks,
1024
+ summary.web.checkoutInterstitialViews
1025
+ ),
1026
+ checkoutInterstitialWorkflowSprintCheckoutRate: safeRate(
1027
+ summary.web.checkoutInterstitialWorkflowSprintCheckoutClicks,
1028
+ summary.web.checkoutInterstitialViews
1029
+ ),
928
1030
  cancellationRate: safeRate(summary.web.checkoutCancelled, summary.web.checkoutStarts),
929
1031
  abandonmentRate: safeRate(summary.web.checkoutAbandoned, summary.web.checkoutStarts),
930
1032
  paidConfirmationRate: safeRate(summary.web.checkoutPaidConfirmations, summary.web.checkoutStarts),