thumbgate 1.37.1 → 1.37.2
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.
- package/.agents/skills/cobble-hot-store-compare-not-clone/SKILL.md +80 -0
- package/.agents/skills/colab-compute-honesty-not-clone/SKILL.md +70 -0
- package/.agents/skills/token-shunt-honesty-not-clone/SKILL.md +67 -0
- package/.agents/skills/typesafe-typed-questions-not-clone/SKILL.md +82 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/forge/forge.yaml +3 -3
- package/adapters/future-agi/.mcp.json +1 -1
- package/adapters/future-agi/config.toml +1 -1
- package/adapters/future-agi/opencode.json +1 -1
- package/adapters/herdr/herdr-plugin.toml +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +129 -0
- package/config/gate-templates.json +48 -0
- package/package.json +23 -7
- package/public/index.html +2 -2
- package/public/install.html +7 -7
- package/public/numbers.html +2 -2
- package/scripts/cli-schema.js +40 -0
- package/scripts/cobble-hot-store-split.js +600 -0
- package/scripts/colab-compute-honesty.js +281 -0
- package/scripts/gates-engine.js +16 -5
- package/scripts/token-shunt-honesty.js +502 -0
- package/scripts/typesafe-typed-questions.js +813 -0
- package/server.json +2 -2
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Colab signup FORMAT steal — not a Colab clone, not a GPU SKU.
|
|
6
|
+
*
|
|
7
|
+
* Live source 2026-09-17 (BrowserOS, iganapolsky@gmail.com):
|
|
8
|
+
* https://colab.research.google.com/signup
|
|
9
|
+
* Pay As You Go $9.99/100 CU and $49.99/500 CU (buttons disabled on this account)
|
|
10
|
+
* Colab Pro $9.99/mo and Pro+ $49.99/mo still show Subscribe
|
|
11
|
+
* "See current plan" is Google AI Plans — not proof of Pro+
|
|
12
|
+
*
|
|
13
|
+
* Transfers:
|
|
14
|
+
* 1. Free exists; paid buys Compute Units, not a dedicated GPU
|
|
15
|
+
* 2. Subscribe-button visible ≠ already subscribed
|
|
16
|
+
* 3. Background / 24h execution is a Pro+ receipt, not a default
|
|
17
|
+
*
|
|
18
|
+
* Does not buy Colab Pro/Pro+/PAYG. Does not install colab-cli.
|
|
19
|
+
* ThumbGate evals stay on GitHub Actions.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const path = require('node:path');
|
|
23
|
+
|
|
24
|
+
const SOURCE_URL = 'https://colab.research.google.com/signup';
|
|
25
|
+
|
|
26
|
+
const PLANS = Object.freeze({
|
|
27
|
+
free: { monthlyUsd: 0, includedCu: 0, backgroundHours: 0 },
|
|
28
|
+
payg: { monthlyUsd: 0, includedCu: 0, backgroundHours: 0, packs: [[9.99, 100], [49.99, 500]] },
|
|
29
|
+
pro: { monthlyUsd: 9.99, includedCu: 100, backgroundHours: 0 },
|
|
30
|
+
proplus: { monthlyUsd: 49.99, includedCu: 600, backgroundHours: 24 },
|
|
31
|
+
enterprise: { monthlyUsd: null, includedCu: null, backgroundHours: null },
|
|
32
|
+
unknown: { monthlyUsd: null, includedCu: null, backgroundHours: 0 },
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const CLONE_RE = /\b(colab-cli|google-colab-pro-runner|ngrok.*colab|colab.*ssh|zero-cost a100|free a100)\b/i;
|
|
36
|
+
const PAID_FEATURE_RE = /\b(pro\+|proplus|a100|v100|background execution|24\s*hours?|high-?ram|premium gpu|compute units?)\b/i;
|
|
37
|
+
const BUY_RE = /\b(buy|subscribe|purchase).{0,40}(colab pro|pro\+|compute units?)\b/i;
|
|
38
|
+
|
|
39
|
+
const RAIL_MAP = Object.freeze([
|
|
40
|
+
{ colab: 'Free always exists; paid is extra CU', thumbgate: 'Do not claim hosted GPU/A100 as the product' },
|
|
41
|
+
{ colab: 'Subscribe button visible ≠ already subscribed', thumbgate: 'Require --plan-proof before Pro/Pro+ claims' },
|
|
42
|
+
{ colab: 'CU packs ($9.99/100, $49.99/500) expire; CU ≠ dedicated GPU', thumbgate: 'Hours remaining need hardware class + CU rate; else fail closed' },
|
|
43
|
+
{ colab: 'Background 24h is Pro+', thumbgate: 'GitHub Actions remains the eval runner; no Colab offload SKU' },
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
function normalizeBoolean(value) {
|
|
47
|
+
if (value === true || value === 1) return true;
|
|
48
|
+
if (value === false || value === 0 || value == null) return false;
|
|
49
|
+
return /^(1|true|yes|on)$/i.test(String(value).trim());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseSnapshot(raw) {
|
|
53
|
+
if (!raw) return null;
|
|
54
|
+
if (typeof raw === 'object') return raw;
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(String(raw));
|
|
57
|
+
} catch {
|
|
58
|
+
return { parseError: true };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeOptions(raw = {}) {
|
|
63
|
+
const plan = String(raw.plan || 'unknown').toLowerCase().replace('pro+', 'proplus');
|
|
64
|
+
return {
|
|
65
|
+
claim: String(raw.claim || ''),
|
|
66
|
+
plan: Object.prototype.hasOwnProperty.call(PLANS, plan) ? plan : 'unknown',
|
|
67
|
+
planProof: raw['plan-proof'] || raw.planProof || null,
|
|
68
|
+
snapshot: parseSnapshot(raw.snapshot || raw['signup-snapshot']),
|
|
69
|
+
cloneColab: normalizeBoolean(raw['clone-colab'] || raw.cloneColab),
|
|
70
|
+
buyPro: normalizeBoolean(raw['buy-pro'] || raw.buyPro),
|
|
71
|
+
mapOnly: normalizeBoolean(raw['map-only'] || raw.mapOnly),
|
|
72
|
+
claimReady: normalizeBoolean(raw['claim-ready'] || raw.claimReady),
|
|
73
|
+
json: normalizeBoolean(raw.json),
|
|
74
|
+
strict: normalizeBoolean(raw.strict),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildColabComputeHonestyReport(rawOptions = {}) {
|
|
79
|
+
const options = normalizeOptions(rawOptions);
|
|
80
|
+
const findings = [];
|
|
81
|
+
const claim = options.claim;
|
|
82
|
+
const snapshot = options.snapshot && !options.snapshot.parseError ? options.snapshot : null;
|
|
83
|
+
|
|
84
|
+
if (options.snapshot && options.snapshot.parseError) {
|
|
85
|
+
findings.push({
|
|
86
|
+
id: 'snapshot_parse_error',
|
|
87
|
+
severity: 'fail',
|
|
88
|
+
gateId: 'require-compute-unit-proof',
|
|
89
|
+
message: 'Signup snapshot JSON did not parse.',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (options.cloneColab || CLONE_RE.test(claim)) {
|
|
94
|
+
findings.push({
|
|
95
|
+
id: 'colab_sku_clone',
|
|
96
|
+
severity: 'fail',
|
|
97
|
+
gateId: 'refuse-colab-sku-clone',
|
|
98
|
+
message: 'Refused Colab clone (colab-cli, ngrok/SSH, zero-cost A100, or a Colab-runner SKU).',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (options.buyPro || BUY_RE.test(claim)) {
|
|
103
|
+
findings.push({
|
|
104
|
+
id: 'colab_spend_refused',
|
|
105
|
+
severity: 'fail',
|
|
106
|
+
gateId: 'refuse-colab-sku-clone',
|
|
107
|
+
message: 'Refused buying Colab Pro/Pro+/Compute Units from this doctor. No surprise spend.',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const wantsPaid = PAID_FEATURE_RE.test(claim);
|
|
112
|
+
const proof = options.planProof ? String(options.planProof).toLowerCase().replace('pro+', 'proplus') : null;
|
|
113
|
+
if (wantsPaid && !options.mapOnly) {
|
|
114
|
+
if (!proof || proof === 'unknown' || proof === 'free') {
|
|
115
|
+
findings.push({
|
|
116
|
+
id: 'paid_feature_without_plan_proof',
|
|
117
|
+
severity: 'fail',
|
|
118
|
+
gateId: 'require-compute-unit-proof',
|
|
119
|
+
message: 'Paid Colab features (Pro/Pro+/A100/CU/24h background) need --plan-proof from a live Current-plan receipt, not a Subscribe button.',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (snapshot && snapshot.proSubscribeVisible && (proof === 'pro' || proof === 'proplus' || /pro\+|proplus/i.test(claim))) {
|
|
125
|
+
findings.push({
|
|
126
|
+
id: 'subscribe_button_is_not_receipt',
|
|
127
|
+
severity: 'fail',
|
|
128
|
+
gateId: 'require-compute-unit-proof',
|
|
129
|
+
message: 'Live signup still shows Subscribe on Pro/Pro+. That is not proof the account is subscribed.',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (/24\s*hour|background execution/i.test(claim) && proof !== 'proplus') {
|
|
134
|
+
findings.push({
|
|
135
|
+
id: 'background_requires_proplus',
|
|
136
|
+
severity: 'fail',
|
|
137
|
+
gateId: 'require-compute-unit-proof',
|
|
138
|
+
message: '24h background execution is a Pro+ receipt on the signup page, not Free/Pro/PAYG.',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (options.claimReady && findings.some((f) => f.severity === 'fail')) {
|
|
143
|
+
findings.push({
|
|
144
|
+
id: 'claim_without_compute_proof',
|
|
145
|
+
severity: 'fail',
|
|
146
|
+
gateId: 'require-compute-unit-proof',
|
|
147
|
+
message: 'Claimed Colab compute ready without plan proof.',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const seen = new Set();
|
|
152
|
+
const deduped = [];
|
|
153
|
+
for (const f of findings) {
|
|
154
|
+
if (seen.has(f.id)) continue;
|
|
155
|
+
seen.add(f.id);
|
|
156
|
+
deduped.push(f);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const failCount = deduped.filter((f) => f.severity === 'fail').length;
|
|
160
|
+
let status = 'ready';
|
|
161
|
+
if (failCount > 0) status = 'fail';
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
name: 'thumbgate-colab-compute-honesty',
|
|
165
|
+
ok: status !== 'fail',
|
|
166
|
+
status,
|
|
167
|
+
source: SOURCE_URL,
|
|
168
|
+
disclaimer:
|
|
169
|
+
'FORMAT steal from Colab paid signup (CU packs, subscribe≠receipt, 24h background is Pro+). Not affiliated with Google Colab. Does not buy a plan or clone a notebook GPU SKU.',
|
|
170
|
+
observed: {
|
|
171
|
+
account: snapshot && snapshot.account,
|
|
172
|
+
paygDisabled: snapshot ? Boolean(snapshot.paygDisabled) : null,
|
|
173
|
+
proSubscribeVisible: snapshot ? Boolean(snapshot.proSubscribeVisible) : null,
|
|
174
|
+
proPlusSubscribeVisible: snapshot ? Boolean(snapshot.proPlusSubscribeVisible) : null,
|
|
175
|
+
},
|
|
176
|
+
plans: PLANS,
|
|
177
|
+
plan: options.plan,
|
|
178
|
+
planProof: proof,
|
|
179
|
+
map: options.mapOnly ? RAIL_MAP : undefined,
|
|
180
|
+
findings: deduped,
|
|
181
|
+
summary: { failCount, findingCount: deduped.length },
|
|
182
|
+
recommendedGates: [...new Set(deduped.map((f) => f.gateId).filter(Boolean))],
|
|
183
|
+
nextActions: [
|
|
184
|
+
'Keep ThumbGate evals on GitHub Actions. Do not offload PreToolUse to Colab.',
|
|
185
|
+
'Treat Subscribe on /signup as not-subscribed until a Current-plan receipt exists.',
|
|
186
|
+
'Do not buy Pro/Pro+/CU from an agent session.',
|
|
187
|
+
'Pair with gates require-compute-unit-proof and refuse-colab-sku-clone.',
|
|
188
|
+
],
|
|
189
|
+
exampleCommand: 'npx thumbgate colab-compute-honesty --json --map-only',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function formatColabComputeHonestyReport(report) {
|
|
194
|
+
const lines = [
|
|
195
|
+
'',
|
|
196
|
+
'ThumbGate Colab Compute-Honesty Doctor',
|
|
197
|
+
'-'.repeat(48),
|
|
198
|
+
`Status : ${report.status}`,
|
|
199
|
+
`Plan : ${report.plan} proof=${report.planProof || '(none)'}`,
|
|
200
|
+
`Source : ${report.source}`,
|
|
201
|
+
`Findings: ${report.summary.findingCount} (fail=${report.summary.failCount})`,
|
|
202
|
+
];
|
|
203
|
+
if (report.map) {
|
|
204
|
+
lines.push('', 'Rail map:');
|
|
205
|
+
for (const row of report.map) lines.push(` - ${row.colab} → ${row.thumbgate}`);
|
|
206
|
+
}
|
|
207
|
+
if (report.findings.length) {
|
|
208
|
+
lines.push('', 'Findings:');
|
|
209
|
+
for (const f of report.findings) {
|
|
210
|
+
lines.push(` - [${f.severity}] ${f.id}${f.gateId ? ` [${f.gateId}]` : ''}`);
|
|
211
|
+
lines.push(` ${f.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
lines.push('', 'Next actions:');
|
|
215
|
+
for (const a of report.nextActions) lines.push(` - ${a}`);
|
|
216
|
+
lines.push('', `Example: ${report.exampleCommand}`);
|
|
217
|
+
lines.push(`Note: ${report.disclaimer}`, '');
|
|
218
|
+
return `${lines.join('\n')}\n`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function parseCliArgs(argv) {
|
|
222
|
+
const options = {};
|
|
223
|
+
for (const arg of argv) {
|
|
224
|
+
if (arg === '--json') { options.json = true; continue; }
|
|
225
|
+
if (arg === '--strict') { options.strict = true; continue; }
|
|
226
|
+
if (arg === '--map-only') { options['map-only'] = true; continue; }
|
|
227
|
+
if (arg === '--claim-ready') { options['claim-ready'] = true; continue; }
|
|
228
|
+
if (arg === '--clone-colab') { options['clone-colab'] = true; continue; }
|
|
229
|
+
if (arg === '--buy-pro') { options['buy-pro'] = true; continue; }
|
|
230
|
+
if (arg === '--help' || arg === '-h') { options.help = true; continue; }
|
|
231
|
+
const m = /^--([^=]+)(?:=(.*))?$/.exec(arg);
|
|
232
|
+
if (!m) continue;
|
|
233
|
+
options[m[1]] = m[2] === undefined ? true : m[2];
|
|
234
|
+
}
|
|
235
|
+
return options;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function printHelp() {
|
|
239
|
+
process.stdout.write(`Usage: node scripts/colab-compute-honesty.js [flags]
|
|
240
|
+
|
|
241
|
+
Flags:
|
|
242
|
+
--claim=TEXT Claim to audit
|
|
243
|
+
--plan=free|payg|pro|proplus|unknown
|
|
244
|
+
--plan-proof=PLAN Live Current-plan receipt (not a Subscribe button)
|
|
245
|
+
--snapshot=JSON Live /signup snapshot
|
|
246
|
+
--map-only
|
|
247
|
+
--claim-ready
|
|
248
|
+
--clone-colab Always fail
|
|
249
|
+
--buy-pro Always fail (no surprise spend)
|
|
250
|
+
--json --strict
|
|
251
|
+
|
|
252
|
+
Source: ${SOURCE_URL}
|
|
253
|
+
`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function runCli(argv = process.argv.slice(2)) {
|
|
257
|
+
const args = parseCliArgs(argv);
|
|
258
|
+
if (args.help) {
|
|
259
|
+
printHelp();
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
const report = buildColabComputeHonestyReport(args);
|
|
263
|
+
if (args.json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
264
|
+
else process.stdout.write(formatColabComputeHonestyReport(report));
|
|
265
|
+
if (args.strict && report.status !== 'ready') return 1;
|
|
266
|
+
if (report.status === 'fail') return 1;
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
module.exports = {
|
|
271
|
+
SOURCE_URL,
|
|
272
|
+
PLANS,
|
|
273
|
+
RAIL_MAP,
|
|
274
|
+
buildColabComputeHonestyReport,
|
|
275
|
+
formatColabComputeHonestyReport,
|
|
276
|
+
runCli,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
if (path.resolve(process.argv[1] || '') === path.resolve(__filename)) {
|
|
280
|
+
process.exitCode = runCli();
|
|
281
|
+
}
|
package/scripts/gates-engine.js
CHANGED
|
@@ -146,6 +146,10 @@ const BOOSTED_RISK_MIN_EXAMPLES = 3;
|
|
|
146
146
|
const PR_THREAD_RESOLUTION_ACTION = 'pr_thread_resolution_verified_after_commit';
|
|
147
147
|
const HELPER_BYPASS_ACTION = 'helper_script_modified';
|
|
148
148
|
const KNOWLEDGE_ENTROPY_THRESHOLD = 0.7;
|
|
149
|
+
// Issue #3689: do not inject lessons that only barely matched. Retrieval already
|
|
150
|
+
// filters >0.1 for ranking; injection requires a higher bar so low-relevance
|
|
151
|
+
// memories cannot ride along with an entropy disclaimer.
|
|
152
|
+
const MIN_LESSON_INJECTION_RELEVANCE = 0.75;
|
|
149
153
|
// Generous character bound: keeps every affected file for realistic actions while still
|
|
150
154
|
// preventing an unbounded haystack. Chosen over a file-count cap, which dropped targets.
|
|
151
155
|
const MEMORY_GUARD_MAX_SERIALIZED_CHARS = 200000;
|
|
@@ -4452,7 +4456,11 @@ async function buildRelevantLessonContextAsync(toolName, toolInput) {
|
|
|
4452
4456
|
* negative lesson present is relevant enough to surface.
|
|
4453
4457
|
*/
|
|
4454
4458
|
function formatNegativeLessonContext(lessons) {
|
|
4455
|
-
const negative = (lessons || []).filter((l) =>
|
|
4459
|
+
const negative = (lessons || []).filter((l) => {
|
|
4460
|
+
if (l.signal !== 'negative') return false;
|
|
4461
|
+
const score = Number(l.rerankedScore ?? l.relevanceScore ?? 0);
|
|
4462
|
+
return score >= MIN_LESSON_INJECTION_RELEVANCE;
|
|
4463
|
+
});
|
|
4456
4464
|
if (negative.length === 0) return null;
|
|
4457
4465
|
|
|
4458
4466
|
const formatted = negative.map((l) => {
|
|
@@ -4477,8 +4485,10 @@ function isKnowledgeConflictHardBlockAction(toolName, toolInput = {}) {
|
|
|
4477
4485
|
}
|
|
4478
4486
|
|
|
4479
4487
|
function buildKnowledgeConflictContext(toolName, toolInput, lessons, entropy) {
|
|
4480
|
-
|
|
4481
|
-
|
|
4488
|
+
// Issue #3689: high entropy means the scorer disagrees with itself. Do NOT
|
|
4489
|
+
// inject a disclaimer + noisy lessons into unrelated tool calls — suppress.
|
|
4490
|
+
// Strict mode may still hard-block destructive/external side effects.
|
|
4491
|
+
const message = `Knowledge conflict: retrieved lessons disagree for this action (entropy ${entropy}).`;
|
|
4482
4492
|
|
|
4483
4493
|
if (isKnowledgeConflictHardBlockAction(toolName, toolInput)) {
|
|
4484
4494
|
recordStat('retrieval_entropy_high', 'block', null, { toolName, toolInput });
|
|
@@ -4490,8 +4500,9 @@ function buildKnowledgeConflictContext(toolName, toolInput, lessons, entropy) {
|
|
|
4490
4500
|
};
|
|
4491
4501
|
}
|
|
4492
4502
|
|
|
4493
|
-
|
|
4494
|
-
|
|
4503
|
+
// Count as log (not warn): we intentionally did not inject context.
|
|
4504
|
+
recordStat('retrieval_entropy_high', 'log', null, { toolName, toolInput });
|
|
4505
|
+
return null;
|
|
4495
4506
|
}
|
|
4496
4507
|
|
|
4497
4508
|
function extractActionContext(toolName, toolInput) {
|