thumbgate 1.27.11 → 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 (131) 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/builtin-lessons.json +23 -0
  12. package/config/gate-templates.json +0 -228
  13. package/config/gates/claim-verification.json +0 -18
  14. package/package.json +35 -25
  15. package/public/assets/brand/thumbgate-logo-transparent.svg +22 -0
  16. package/public/assets/brand/thumbgate-mark-inline-v3.svg +19 -0
  17. package/public/assets/brand/thumbgate-mark.svg +11 -5
  18. package/public/blog.html +0 -30
  19. package/public/brand/thumbgate-mark.svg +9 -5
  20. package/public/chatgpt-app.html +2 -2
  21. package/public/compare.html +2 -1
  22. package/public/dashboard.html +1 -1
  23. package/public/federal.html +1 -1
  24. package/public/index.html +95 -216
  25. package/public/learn.html +59 -35
  26. package/public/lessons.html +1 -1
  27. package/public/numbers.html +2 -2
  28. package/public/pro.html +7 -7
  29. package/scripts/aws-blocks-guardrails.js +228 -0
  30. package/scripts/cli-schema.js +22 -10
  31. package/scripts/dashboard-chat.js +2 -1
  32. package/scripts/document-intake.js +1 -49
  33. package/scripts/durability/step.js +3 -3
  34. package/scripts/gate-stats.js +5 -11
  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/llm-client.js +187 -5
  39. package/scripts/plausible-domain-config.js +3 -1
  40. package/scripts/seo-gsd.js +240 -1
  41. package/scripts/tool-registry.js +2 -2
  42. package/scripts/vector-store.js +44 -0
  43. package/scripts/workspace-evolver.js +62 -2
  44. package/src/api/server.js +340 -131
  45. package/public/assets/brand/thumbgate-mark-inline.svg +0 -15
  46. package/public/compare/adopt-ai.html +0 -219
  47. package/public/compare/agentix-labs.html +0 -197
  48. package/public/compare/ai-experience-orchestration.html +0 -216
  49. package/public/compare/anthropic-claude-for-legal.html +0 -260
  50. package/public/compare/anthropic-containment.html +0 -280
  51. package/public/compare/arcade.html +0 -175
  52. package/public/compare/arcjet.html +0 -239
  53. package/public/compare/bumblebee.html +0 -307
  54. package/public/compare/claude-code-hooks.html +0 -294
  55. package/public/compare/databricks-unity-ai-gateway.html +0 -215
  56. package/public/compare/fallow.html +0 -351
  57. package/public/compare/heidi.html +0 -233
  58. package/public/compare/mem0.html +0 -342
  59. package/public/compare/oak-and-sparrow-gatekeeper.html +0 -289
  60. package/public/compare/rein.html +0 -236
  61. package/public/compare/sigmashake.html +0 -256
  62. package/public/compare/speclock.html +0 -342
  63. package/public/guides/agent-harness-optimization.html +0 -342
  64. package/public/guides/agentic-web-governance.html +0 -406
  65. package/public/guides/ai-agent-governance-sprint.html +0 -415
  66. package/public/guides/ai-agent-pre-action-approval-gates.html +0 -401
  67. package/public/guides/ai-agent-workflow-migration-checklist.html +0 -392
  68. package/public/guides/ai-deployment-readiness.html +0 -415
  69. package/public/guides/ai-mode-ads-agent-governance.html +0 -401
  70. package/public/guides/ai-search-topical-presence.html +0 -342
  71. package/public/guides/autoresearch-agent-safety.html +0 -342
  72. package/public/guides/background-agent-governance.html +0 -358
  73. package/public/guides/best-tools-stop-ai-agents-breaking-production.html +0 -363
  74. package/public/guides/browser-automation-safety.html +0 -342
  75. package/public/guides/chatgpt-ads-trust.html +0 -353
  76. package/public/guides/claude-code-feedback.html +0 -339
  77. package/public/guides/claude-code-prevent-repeated-mistakes.html +0 -161
  78. package/public/guides/claude-code-skills-guardrails.html +0 -343
  79. package/public/guides/claude-desktop.html +0 -356
  80. package/public/guides/code-knowledge-graph-guardrails.html +0 -365
  81. package/public/guides/codex-cli-guardrails.html +0 -339
  82. package/public/guides/cursor-agent-guardrails.html +0 -339
  83. package/public/guides/cursor-prevent-repeated-mistakes.html +0 -161
  84. package/public/guides/database-agent-safety.html +0 -406
  85. package/public/guides/deepseek-v4-runtime-guardrails.html +0 -346
  86. package/public/guides/developer-machine-supply-chain-guardrails.html +0 -358
  87. package/public/guides/gcp-mcp-guardrails.html +0 -147
  88. package/public/guides/gemini-cli-feedback-memory.html +0 -339
  89. package/public/guides/gpt-5-5-model-evaluation.html +0 -358
  90. package/public/guides/internal-ai-engineering-stack-guardrails.html +0 -348
  91. package/public/guides/long-running-agent-context-management.html +0 -346
  92. package/public/guides/mcp-tool-governance.html +0 -401
  93. package/public/guides/multica-thumbgate-setup.html +0 -134
  94. package/public/guides/native-messaging-host-security.html +0 -342
  95. package/public/guides/policy-engine-pre-action-gates.html +0 -346
  96. package/public/guides/pre-action-checks.html +0 -342
  97. package/public/guides/pretooluse-hooks-vs-advisory-prompt-rules.html +0 -342
  98. package/public/guides/prompt-tricks-to-workflow-rules.html +0 -365
  99. package/public/guides/proxy-pointer-rag-guardrails.html +0 -352
  100. package/public/guides/rag-precision-tuning-guardrails.html +0 -352
  101. package/public/guides/reasoning-compression-guardrails.html +0 -346
  102. package/public/guides/relational-knowledge-ai-recommendations.html +0 -342
  103. package/public/guides/roo-code-alternative-cline.html +0 -339
  104. package/public/guides/semantic-programmatic-seo-guardrails.html +0 -352
  105. package/public/guides/seo-agent-skills-guardrails.html +0 -344
  106. package/public/guides/stop-repeated-ai-agent-mistakes.html +0 -342
  107. package/public/learn/ac-dc-runtime-enforcement.html +0 -277
  108. package/public/learn/agent-harness-pattern.html +0 -181
  109. package/public/learn/agent-identity-connector-governance.html +0 -146
  110. package/public/learn/agent-swarms-shared-gates.html +0 -173
  111. package/public/learn/agentic-enterprise-context-brain.html +0 -117
  112. package/public/learn/agentic-os-team-governance.html +0 -146
  113. package/public/learn/ai-agent-governance.html +0 -158
  114. package/public/learn/ai-agent-persistent-memory.html +0 -211
  115. package/public/learn/anthropomorphic-claim-gates.html +0 -180
  116. package/public/learn/background-agent-control-layer.html +0 -184
  117. package/public/learn/claude-code-goal-with-rubrics.html +0 -205
  118. package/public/learn/codex-role-plugins-need-governance.html +0 -125
  119. package/public/learn/cost-aware-agent-gate-routing.html +0 -173
  120. package/public/learn/databricks-unity-ai-gateway-runtime-governance.html +0 -157
  121. package/public/learn/deterministic-agent-workflows.html +0 -185
  122. package/public/learn/feedback-loop-vs-decision-layer.html +0 -283
  123. package/public/learn/from-prototype-to-production.html +0 -223
  124. package/public/learn/learn.css +0 -51
  125. package/public/learn/mcp-pre-action-checks-explained.html +0 -172
  126. package/public/learn/pretix-stripe-connect-marketplaces.html +0 -161
  127. package/public/learn/regulated-agent-execution-boundary.html +0 -196
  128. package/public/learn/spec-driven-development.html +0 -168
  129. package/public/learn/stop-ai-agent-force-push.html +0 -134
  130. package/public/learn/vibe-coding-safety-net.html +0 -142
  131. 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
- };