thumbgate 1.15.0 → 1.16.0

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 (129) hide show
  1. package/.claude-plugin/marketplace.json +6 -6
  2. package/.claude-plugin/plugin.json +3 -3
  3. package/.well-known/llms.txt +5 -5
  4. package/.well-known/mcp/server-card.json +1 -1
  5. package/README.md +59 -35
  6. package/adapters/chatgpt/openapi.yaml +118 -2
  7. package/adapters/claude/.mcp.json +2 -2
  8. package/adapters/mcp/server-stdio.js +210 -84
  9. package/adapters/opencode/opencode.json +1 -1
  10. package/bench/prompt-eval-suite.json +5 -1
  11. package/bin/cli.js +157 -8
  12. package/config/evals/agent-safety-eval.json +338 -22
  13. package/config/gates/routine.json +43 -0
  14. package/config/github-about.json +3 -3
  15. package/config/model-candidates.json +131 -0
  16. package/openapi/openapi.yaml +118 -2
  17. package/package.json +55 -48
  18. package/public/blog.html +7 -7
  19. package/public/codex-plugin.html +6 -6
  20. package/public/compare.html +29 -23
  21. package/public/dashboard.html +82 -10
  22. package/public/guide.html +28 -28
  23. package/public/index.html +216 -98
  24. package/public/learn.html +50 -22
  25. package/public/lessons.html +1 -1
  26. package/public/numbers.html +17 -17
  27. package/public/pro.html +82 -18
  28. package/scripts/agent-audit-trace.js +55 -0
  29. package/scripts/agent-memory-lifecycle.js +96 -0
  30. package/scripts/agent-readiness-plan.js +118 -0
  31. package/scripts/agentic-data-pipeline.js +21 -1
  32. package/scripts/agents-sdk-sandbox-plan.js +57 -0
  33. package/scripts/ai-org-governance.js +98 -0
  34. package/scripts/ai-search-distribution.js +43 -0
  35. package/scripts/artifact-agent-plan.js +81 -0
  36. package/scripts/billing.js +27 -8
  37. package/scripts/cli-schema.js +18 -2
  38. package/scripts/code-mode-mcp-plan.js +71 -0
  39. package/scripts/context-engine.js +1 -2
  40. package/scripts/context-manager.js +4 -1
  41. package/scripts/dashboard-render-spec.js +1 -1
  42. package/scripts/dashboard.js +275 -9
  43. package/scripts/decision-journal.js +13 -3
  44. package/scripts/document-workflow-governance.js +62 -0
  45. package/scripts/enterprise-agent-rollout.js +34 -0
  46. package/scripts/experience-replay-governance.js +69 -0
  47. package/scripts/export-hf-dataset.js +1 -1
  48. package/scripts/feedback-loop.js +92 -4
  49. package/scripts/feedback-to-rules.js +17 -23
  50. package/scripts/gates-engine.js +4 -6
  51. package/scripts/growth-campaigns.js +49 -0
  52. package/scripts/harness-selector.js +16 -4
  53. package/scripts/hybrid-supervisor-agent.js +64 -0
  54. package/scripts/inference-cache-policy.js +72 -0
  55. package/scripts/inference-economics.js +53 -0
  56. package/scripts/internal-agent-bootstrap.js +12 -2
  57. package/scripts/knowledge-layer-plan.js +108 -0
  58. package/scripts/lesson-inference.js +183 -44
  59. package/scripts/lesson-search.js +4 -1
  60. package/scripts/llm-client.js +157 -26
  61. package/scripts/mailer/resend-mailer.js +112 -1
  62. package/scripts/mcp-transport-strategy.js +66 -0
  63. package/scripts/memory-store-governance.js +60 -0
  64. package/scripts/meta-agent-loop.js +7 -13
  65. package/scripts/model-access-eligibility.js +38 -0
  66. package/scripts/model-migration-readiness.js +55 -0
  67. package/scripts/operational-integrity.js +96 -3
  68. package/scripts/otel-declarative-config.js +56 -0
  69. package/scripts/perplexity-client.js +1 -1
  70. package/scripts/post-training-governance.js +34 -0
  71. package/scripts/private-core-boundary.js +72 -0
  72. package/scripts/production-agent-readiness.js +40 -0
  73. package/scripts/prompt-eval.js +564 -32
  74. package/scripts/prompt-programs.js +93 -0
  75. package/scripts/provider-action-normalizer.js +585 -0
  76. package/scripts/scaling-law-claims.js +60 -0
  77. package/scripts/security-scanner.js +1 -1
  78. package/scripts/self-distill-agent.js +7 -32
  79. package/scripts/seo-gsd.js +232 -55
  80. package/scripts/skill-rag-router.js +53 -0
  81. package/scripts/spec-gate.js +1 -1
  82. package/scripts/student-consistent-training.js +73 -0
  83. package/scripts/synthetic-data-provenance.js +98 -0
  84. package/scripts/task-context-result.js +81 -0
  85. package/scripts/telemetry-analytics.js +149 -0
  86. package/scripts/thompson-sampling.js +2 -2
  87. package/scripts/token-savings.js +7 -6
  88. package/scripts/token-tco.js +46 -0
  89. package/scripts/tool-registry.js +63 -3
  90. package/scripts/verification-loop.js +10 -1
  91. package/scripts/verifier-scoring.js +71 -0
  92. package/scripts/workflow-sentinel.js +284 -28
  93. package/scripts/workspace-agent-routines.js +118 -0
  94. package/src/api/server.js +381 -120
  95. package/scripts/analytics-report.js +0 -328
  96. package/scripts/autonomous-workflow.js +0 -377
  97. package/scripts/billing-setup.js +0 -109
  98. package/scripts/creator-campaigns.js +0 -239
  99. package/scripts/cross-encoder-reranker.js +0 -235
  100. package/scripts/daemon-manager.js +0 -108
  101. package/scripts/decision-trace.js +0 -354
  102. package/scripts/delegation-runtime.js +0 -896
  103. package/scripts/dispatch-brief.js +0 -159
  104. package/scripts/distribution-surfaces.js +0 -110
  105. package/scripts/feedback-history-distiller.js +0 -382
  106. package/scripts/funnel-analytics.js +0 -35
  107. package/scripts/history-distiller.js +0 -200
  108. package/scripts/hosted-job-launcher.js +0 -256
  109. package/scripts/intent-router.js +0 -392
  110. package/scripts/lesson-reranker.js +0 -263
  111. package/scripts/lesson-retrieval.js +0 -148
  112. package/scripts/managed-lesson-agent.js +0 -183
  113. package/scripts/operational-dashboard.js +0 -103
  114. package/scripts/operational-summary.js +0 -129
  115. package/scripts/operator-artifacts.js +0 -608
  116. package/scripts/optimize-context.js +0 -17
  117. package/scripts/org-dashboard.js +0 -206
  118. package/scripts/partner-orchestration.js +0 -146
  119. package/scripts/predictive-insights.js +0 -356
  120. package/scripts/pulse.js +0 -80
  121. package/scripts/reflector-agent.js +0 -221
  122. package/scripts/sales-pipeline.js +0 -681
  123. package/scripts/session-episode-store.js +0 -329
  124. package/scripts/session-health-sensor.js +0 -242
  125. package/scripts/session-report.js +0 -120
  126. package/scripts/swarm-coordinator.js +0 -81
  127. package/scripts/tool-kpi-tracker.js +0 -12
  128. package/scripts/webhook-delivery.js +0 -62
  129. package/scripts/workflow-sprint-intake.js +0 -475
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const MEMORY_TYPES = new Set(['episodic', 'semantic', 'procedural', 'preference', 'working']);
5
+
6
+ function normalizeText(value) {
7
+ if (value === undefined || value === null) return '';
8
+ return String(value).trim();
9
+ }
10
+
11
+ function normalizeMemoryType(value) {
12
+ const normalized = normalizeText(value).toLowerCase();
13
+ return MEMORY_TYPES.has(normalized) ? normalized : 'episodic';
14
+ }
15
+
16
+ function buildMemoryLifecyclePolicy(input = {}) {
17
+ return {
18
+ generatedAt: normalizeText(input.generatedAt) || new Date().toISOString(),
19
+ memoryTypes: [
20
+ {
21
+ type: 'working',
22
+ purpose: 'Short-lived task context.',
23
+ retention: 'session',
24
+ promotionGate: 'discard unless referenced by outcome evidence',
25
+ },
26
+ {
27
+ type: 'episodic',
28
+ purpose: 'Specific agent actions, feedback, and outcomes.',
29
+ retention: 'bounded_history',
30
+ promotionGate: 'requires actionable context and source trace',
31
+ },
32
+ {
33
+ type: 'semantic',
34
+ purpose: 'Consolidated facts, standards, and reusable lessons.',
35
+ retention: 'durable',
36
+ promotionGate: 'requires deduplication and contradiction check',
37
+ },
38
+ {
39
+ type: 'procedural',
40
+ purpose: 'Reusable workflows, prompt programs, and gates.',
41
+ retention: 'durable',
42
+ promotionGate: 'requires test or replay evidence',
43
+ },
44
+ {
45
+ type: 'preference',
46
+ purpose: 'Operator style and decision preferences.',
47
+ retention: 'durable_redactable',
48
+ promotionGate: 'requires explicit user signal',
49
+ },
50
+ ],
51
+ retrieval: {
52
+ defaultTopK: 8,
53
+ recencyWeight: 0.25,
54
+ semanticWeight: 0.5,
55
+ outcomeWeight: 0.25,
56
+ requireSourceAnchors: true,
57
+ },
58
+ privacy: {
59
+ piiScanRequired: true,
60
+ secretScanRequired: true,
61
+ exportRequiresRedaction: true,
62
+ },
63
+ };
64
+ }
65
+
66
+ function evaluateMemoryPromotion(memory = {}, policy = buildMemoryLifecyclePolicy()) {
67
+ const type = normalizeMemoryType(memory.type);
68
+ const content = normalizeText(memory.content);
69
+ const source = normalizeText(memory.source);
70
+ const outcome = normalizeText(memory.outcome);
71
+ const issues = [];
72
+
73
+ if (!content) issues.push('missing_content');
74
+ if (!source) issues.push('missing_source_anchor');
75
+ if (type !== 'preference' && !outcome) issues.push('missing_outcome');
76
+ if (/api[_-]?key|secret|password|token|bearer\s+[a-z0-9._-]+/i.test(content)) {
77
+ issues.push('secret_like_content');
78
+ }
79
+ if (type === 'preference' && memory.explicitUserSignal !== true) {
80
+ issues.push('preference_without_explicit_signal');
81
+ }
82
+
83
+ return {
84
+ type,
85
+ decision: issues.length === 0 ? 'promote' : 'hold',
86
+ issues,
87
+ retrievalEligible: issues.length === 0 || !issues.includes('secret_like_content'),
88
+ policyVersion: policy.generatedAt,
89
+ };
90
+ }
91
+
92
+ module.exports = {
93
+ buildMemoryLifecyclePolicy,
94
+ evaluateMemoryPromotion,
95
+ normalizeMemoryType,
96
+ };
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_CHECKS = [
4
+ {
5
+ id: 'robots_ai_rules',
6
+ category: 'discoverability',
7
+ artifact: 'public/robots.txt',
8
+ requirement: 'Declare sitemap and AI bot access policy.',
9
+ },
10
+ {
11
+ id: 'sitemap',
12
+ category: 'discoverability',
13
+ artifact: 'public/sitemap.xml',
14
+ requirement: 'Expose canonical public pages for crawlers and AI agents.',
15
+ },
16
+ {
17
+ id: 'markdown_negotiation',
18
+ category: 'content_accessibility',
19
+ artifact: 'public/llm-context.md',
20
+ requirement: 'Offer dense Markdown context for agents that prefer text-first retrieval.',
21
+ },
22
+ {
23
+ id: 'mcp_server_card',
24
+ category: 'protocol_discovery',
25
+ artifact: 'public/.well-known/mcp-server.json',
26
+ requirement: 'Publish MCP discovery metadata for agent tools.',
27
+ },
28
+ {
29
+ id: 'agent_skills',
30
+ category: 'protocol_discovery',
31
+ artifact: 'public/.well-known/agent-skills.json',
32
+ requirement: 'Describe callable skills and evidence requirements.',
33
+ },
34
+ {
35
+ id: 'oauth_protected_resource',
36
+ category: 'api_auth',
37
+ artifact: 'public/.well-known/oauth-protected-resource',
38
+ requirement: 'Advertise protected API resource metadata when authenticated tools exist.',
39
+ },
40
+ {
41
+ id: 'agentic_commerce',
42
+ category: 'commerce',
43
+ artifact: 'public/.well-known/agentic-commerce.json',
44
+ requirement: 'Expose paid plan, checkout, refund, and support metadata for commerce agents.',
45
+ },
46
+ ];
47
+
48
+ function normalizeExisting(existing = []) {
49
+ if (Array.isArray(existing)) return new Set(existing);
50
+ return new Set(Object.entries(existing).filter(([, present]) => present).map(([id]) => id));
51
+ }
52
+
53
+ function buildAgentReadinessPlan(options = {}) {
54
+ const baseUrl = options.baseUrl || 'https://thumbgate-production.up.railway.app';
55
+ const existing = normalizeExisting(options.existing);
56
+ const checks = (options.checks || DEFAULT_CHECKS).map((check) => {
57
+ const present = existing.has(check.id) || existing.has(check.artifact);
58
+ return {
59
+ ...check,
60
+ status: present ? 'present' : 'missing',
61
+ url: check.artifact.startsWith('public/')
62
+ ? `${baseUrl}/${check.artifact.replace(/^public\//, '')}`
63
+ : `${baseUrl}/${check.artifact}`,
64
+ };
65
+ });
66
+ const missing = checks.filter((check) => check.status === 'missing');
67
+
68
+ return {
69
+ baseUrl,
70
+ score: Math.round(((checks.length - missing.length) / checks.length) * 100),
71
+ checks,
72
+ quickWins: missing
73
+ .filter((check) => ['discoverability', 'content_accessibility', 'protocol_discovery'].includes(check.category))
74
+ .slice(0, 5)
75
+ .map((check) => ({
76
+ id: check.id,
77
+ action: `publish ${check.artifact}`,
78
+ reason: check.requirement,
79
+ })),
80
+ promotionAngles: [
81
+ 'agent-ready pre-action gates',
82
+ 'MCP-discoverable reliability gateway',
83
+ 'machine-readable evidence before agent actions',
84
+ 'commerce metadata for paid operator lanes',
85
+ ],
86
+ };
87
+ }
88
+
89
+ function evaluateAgentReadinessPlan(plan) {
90
+ const issues = [];
91
+ const required = [
92
+ 'robots_ai_rules',
93
+ 'sitemap',
94
+ 'markdown_negotiation',
95
+ 'mcp_server_card',
96
+ ];
97
+ const byId = new Map((plan.checks || []).map((check) => [check.id, check]));
98
+
99
+ for (const id of required) {
100
+ if (byId.get(id)?.status !== 'present') issues.push(`missing_${id}`);
101
+ }
102
+
103
+ if (!plan.baseUrl?.startsWith('https://')) {
104
+ issues.push('https_base_url_required');
105
+ }
106
+
107
+ return {
108
+ decision: issues.length ? 'warn' : 'allow',
109
+ issues,
110
+ score: plan.score || 0,
111
+ };
112
+ }
113
+
114
+ module.exports = {
115
+ DEFAULT_CHECKS,
116
+ buildAgentReadinessPlan,
117
+ evaluateAgentReadinessPlan,
118
+ };
@@ -17,7 +17,27 @@ const {
17
17
  serializeAnalyticsWindow,
18
18
  } = require('./analytics-window');
