thumbgate 1.27.12 → 1.27.13

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 (132) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.well-known/llms.txt +2 -1
  3. package/.well-known/mcp/server-card.json +1 -1
  4. package/README.md +2 -4
  5. package/adapters/claude/.mcp.json +2 -2
  6. package/adapters/mcp/server-stdio.js +1 -1
  7. package/adapters/opencode/opencode.json +1 -1
  8. package/adapters/policy-engine/ethicore-guardian-client.js +68 -0
  9. package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +260 -0
  10. package/bin/cli.js +78 -259
  11. package/config/gate-templates.json +0 -228
  12. package/config/gates/claim-verification.json +0 -18
  13. package/package.json +35 -25
  14. package/public/assets/brand/thumbgate-logo-transparent.svg +22 -0
  15. package/public/assets/brand/thumbgate-mark-inline-v3.svg +19 -0
  16. package/public/assets/brand/thumbgate-mark.svg +11 -5
  17. package/public/blog.html +0 -30
  18. package/public/brand/thumbgate-mark.svg +9 -5
  19. package/public/chatgpt-app.html +2 -2
  20. package/public/compare.html +2 -1
  21. package/public/dashboard.html +1 -1
  22. package/public/federal.html +1 -1
  23. package/public/index.html +95 -216
  24. package/public/learn.html +59 -35
  25. package/public/lessons.html +1 -1
  26. package/public/numbers.html +2 -2
  27. package/public/pro.html +7 -7
  28. package/scripts/aws-blocks-guardrails.js +228 -0
  29. package/scripts/cli-schema.js +22 -10
  30. package/scripts/dashboard-chat.js +2 -1
  31. package/scripts/document-intake.js +1 -49
  32. package/scripts/durability/step.js +3 -3
  33. package/scripts/gate-stats.js +5 -11
  34. package/scripts/gates-engine.js +0 -49
  35. package/scripts/gemini-embedding-policy.js +2 -1
  36. package/scripts/hook-stop-anti-claim.js +116 -184
  37. package/scripts/hosted-config.js +0 -12
  38. package/scripts/lesson-search.js +1 -15
  39. package/scripts/llm-client.js +187 -5
  40. package/scripts/plausible-domain-config.js +3 -1
  41. package/scripts/seo-gsd.js +240 -1
  42. package/scripts/tool-registry.js +2 -2
  43. package/scripts/vector-store.js +44 -0
  44. package/scripts/workspace-evolver.js +62 -2
  45. package/src/api/server.js +340 -131
  46. package/public/assets/brand/thumbgate-mark-inline.svg +0 -15
  47. package/public/compare/adopt-ai.html +0 -219
  48. package/public/compare/agentix-labs.html +0 -197
  49. package/public/compare/ai-experience-orchestration.html +0 -216
  50. package/public/compare/anthropic-claude-for-legal.html +0 -260
  51. package/public/compare/anthropic-containment.html +0 -280
  52. package/public/compare/arcade.html +0 -175
  53. package/public/compare/arcjet.html +0 -239
  54. package/public/compare/bumblebee.html +0 -307
  55. package/public/compare/claude-code-hooks.html +0 -294
  56. package/public/compare/databricks-unity-ai-gateway.html +0 -215
  57. package/public/compare/fallow.html +0 -351
  58. package/public/compare/heidi.html +0 -233
  59. package/public/compare/mem0.html +0 -342
  60. package/public/compare/oak-and-sparrow-gatekeeper.html +0 -289
  61. package/public/compare/rein.html +0 -236
  62. package/public/compare/sigmashake.html +0 -256
  63. package/public/compare/speclock.html +0 -342
  64. package/public/guides/agent-harness-optimization.html +0 -342
  65. package/public/guides/agentic-web-governance.html +0 -406
  66. package/public/guides/ai-agent-governance-sprint.html +0 -415
  67. package/public/guides/ai-agent-pre-action-approval-gates.html +0 -401
  68. package/public/guides/ai-agent-workflow-migration-checklist.html +0 -392
  69. package/public/guides/ai-deployment-readiness.html +0 -415
  70. package/public/guides/ai-mode-ads-agent-governance.html +0 -401
  71. package/public/guides/ai-search-topical-presence.html +0 -342
  72. package/public/guides/autoresearch-agent-safety.html +0 -342
  73. package/public/guides/background-agent-governance.html +0 -358
  74. package/public/guides/best-tools-stop-ai-agents-breaking-production.html +0 -363
  75. package/public/guides/browser-automation-safety.html +0 -342
  76. package/public/guides/chatgpt-ads-trust.html +0 -353
  77. package/public/guides/claude-code-feedback.html +0 -339
  78. package/public/guides/claude-code-prevent-repeated-mistakes.html +0 -161
  79. package/public/guides/claude-code-skills-guardrails.html +0 -343
  80. package/public/guides/claude-desktop.html +0 -356
  81. package/public/guides/code-knowledge-graph-guardrails.html +0 -365
  82. package/public/guides/codex-cli-guardrails.html +0 -339
  83. package/public/guides/cursor-agent-guardrails.html +0 -339
  84. package/public/guides/cursor-prevent-repeated-mistakes.html +0 -161
  85. package/public/guides/database-agent-safety.html +0 -406
  86. package/public/guides/deepseek-v4-runtime-guardrails.html +0 -346
  87. package/public/guides/developer-machine-supply-chain-guardrails.html +0 -358
  88. package/public/guides/gcp-mcp-guardrails.html +0 -147
  89. package/public/guides/gemini-cli-feedback-memory.html +0 -339
  90. package/public/guides/gpt-5-5-model-evaluation.html +0 -358
  91. package/public/guides/internal-ai-engineering-stack-guardrails.html +0 -348
  92. package/public/guides/long-running-agent-context-management.html +0 -346
  93. package/public/guides/mcp-tool-governance.html +0 -401
  94. package/public/guides/multica-thumbgate-setup.html +0 -134
  95. package/public/guides/native-messaging-host-security.html +0 -342
  96. package/public/guides/policy-engine-pre-action-gates.html +0 -346
  97. package/public/guides/pre-action-checks.html +0 -342
  98. package/public/guides/pretooluse-hooks-vs-advisory-prompt-rules.html +0 -342
  99. package/public/guides/prompt-tricks-to-workflow-rules.html +0 -365
  100. package/public/guides/proxy-pointer-rag-guardrails.html +0 -352
  101. package/public/guides/rag-precision-tuning-guardrails.html +0 -352
  102. package/public/guides/reasoning-compression-guardrails.html +0 -346
  103. package/public/guides/relational-knowledge-ai-recommendations.html +0 -342
  104. package/public/guides/roo-code-alternative-cline.html +0 -339
  105. package/public/guides/semantic-programmatic-seo-guardrails.html +0 -352
  106. package/public/guides/seo-agent-skills-guardrails.html +0 -344
  107. package/public/guides/stop-repeated-ai-agent-mistakes.html +0 -342
  108. package/public/learn/ac-dc-runtime-enforcement.html +0 -277
  109. package/public/learn/agent-harness-pattern.html +0 -181
  110. package/public/learn/agent-identity-connector-governance.html +0 -146
  111. package/public/learn/agent-swarms-shared-gates.html +0 -173
  112. package/public/learn/agentic-enterprise-context-brain.html +0 -117
  113. package/public/learn/agentic-os-team-governance.html +0 -146
  114. package/public/learn/ai-agent-governance.html +0 -158
  115. package/public/learn/ai-agent-persistent-memory.html +0 -211
  116. package/public/learn/anthropomorphic-claim-gates.html +0 -180
  117. package/public/learn/background-agent-control-layer.html +0 -184
  118. package/public/learn/claude-code-goal-with-rubrics.html +0 -205
  119. package/public/learn/codex-role-plugins-need-governance.html +0 -125
  120. package/public/learn/cost-aware-agent-gate-routing.html +0 -173
  121. package/public/learn/databricks-unity-ai-gateway-runtime-governance.html +0 -157
  122. package/public/learn/deterministic-agent-workflows.html +0 -185
  123. package/public/learn/feedback-loop-vs-decision-layer.html +0 -283
  124. package/public/learn/from-prototype-to-production.html +0 -223
  125. package/public/learn/learn.css +0 -51
  126. package/public/learn/mcp-pre-action-checks-explained.html +0 -172
  127. package/public/learn/pretix-stripe-connect-marketplaces.html +0 -161
  128. package/public/learn/regulated-agent-execution-boundary.html +0 -196
  129. package/public/learn/spec-driven-development.html +0 -168
  130. package/public/learn/stop-ai-agent-force-push.html +0 -134
  131. package/public/learn/vibe-coding-safety-net.html +0 -142
  132. package/scripts/reddit-browser-notification-watch.js +0 -230