19
19
  const { appendWorkflowRun } = require('./workflow-runs');
20
- const { buildPredictiveInsights } = require('./predictive-insights');
20
+ const {
21
+ createUnavailableReport,
22
+ loadOptionalModule,
23
+ } = require('./private-core-boundary');
24
+ const { buildPredictiveInsights } = loadOptionalModule('./predictive-insights', () => ({
25
+ buildPredictiveInsights: () => ({
26
+ opportunitySummary: [],
27
+ anomalySummary: { count: 0, severity: 'none' },
28
+ topCreators: [],
29
+ topSources: [],
30
+ upgradePropensity: {
31
+ pro: { band: 'unavailable', score: 0 },
32
+ team: { band: 'unavailable', score: 0 },
33
+ },
34
+ revenueForecast: {
35
+ predictedBookedRevenueCents: 0,
36
+ incrementalOpportunityCents: 0,
37
+ },
38
+ ...createUnavailableReport('Predictive insights'),
39
+ }),
40
+ }));
21
41
  const { ensureDir } = require('./fs-utils');
22
42
 
23
43
  const PIPELINE_DIRNAME = 'agentic-data-pipeline';
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ function buildAgentsSdkSandboxPlan(options = {}) {
4
+ const provider = options.provider || 'unix_local';
5
+ const mounts = options.mounts || [{ name: 'data', mode: 'read_only' }];
6
+ const outputDir = options.outputDir || 'outputs';
7
+
8
+ return {
9
+ harness: 'openai_agents_sdk_sandbox',
10
+ provider,
11
+ manifest: {
12
+ mounts,
13
+ outputDir,
14
+ network: options.network || 'disabled_by_default',
15
+ allowedCommands: options.allowedCommands || ['npm test', 'npm run test:coverage'],
16
+ },
17
+ separation: {
18
+ credentialsInSandbox: false,
19
+ toolBrokerOwnsSecrets: true,
20
+ sandboxGetsScopedFilesOnly: true,
21
+ },
22
+ durability: {
23
+ externalState: true,
24
+ checkpoints: ['manifest_loaded', 'tools_completed', 'patch_written', 'tests_run'],
25
+ rehydrateOnSandboxLoss: true,
26
+ },
27
+ gates: [
28
+ 'read manifest before file access',
29
+ 'write only under declared output or scoped repo path',
30
+ 'cite source filenames for data-room answers',
31
+ 'run configured verification before completion claim',
32
+ 'persist decision journal outside sandbox',
33
+ ],
34
+ };
35
+ }
36
+
37
+ function evaluateSandboxPlan(plan = {}) {
38
+ const issues = [];
39
+ if (!plan.manifest?.mounts?.length) issues.push('missing_manifest_mounts');
40
+ if (!plan.manifest?.outputDir) issues.push('missing_output_dir');
41
+ if (plan.separation?.credentialsInSandbox !== false) issues.push('credentials_must_stay_outside_sandbox');
42
+ if (!plan.durability?.externalState) issues.push('external_state_required');
43
+ if (!plan.durability?.rehydrateOnSandboxLoss) issues.push('rehydration_required');
44
+ if (!Array.isArray(plan.gates) || !plan.gates.some((gate) => /verification/i.test(gate))) {
45
+ issues.push('verification_gate_required');
46
+ }
47
+
48
+ return {
49
+ decision: issues.length ? 'warn' : 'allow',
50
+ issues,
51
+ };
52
+ }
53
+
54
+ module.exports = {
55
+ buildAgentsSdkSandboxPlan,
56
+ evaluateSandboxPlan,
57
+ };
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ function normalizeText(value) {
5
+ if (value === undefined || value === null) return '';
6
+ return String(value).trim();
7
+ }
8
+
9
+ function normalizeBudget(value, fallback) {
10
+ const number = Number(value);
11
+ return Number.isFinite(number) && number >= 0 ? number : fallback;
12
+ }
13
+
14
+ function buildAiOrgGovernancePlan(input = {}) {
15
+ const mission = normalizeText(input.mission) || 'Continuously improve agent-governed workflows while staying within budget.';
16
+ const monthlyBudgetUsd = normalizeBudget(input.monthlyBudgetUsd, 25);
17
+ const roles = [
18
+ {
19
+ id: 'ceo',
20
+ title: 'Planner',
21
+ mission: 'Break goals into tickets, assign owners, and enforce ROI.',
22
+ monthlyBudgetUsd: normalizeBudget(input.ceoBudgetUsd, Math.min(10, monthlyBudgetUsd)),
23
+ canCreateAgents: false,
24
+ },
25
+ {
26
+ id: 'research_analyst',
27
+ title: 'Research Analyst',
28
+ mission: 'Collect market and technical signals into structured briefs.',
29
+ monthlyBudgetUsd: normalizeBudget(input.researchBudgetUsd, Math.min(5, monthlyBudgetUsd)),
30
+ canCreateAgents: false,
31
+ },
32
+ {
33
+ id: 'qa_operator',
34
+ title: 'QA Operator',
35
+ mission: 'Review evidence, tests, diffs, and spend anomalies before promotion.',
36
+ monthlyBudgetUsd: normalizeBudget(input.qaBudgetUsd, Math.min(5, monthlyBudgetUsd)),
37
+ canCreateAgents: false,
38
+ },
39
+ ];
40
+
41
+ return {
42
+ generatedAt: normalizeText(input.generatedAt) || new Date().toISOString(),
43
+ orgName: normalizeText(input.orgName) || 'ThumbGate Agent Company',
44
+ mission,
45
+ monthlyBudgetUsd,
46
+ roles,
47
+ ticketTemplates: [
48
+ {
49
+ id: 'market_signal_brief',
50
+ ownerRole: 'research_analyst',
51
+ outputSchema: ['source', 'claim', 'relevance', 'action', 'evidence'],
52
+ },
53
+ {
54
+ id: 'workflow_hardening',
55
+ ownerRole: 'ceo',
56
+ outputSchema: ['risk', 'gate', 'test', 'rollback', 'owner'],
57
+ },
58
+ {
59
+ id: 'evidence_review',
60
+ ownerRole: 'qa_operator',
61
+ outputSchema: ['claim', 'evidence', 'verdict', 'missing_proof'],
62
+ },
63
+ ],
64
+ approvalGates: [
65
+ 'new_agent_role',
66
+ 'budget_increase',
67
+ 'credentialed_connector_write',
68
+ 'production_release',
69
+ 'public_claim_without_evidence',
70
+ ],
71
+ audit: {
72
+ daily: ['ticket outcomes', 'spend by role', 'blocked actions', 'open approvals'],
73
+ weekly: ['low ROI tickets', 'stale agents', 'budget cap changes', 'policy drift'],
74
+ },
75
+ };
76
+ }
77
+
78
+ function evaluateAiOrgAction(action = {}, plan = buildAiOrgGovernancePlan()) {
79
+ const type = normalizeText(action.type);
80
+ const issues = [];
81
+ if (type === 'create_agent') issues.push('new_agent_role');
82
+ if (type === 'raise_budget') issues.push('budget_increase');
83
+ if (type === 'connector_write') issues.push('credentialed_connector_write');
84
+ if (type === 'public_claim' && !(Array.isArray(action.evidence) && action.evidence.length > 0)) {
85
+ issues.push('public_claim_without_evidence');
86
+ }
87
+ const gateHits = issues.filter((issue) => plan.approvalGates.includes(issue));
88
+ return {
89
+ decision: gateHits.length > 0 ? 'warn' : 'allow',
90
+ gateHits,
91
+ requiredApproval: gateHits.length > 0,
92
+ };
93
+ }
94
+
95
+ module.exports = {
96
+ buildAiOrgGovernancePlan,
97
+ evaluateAiOrgAction,
98
+ };
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ function buildAiSearchDistributionPlan(input = {}) {
5
+ const brand = input.brand || 'ThumbGate';
6
+ const canonicalUrl = input.canonicalUrl || 'https://thumbgate-production.up.railway.app';
7
+ const proofUrl = input.proofUrl || `${canonicalUrl}/VERIFICATION_EVIDENCE.md`;
8
+ const claims = [
9
+ `${brand} is a pre-action gate system for AI agents.`,
10
+ `${brand} turns thumbs-up/down feedback into enforceable prevention rules.`,
11
+ `${brand} blocks known-bad tool actions before execution when wired into the agent runtime.`,
12
+ `${brand} provides decision journals, evidence gates, and workflow hardening for agentic teams.`,
13
+ ];
14
+ return {
15
+ brand,
16
+ canonicalUrl,
17
+ proofUrl,
18
+ entityClaims: claims,
19
+ fragments: claims.map((claim, index) => ({
20
+ id: `thumbgate_entity_fragment_${index + 1}`,
21
+ text: claim,
22
+ schemaHint: 'SoftwareApplication',
23
+ proofUrl,
24
+ })),
25
+ distributionSurfaces: [
26
+ 'public/llm-context.md',
27
+ 'README.md',
28
+ 'GitHub About',
29
+ 'npm package description',
30
+ 'LinkedIn post',
31
+ 'newsletter/webinar page',
32
+ 'comparison pages',
33
+ ],
34
+ measurement: {
35
+ primary: ['AI citations', 'branded search mentions', 'LLM recommendation presence'],
36
+ secondary: ['referral clicks', 'checkout starts', 'workflow sprint leads'],
37
+ },
38
+ };
39
+ }
40
+
41
+ module.exports = {
42
+ buildAiSearchDistributionPlan,
43
+ };
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ function normalizeText(value) {
5
+ if (value === undefined || value === null) return '';
6
+ return String(value).trim();
7
+ }
8
+
9
+ function normalizeTask(task, index = 0) {
10
+ const id = normalizeText(task?.id) || `task-${index + 1}`;
11
+ return {
12
+ id,
13
+ description: normalizeText(task?.description),
14
+ branchName: normalizeText(task?.branchName) || id.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-'),
15
+ priority: Number.isFinite(Number(task?.priority)) ? Number(task.priority) : index + 1,
16
+ };
17
+ }
18
+
19
+ function buildArtifactAgentPlan(input = {}) {
20
+ const baselineName = normalizeText(input.baselineName) || 'baseline';
21
+ const gitUrl = normalizeText(input.gitUrl) || 'https://github.com/IgorGanapolsky/ThumbGate.git';
22
+ const tasks = Array.isArray(input.tasks) ? input.tasks.map(normalizeTask) : [];
23
+ const forks = tasks.map((task) => ({
24
+ taskId: task.id,
25
+ forkName: `${baselineName}-${task.id}`.toLowerCase().replaceAll(/[^a-z0-9-]+/g, '-'),
26
+ branchName: task.branchName,
27
+ artifactRemote: null,
28
+ tokenRef: `artifact_token_${task.id}`,
29
+ status: 'planned',
30
+ }));
31
+
32
+ return {
33
+ generatedAt: normalizeText(input.generatedAt) || new Date().toISOString(),
34
+ baseline: {
35
+ name: baselineName,
36
+ gitUrl,
37
+ importIfMissing: true,
38
+ },
39
+ taskSchema: {
40
+ required: ['id', 'description', 'branchName', 'priority'],
41
+ properties: {
42
+ id: 'stable task identifier',
43
+ description: 'agent-readable task description',
44
+ branchName: 'deterministic branch/fork suffix',
45
+ priority: 'lower number runs first',
46
+ },
47
+ },
48
+ tasks,
49
+ forks,
50
+ runnerContract: {
51
+ filesystem: 'in_memory_git',
52
+ tools: ['read(path)', 'write(path, contents)', 'run_tests(command)', 'commit(message)'],
53
+ constraints: [
54
+ 'read before write',
55
+ 'minimize changes',
56
+ 'commit every successful task',
57
+ 'never expose artifact tokens in logs',
58
+ ],
59
+ },
60
+ reviewGate: {
61
+ requiredBeforeMerge: [
62
+ 'diff summary',
63
+ 'changed files',
64
+ 'test output',
65
+ 'decision journal entry',
66
+ 'human or reviewer-agent approval',
67
+ ],
68
+ blockedWithout: ['baseline comparison', 'rollback path', 'evidence artifacts'],
69
+ },
70
+ observability: {
71
+ events: ['task_created', 'fork_created', 'agent_started', 'tool_call', 'commit_pushed', 'reviewed', 'merged_or_rejected'],
72
+ metrics: ['task_latency_ms', 'tool_call_count', 'test_pass_rate', 'review_reject_rate'],
73
+ traceKey: 'artifact_task_id',
74
+ },
75
+ };
76
+ }
77
+
78
+ module.exports = {
79
+ buildArtifactAgentPlan,
80
+ normalizeTask,
81
+ };
@@ -30,7 +30,6 @@ const {
30
30
  resolveFallbackArtifactPath,
31
31
  } = require('./feedback-paths');