@@ -1,230 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
- const { chromium } = require('playwright-core');
7
-
8
- const DEFAULT_CDP_ENDPOINT = 'http://127.0.0.1:9222';
9
- const DEFAULT_STATE_FILE = path.resolve(__dirname, '..', '.thumbgate', 'reddit-browser-notification-state.json');
10
- const DEFAULT_EVENTS_FILE = path.resolve(__dirname, '..', '.thumbgate', 'reddit-browser-notifications.jsonl');
11
- const REDDIT_NOTIFICATIONS_URL = 'https://www.reddit.com/notifications';
12
-
13
- function resolveRuntimeFile(envName, defaultPath) {
14
- const configured = process.env[envName];
15
- return configured ? path.resolve(configured) : defaultPath;
16
- }
17
-
18
- function loadJson(filePath, fallback) {
19
- try {
20
- if (fs.existsSync(filePath)) return JSON.parse(fs.readFileSync(filePath, 'utf8'));
21
- } catch {
22
- // Ignore corrupt transient state; a later write will repair it.
23
- }
24
- return fallback;
25
- }
26
-
27
- function writeJson(filePath, value) {
28
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
29
- fs.writeFileSync(filePath, JSON.stringify(value, null, 2));
30
- }
31
-
32
- function appendJsonl(filePath, rows) {
33
- if (rows.length === 0) return;
34
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
35
- fs.appendFileSync(filePath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`);
36
- }
37
-
38
- function fingerprintNotification(notification) {
39
- return [
40
- notification.author || '',
41
- notification.kind || '',
42
- notification.subreddit || '',
43
- notification.preview || '',
44
- notification.age || '',
45
- ].join('|').toLowerCase();
46
- }
47
-
48
- function scoreNotification(notification) {
49
- const text = `${notification.author || ''} ${notification.kind || ''} ${notification.preview || ''}`.toLowerCase();
50
- let score = 0;
51
- const reasons = [];
52
-
53
- if (/accepted your chat invite|chat invite/i.test(text)) {
54
- score += 5;
55
- reasons.push('chat_accepted');
56
- }
57
- if (/\b(interested|try|paid|diagnostic|workflow|failure|gate|thumbgate|thubgate)\b/i.test(text)) {
58
- score += 4;
59
- reasons.push('buyer_signal');
60
- }
61
- if (/\b(replied|mentioned)\b/i.test(text)) {
62
- score += 2;
63
- reasons.push('reply_or_mention');
64
- }
65
- if (/\b(spam|slop|bot|report|ignore all previous instructions)\b/i.test(text)) {
66
- score -= 5;
67
- reasons.push('hostile_or_meta');
68
- }
69
- if (/automoderator|mod-bot|minimum karma|removed|reviewed shortly/i.test(text)) {
70
- score -= 1;
71
- reasons.push('platform_moderation');
72
- }
73
-
74
- return { score, reasons };
75
- }
76
-
77
- function ageMinutes(age) {
78
- const text = String(age || '').trim().toLowerCase();
79
- if (!text || text === 'just now') return 0;
80
- const match = /^(\d+)\s*([mhdw])\s+ago$/.exec(text);
81
- if (!match) return Number.POSITIVE_INFINITY;
82
- const value = Number(match[1]);
83
- const unit = match[2];
84
- if (unit === 'm') return value;
85
- if (unit === 'h') return value * 60;
86
- if (unit === 'd') return value * 24 * 60;
87
- return value * 7 * 24 * 60;
88
- }
89
-
90
- function isAgeLine(line) {
91
- return /^(?:just now|\d+\s*[mhdw]\s+ago)$/i.test(String(line || '').trim());
92
- }
93
-
94
- function isRecentNotification(notification, maxAgeMinutes = 48 * 60) {
95
- return ageMinutes(notification.age) <= maxAgeMinutes;
96
- }
97
-
98
- function parseNotificationBlocks(bodyText) {
99
- const lines = String(bodyText || '')
100
- .split('\n')
101
- .map((line) => line.trim())
102
- .filter(Boolean);
103
- const notifications = [];
104
-
105
- for (let index = 0; index < lines.length; index += 1) {
106
- let author = lines[index];
107
- let kind = lines[index + 1] || '';
108
- let kindIndex = index + 1;
109
- if (isAgeLine(author)) continue;
110
-
111
- if (/\b(replied to|mentioned you|new mentions)\b/i.test(author)) {
112
- kind = author;
113
- kindIndex = index;
114
- const authorMatch = /^u\/([^\s]+)/i.exec(kind);
115
- author = authorMatch ? authorMatch[1] : author;
116
- }
117
-
118
- if (!kind || !/\b(accepted your chat invite|replied to|mentioned you|new mentions)\b/i.test(kind)) continue;
119
-
120
- const hasPreview = !/accepted your chat invite|new mentions/i.test(kind);
121
- const preview = hasPreview ? (lines[kindIndex + 1] || '') : '';
122
- const age = hasPreview ? (lines[kindIndex + 2] || '') : (lines[kindIndex + 1] || '');
123
- const subredditMatch = /\bin\s+r\/([A-Za-z0-9_]+)/.exec(kind);
124
- const notification = {
125
- author,
126
- kind,
127
- subreddit: subredditMatch ? subredditMatch[1] : null,
128
- preview,
129
- age,
130
- };
131
- const scored = scoreNotification(notification);
132
- notifications.push({
133
- ...notification,
134
- ...scored,
135
- ageMinutes: ageMinutes(notification.age),
136
- fingerprint: fingerprintNotification(notification),
137
- });
138
- }
139
-
140
- return notifications;
141
- }
142
-
143
- async function readRedditNotifications({
144
- cdpEndpoint = process.env.THUMBGATE_CHROME_CDP_ENDPOINT || DEFAULT_CDP_ENDPOINT,
145
- timeoutMs = Number(process.env.THUMBGATE_REDDIT_BROWSER_TIMEOUT_MS || 15000),
146
- } = {}) {
147
- const browser = await chromium.connectOverCDP(cdpEndpoint);
148
- const context = browser.contexts()[0] || await browser.newContext();
149
- const page = await context.newPage();
150
- try {
151
- await page.goto(REDDIT_NOTIFICATIONS_URL, { waitUntil: 'domcontentloaded', timeout: timeoutMs });
152
- await page.waitForTimeout(3000);
153
- const bodyText = await page.locator('body').innerText({ timeout: timeoutMs });
154
- return parseNotificationBlocks(bodyText);
155
- } finally {
156
- await page.close().catch(() => {});
157
- await browser.close().catch(() => {});
158
- }
159
- }
160
-
161
- async function run({ dryRun = false, now = new Date().toISOString() } = {}) {
162
- const stateFile = resolveRuntimeFile('THUMBGATE_REDDIT_BROWSER_STATE_FILE', DEFAULT_STATE_FILE);
163
- const eventsFile = resolveRuntimeFile('THUMBGATE_REDDIT_BROWSER_EVENTS_FILE', DEFAULT_EVENTS_FILE);
164
- const state = loadJson(stateFile, { seen: {} });
165
- const notifications = await readRedditNotifications();
166
- const fresh = notifications.filter((notification) => !state.seen[notification.fingerprint]);
167
- const actionable = fresh.filter((notification) => notification.score > 0 && isRecentNotification(notification));
168
- const rows = actionable.map((notification) => ({
169
- checkedAt: now,
170
- platform: 'reddit',
171
- source: 'browser_notifications',
172
- status: 'pending_review',
173
- ...notification,
174
- }));
175
-
176
- for (const notification of fresh) {
177
- state.seen[notification.fingerprint] = { seenAt: now, score: notification.score };
178
- }
179
- state.lastCheck = now;
180
-
181
- if (!dryRun) {
182
- writeJson(stateFile, state);
183
- appendJsonl(eventsFile, rows);
184
- }
185
-
186
- return {
187
- notifications: notifications.length,
188
- fresh: fresh.length,
189
- actionable: actionable.length,
190
- eventsFile,
191
- actionableItems: actionable,
192
- dryRun,
193
- };
194
- }
195
-
196
- function parseArgs(argv = process.argv.slice(2)) {
197
- return {
198
- dryRun: argv.includes('--dry-run'),
199
- json: argv.includes('--json'),
200
- };
201
- }
202
-
203
- if (require.main === module) {
204
- const args = parseArgs();
205
- run({ dryRun: args.dryRun })
206
- .then((result) => {
207
- if (args.json) {
208
- console.log(JSON.stringify(result, null, 2));
209
- } else {
210
- console.log(`[reddit-browser-watch] notifications=${result.notifications} fresh=${result.fresh} actionable=${result.actionable} dryRun=${result.dryRun}`);
211
- for (const item of result.actionableItems) {
212
- console.log(`- score=${item.score} author=${item.author} kind=${item.kind} preview=${item.preview.slice(0, 120)}`);
213
- }
214
- }
215
- })
216
- .catch((err) => {
217
- console.error(`[reddit-browser-watch] ${err.message}`);
218
- process.exitCode = 1;
219
- });
220
- }
221
-
222
- module.exports = {
223
- fingerprintNotification,
224
- ageMinutes,
225
- isRecentNotification,
226
- parseNotificationBlocks,
227
- readRedditNotifications,
228
- run,
229
- scoreNotification,
230
- };