32
32
  const { getTelemetryAnalytics, getTelemetrySourceDiagnostics } = require('./telemetry-analytics');
33
- const { loadWorkflowSprintLeads } = require('./workflow-sprint-intake');
34
33
  const {
35
34
  PRO_MONTHLY_PRICE_ID,
36
35
  PRO_ANNUAL_PRICE_ID,
@@ -52,6 +51,12 @@ const {
52
51
  const { ensureParentDir } = require('./fs-utils');
53
52
  const mailer = require('./mailer');
54
53
 
54
+ function loadWorkflowSprintIntakeModule() {
55
+ const modulePath = path.resolve(__dirname, 'workflow-sprint-intake.js');
56
+ if (!fs.existsSync(modulePath)) return null;
57
+ return require(modulePath);
58
+ }
59
+
55
60
  // ---------------------------------------------------------------------------
56
61
  // Config
57
62
  // ---------------------------------------------------------------------------
@@ -400,10 +405,23 @@ function resolveCheckoutBrandUrls(appOrigin) {
400
405
  const origin = resolvePublicAppOrigin(appOrigin);
401
406
  return {
402
407
  icon: joinPublicUrl(origin, '/assets/brand/thumbgate-icon-512.png'),
408
+ iconPro: joinPublicUrl(origin, '/assets/brand/thumbgate-icon-pro-512.png'),
409
+ iconTeam: joinPublicUrl(origin, '/assets/brand/thumbgate-icon-team-512.png'),
403
410
  logo: joinPublicUrl(origin, '/assets/brand/thumbgate-logo-1200x360.png'),
404
411
  };
405
412
  }
406
413
 
414
+ // Resolve the per-tier product image that ships to Stripe `product_data.images`.
415
+ // Keeping three distinct URLs means the Stripe dashboard and checkout surface
416
+ // never show twins for Free/Pro/Team; see tests/billing-tier-icons.test.js.
417
+ function resolveTierIconUrl(planId, appOrigin) {
418
+ const brandUrls = resolveCheckoutBrandUrls(appOrigin);
419
+ const normalized = typeof planId === 'string' ? planId.toLowerCase() : '';
420
+ if (normalized === 'team') return brandUrls.iconTeam;
421
+ if (normalized === 'pro') return brandUrls.iconPro;
422
+ return brandUrls.icon;
423
+ }
424
+
407
425
  function buildCheckoutBrandingSettings(appOrigin) {
408
426
  const brandUrls = resolveCheckoutBrandUrls(appOrigin);
409
427
  return {
@@ -419,12 +437,11 @@ function buildCheckoutBrandingSettings(appOrigin) {
419
437
  };
420
438
  }
421
439
 
422
- function buildCheckoutProductData({ name, description, appOrigin }) {
423
- const brandUrls = resolveCheckoutBrandUrls(appOrigin);
440
+ function buildCheckoutProductData({ name, description, appOrigin, planId }) {
424
441
  return {
425
442
  name,
426
443
  description,
427
- images: [brandUrls.icon],
444
+ images: [resolveTierIconUrl(planId, appOrigin)],
428
445
  };
429
446
  }
430
447
 
@@ -443,9 +460,10 @@ function buildSubscriptionPriceData(checkoutSelection, appOrigin) {
443
460
  product_data: buildCheckoutProductData({
444
461
  name: isTeam ? 'ThumbGate Team' : 'ThumbGate Pro',
445
462
  description: isTeam
446
- ? 'Shared Pre-Action Gates, team governance, and workflow hardening for AI coding agents.'
447
- : 'Local dashboard, DPO export, and Pre-Action Gates for AI coding agents.',
463
+ ? 'Shared Pre-Action Checks, team governance, and workflow hardening for AI coding agents.'
464
+ : 'Local dashboard, DPO export, and Pre-Action Checks for AI coding agents.',
448
465
  appOrigin,
466
+ planId: isTeam ? 'team' : 'pro',
449
467
  }),
450
468
  };
451
469
  }
@@ -516,7 +534,7 @@ function buildTrialActivationEmail({ customerEmail, apiKey, sessionId, planId, a
516
534
  const subject = 'Your 7-day ThumbGate Pro trial is live';
517
535
  const preheader = 'Activate Pro in one command, open the dashboard, and start blocking repeated AI coding mistakes.';
518
536
  const headline = 'Your 7-day ThumbGate Pro trial is live.';
519
- const intro = 'ThumbGate turns thumbs up/down feedback into Pre-Action Gates that stop repeated AI coding mistakes before the next tool call. It keeps lessons local and turns repeated mistakes into Reliability Gateway blocks.';
537
+ const intro = 'ThumbGate turns thumbs up/down feedback into Pre-Action Checks that stop repeated AI coding mistakes before the next tool call. It keeps lessons local and turns repeated mistakes into Reliability Gateway blocks.';
520
538
  const exampleFeedback = 'thumbs down: the answer skipped exact files and tests; next time include paths, commands, and verification evidence.';
521
539
  const safeDashboardUrl = escapeHtml(dashboardUrl);
522
540
  const safeDocsUrl = escapeHtml(docsUrl);
@@ -1759,8 +1777,9 @@ function getBusinessAnalytics(options = {}) {
1759
1777
  (entry) => entry && entry.timestamp
1760
1778
  );
1761
1779
  const revenueEvents = loadResolvedRevenueEvents({ ...analyticsWindow, extraRevenueEvents });
1780
+ const workflowSprintIntake = loadWorkflowSprintIntakeModule();
1762
1781
  const workflowSprintLeads = filterEntriesForWindow(
1763
- loadWorkflowSprintLeads(),
1782
+ workflowSprintIntake ? workflowSprintIntake.loadWorkflowSprintLeads() : [],
1764
1783
  analyticsWindow,
1765
1784
  (entry) => entry && entry.submittedAt
1766
1785
  );
@@ -92,7 +92,7 @@ const CLI_COMMANDS = [
92
92
  },
93
93
  {
94
94
  name: 'gate-stats',
95
- description: 'Gate engine statistics — active gates, blocks, warns, time saved',
95
+ description: 'Check engine statistics — active checks, blocks, warns, time saved',
96
96
  group: 'discovery',
97
97
  flags: [
98
98
  { name: 'json', type: 'boolean', description: 'Output as JSON' },
@@ -128,12 +128,28 @@ const CLI_COMMANDS = [
128
128
  discoveryCommand({
129
129
  name: 'harness-audit',
130
130
  aliases: ['harness'],
131
- description: 'Score global docs, MCP discovery, and specialized gate harnesses',
131
+ description: 'Score global docs, MCP discovery, and specialized check harnesses',
132
132
  flags: [
133
133
  jsonFlag(),
134
134
  { name: 'doc-token-budget', type: 'number', description: 'Global docs budget (default 9000)' },
135
135
  ],
136
136
  }),
137
+ discoveryCommand({
138
+ name: 'eval',
139
+ aliases: ['prompt-eval'],
140
+ description: 'Turn feedback into reusable prompt/workflow eval proof',
141
+ flags: [
142
+ jsonFlag(),
143
+ { name: 'from-feedback', type: 'boolean', description: 'Generate eval cases from feedback-log.jsonl' },
144
+ { name: 'feedback-log', type: 'string', description: 'Explicit feedback-log.jsonl path' },
145
+ { name: 'feedback-dir', type: 'string', description: 'Explicit ThumbGate feedback directory' },
146
+ { name: 'suite', type: 'string', description: 'Run an existing prompt eval suite' },
147
+ { name: 'write-suite', type: 'string', description: 'Write generated suite JSON' },
148
+ { name: 'write-report', type: 'string', description: 'Write Markdown proof report' },
149
+ { name: 'min-score', type: 'number', description: 'Minimum passing score (default 80)' },
150
+ { name: 'max-cases', type: 'number', description: 'Maximum feedback-derived cases (default 25)' },
151
+ ],
152
+ }),
137
153
  discoveryCommand({
138
154
  name: 'native-messaging-audit',
139
155
  aliases: ['bridge-audit'],