thumbgate 1.27.17 → 1.27.18
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/.claude-plugin/marketplace.json +85 -0
- package/.claude-plugin/plugin.json +3 -4
- package/.well-known/llms.txt +12 -33
- package/.well-known/mcp/server-card.json +8 -8
- package/README.md +30 -221
- package/adapters/chatgpt/openapi.yaml +1695 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/gemini/function-declarations.json +0 -1
- package/adapters/mcp/server-stdio.js +11 -263
- package/adapters/opencode/opencode.json +1 -1
- package/bench/thumbgate-bench.json +2 -2
- package/bin/cli.js +122 -1421
- package/bin/postinstall.js +27 -11
- package/config/gate-templates.json +0 -216
- package/config/gates/claim-verification.json +0 -12
- package/config/gates/default.json +2 -31
- package/config/github-about.json +2 -2
- package/config/mcp-allowlists.json +13 -23
- package/config/merge-quality-checks.json +1 -0
- package/config/model-candidates.json +6 -121
- package/openapi/openapi.yaml +0 -12
- package/package.json +99 -229
- package/public/blog.html +4 -4
- package/public/codex-plugin.html +20 -72
- package/public/compare.html +8 -31
- package/public/dashboard.html +166 -930
- package/public/federal.html +2 -2
- package/public/guide.html +9 -33
- package/public/index.html +116 -495
- package/public/learn.html +18 -191
- package/public/lessons.html +10 -168
- package/public/numbers.html +7 -7
- package/public/pro.html +11 -34
- package/scripts/agent-memory-lifecycle.js +0 -211
- package/scripts/agent-readiness.js +3 -167
- package/scripts/agent-reward-model.js +1 -53
- package/scripts/auto-promote-gates.js +10 -82
- package/scripts/auto-wire-hooks.js +0 -14
- package/scripts/billing.js +1 -93
- package/scripts/build-metadata.js +10 -50
- package/scripts/cli-feedback.js +6 -18
- package/scripts/cli-schema.js +0 -90
- package/scripts/cli-telemetry.js +1 -6
- package/scripts/commercial-offer.js +2 -82
- package/scripts/context-manager.js +6 -74
- package/scripts/dashboard.js +2 -68
- package/scripts/durability/step.js +3 -3
- package/scripts/export-databricks-bundle.js +1 -5
- package/scripts/export-dpo-pairs.js +2 -7
- package/scripts/feedback-loop.js +1 -145
- package/scripts/feedback-quality.js +0 -87
- package/scripts/filesystem-search.js +10 -35
- package/scripts/gate-stats.js +0 -89
- package/scripts/gates-engine.js +85 -1105
- package/scripts/gemini-embedding-policy.js +1 -2
- package/scripts/hook-runtime.js +14 -20
- package/scripts/hook-thumbgate-cache-updater.js +2 -18
- package/scripts/hybrid-feedback-context.js +7 -142
- package/scripts/lesson-inference.js +3 -8
- package/scripts/lesson-search.js +1 -17
- package/scripts/license.js +1 -0
- package/scripts/llm-client.js +9 -390
- package/scripts/local-model-profile.js +8 -15
- package/scripts/memory-scope-readiness.js +0 -159
- package/scripts/meta-agent-loop.js +0 -36
- package/scripts/operational-integrity.js +5 -39
- package/scripts/oss-pr-opportunity-scout.js +5 -35
- package/scripts/plausible-server-events.js +6 -9
- package/scripts/pro-local-dashboard.js +4 -4
- package/scripts/proxy-pointer-rag-guardrails.js +1 -42
- package/scripts/rate-limiter.js +13 -64
- package/scripts/secret-scanner.js +5 -44
- package/scripts/security-scanner.js +10 -260
- package/scripts/self-distill-agent.js +1 -3
- package/scripts/seo-gsd.js +2 -916
- package/scripts/statusline-cache-path.js +2 -17
- package/scripts/statusline-local-stats.js +1 -9
- package/scripts/statusline-meta.js +2 -28
- package/scripts/statusline.sh +4 -20
- package/scripts/telemetry-analytics.js +0 -357
- package/scripts/thompson-sampling.js +10 -31
- package/scripts/thumbgate-bench.js +1 -16
- package/scripts/thumbgate-search.js +19 -85
- package/scripts/tool-registry.js +1 -169
- package/scripts/vector-store.js +0 -45
- package/scripts/workflow-sentinel.js +53 -286
- package/scripts/workspace-evolver.js +2 -62
- package/src/api/server.js +290 -2578
- package/.claude/commands/dashboard.md +0 -15
- package/.claude/commands/thumbgate-blocked.md +0 -27
- package/.claude/commands/thumbgate-dashboard.md +0 -15
- package/.claude/commands/thumbgate-doctor.md +0 -30
- package/.claude/commands/thumbgate-guard.md +0 -36
- package/.claude/commands/thumbgate-protect.md +0 -30
- package/.claude/commands/thumbgate-rules.md +0 -30
- package/.well-known/agentic-verify.txt +0 -1
- package/adapters/gcp/dfcx-webhook-gate.js +0 -295
- package/adapters/letta/README.md +0 -41
- package/adapters/letta/thumbgate-letta-adapter.js +0 -133
- package/adapters/policy-engine/ethicore-guardian-client.js +0 -68
- package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +0 -260
- package/bench/observability-eval-suite.json +0 -26
- package/bin/dashboard-cli.js +0 -7
- package/commands/dashboard.md +0 -15
- package/commands/thumbgate-dashboard.md +0 -15
- package/config/builtin-lessons.json +0 -23
- package/config/gate-classifier-routing.json +0 -98
- package/config/post-deploy-marketing-pages.json +0 -61
- package/public/about.html +0 -162
- package/public/agent-manager.html +0 -179
- package/public/agents-cost-savings.html +0 -153
- package/public/ai-malpractice-prevention.html +0 -818
- package/public/assets/brand/github-social-preview.png +0 -0
- package/public/assets/brand/thumbgate-icon-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
- package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
- package/public/assets/brand/thumbgate-logo-transparent.svg +0 -22
- package/public/assets/brand/thumbgate-mark-inline-v3.svg +0 -19
- package/public/assets/brand/thumbgate-mark-pro.svg +0 -23
- package/public/assets/brand/thumbgate-mark-team.svg +0 -26
- package/public/assets/brand/thumbgate-mark.svg +0 -21
- package/public/assets/brand/thumbgate-wordmark.svg +0 -20
- package/public/assets/claude-thumbgate-statusbar.svg +0 -8
- package/public/assets/codex-thumbgate-statusbar-test.svg +0 -9
- package/public/assets/legal-intake-control-flow.svg +0 -66
- package/public/brand/thumbgate-mark.svg +0 -19
- package/public/brand/thumbgate-og.svg +0 -16
- package/public/chatgpt-app.html +0 -330
- package/public/codex-enterprise.html +0 -123
- package/public/js/buyer-intent.js +0 -672
- package/public/pricing.html +0 -399
- package/scripts/action-receipts.js +0 -324
- package/scripts/activation-quickstart.js +0 -187
- package/scripts/agent-operations-planner.js +0 -621
- package/scripts/ai-component-inventory.js +0 -367
- package/scripts/async-eval-observability.js +0 -236
- package/scripts/audit.js +0 -65
- package/scripts/aws-blocks-guardrails.js +0 -228
- package/scripts/classifier-routing.js +0 -130
- package/scripts/dashboard-chat.js +0 -332
- package/scripts/feedback-aggregate.js +0 -281
- package/scripts/feedback-sanitizer.js +0 -105
- package/scripts/hob-pack.js +0 -591
- package/scripts/hook-stop-anti-claim.js +0 -301
- package/scripts/install-shim.js +0 -87
- package/scripts/mcp-oauth.js +0 -293
- package/scripts/noop-detect.js +0 -285
- package/scripts/omlx-smoke.js +0 -192
- package/scripts/parallel-workflow-orchestrator.js +0 -293
- package/scripts/plan-gate.js +0 -243
- package/scripts/plausible-domain-config.js +0 -99
- package/scripts/qa-scenario-planner.js +0 -136
- package/scripts/repeat-metric.js +0 -137
- package/scripts/secret-fixture-tokens.js +0 -61
- package/scripts/secret-redaction.js +0 -166
- package/scripts/self-harness-optimizer.js +0 -141
- package/scripts/self-healing-check.js +0 -193
- package/scripts/silent-failure-cluster.js +0 -531
- package/scripts/statusline-cache-read.js +0 -57
- package/scripts/sync-telemetry-from-prod.js +0 -374
- package/scripts/tool-contract-validator.js +0 -76
- package/scripts/trajectory-scorer.js +0 -63
- package/scripts/verify-marketing-pages-deployed.js +0 -195
- package/scripts/visitor-journey.js +0 -172
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const fs = require('node:fs');
|
|
5
|
-
const os = require('node:os');
|
|
6
|
-
const path = require('node:path');
|
|
7
|
-
const crypto = require('node:crypto');
|
|
8
|
-
const {
|
|
9
|
-
getFallbackFeedbackDir,
|
|
10
|
-
getGlobalFeedbackDir,
|
|
11
|
-
getHomeDir,
|
|
12
|
-
getLegacyFeedbackDir,
|
|
13
|
-
getThumbgateFeedbackDir,
|
|
14
|
-
resolveFeedbackDir,
|
|
15
|
-
resolveProjectDir,
|
|
16
|
-
} = require('./feedback-paths');
|
|
17
|
-
const { readJsonl } = require('./fs-utils');
|
|
18
|
-
|
|
19
|
-
const FEEDBACK_LOG = 'feedback-log.jsonl';
|
|
20
|
-
const MEMORY_LOG = 'memory-log.jsonl';
|
|
21
|
-
const STATUSLINE_CACHE = 'statusline_cache.json';
|
|
22
|
-
|
|
23
|
-
function truthyDisabled(value) {
|
|
24
|
-
return value === '0' || value === 'false' || value === 'local';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function shouldAggregateFeedback(options = {}) {
|
|
28
|
-
const env = options.env || process.env;
|
|
29
|
-
return !truthyDisabled(String(env.THUMBGATE_STATUSLINE_AGGREGATE || env.THUMBGATE_AGGREGATE_FEEDBACK || '1').toLowerCase());
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function normalizePath(candidate) {
|
|
33
|
-
if (!candidate) return null;
|
|
34
|
-
try {
|
|
35
|
-
return path.resolve(String(candidate));
|
|
36
|
-
} catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function uniquePaths(values = []) {
|
|
42
|
-
const seen = new Set();
|
|
43
|
-
const out = [];
|
|
44
|
-
for (const value of values) {
|
|
45
|
-
const resolved = normalizePath(value);
|
|
46
|
-
if (!resolved || seen.has(resolved)) continue;
|
|
47
|
-
seen.add(resolved);
|
|
48
|
-
out.push(resolved);
|
|
49
|
-
}
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function safeExists(candidate) {
|
|
54
|
-
try {
|
|
55
|
-
return Boolean(candidate && fs.existsSync(candidate));
|
|
56
|
-
} catch {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function immediateChildDirs(parentDir) {
|
|
62
|
-
try {
|
|
63
|
-
return fs.readdirSync(parentDir, { withFileTypes: true })
|
|
64
|
-
.filter((entry) => entry.isDirectory())
|
|
65
|
-
.map((entry) => path.join(parentDir, entry.name));
|
|
66
|
-
} catch {
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function ancestorProjectFeedbackDirs(projectDir, options = {}) {
|
|
72
|
-
const home = normalizePath(getHomeDir(options));
|
|
73
|
-
const start = normalizePath(projectDir);
|
|
74
|
-
if (!start) return [];
|
|
75
|
-
|
|
76
|
-
const dirs = [];
|
|
77
|
-
let cursor = start;
|
|
78
|
-
while (cursor && cursor !== path.dirname(cursor)) {
|
|
79
|
-
dirs.push(
|
|
80
|
-
path.join(cursor, '.thumbgate'),
|
|
81
|
-
path.join(cursor, '.thumbgate-compat'),
|
|
82
|
-
path.join(cursor, '.claude', 'memory', 'feedback')
|
|
83
|
-
);
|
|
84
|
-
if (home && cursor === home) break;
|
|
85
|
-
const next = path.dirname(cursor);
|
|
86
|
-
if (next === cursor) break;
|
|
87
|
-
cursor = next;
|
|
88
|
-
}
|
|
89
|
-
return dirs;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function listFeedbackStoreDirs(options = {}) {
|
|
93
|
-
const env = options.env || process.env;
|
|
94
|
-
const projectDir = resolveProjectDir({ cwd: options.cwd, env, projectDir: options.projectDir });
|
|
95
|
-
const home = getHomeDir({ env });
|
|
96
|
-
const homeThumbgate = path.join(home, '.thumbgate');
|
|
97
|
-
const projectsDir = path.join(homeThumbgate, 'projects');
|
|
98
|
-
const explicitRoots = String(env.THUMBGATE_AGGREGATE_ROOTS || '')
|
|
99
|
-
.split(path.delimiter)
|
|
100
|
-
.map((value) => value.trim())
|
|
101
|
-
.filter(Boolean);
|
|
102
|
-
const explicitFeedbackDir = options.feedbackDir || env.THUMBGATE_FEEDBACK_DIR;
|
|
103
|
-
const normalizedExplicitFeedbackDir = normalizePath(explicitFeedbackDir);
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
normalizedExplicitFeedbackDir &&
|
|
107
|
-
normalizedExplicitFeedbackDir.startsWith(normalizePath(os.tmpdir()) + path.sep) &&
|
|
108
|
-
explicitRoots.length === 0
|
|
109
|
-
) {
|
|
110
|
-
return uniquePaths([explicitFeedbackDir])
|
|
111
|
-
.filter((dir) => safeExists(path.join(dir, FEEDBACK_LOG)) || safeExists(path.join(dir, MEMORY_LOG)) || safeExists(path.join(dir, STATUSLINE_CACHE)));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return uniquePaths([
|
|
115
|
-
explicitFeedbackDir,
|
|
116
|
-
resolveFeedbackDir({ projectDir, env }),
|
|
117
|
-
getThumbgateFeedbackDir({ projectDir, env }),
|
|
118
|
-
getFallbackFeedbackDir({ projectDir, env }),
|
|
119
|
-
getLegacyFeedbackDir({ projectDir, env }),
|
|
120
|
-
getGlobalFeedbackDir({ projectDir, env }),
|
|
121
|
-
homeThumbgate,
|
|
122
|
-
...immediateChildDirs(projectsDir),
|
|
123
|
-
...ancestorProjectFeedbackDirs(projectDir, { env }),
|
|
124
|
-
...explicitRoots,
|
|
125
|
-
]).filter((dir) => safeExists(path.join(dir, FEEDBACK_LOG)) || safeExists(path.join(dir, MEMORY_LOG)) || safeExists(path.join(dir, STATUSLINE_CACHE)));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function normalizeSignal(signal) {
|
|
129
|
-
const raw = String(signal || '').toLowerCase();
|
|
130
|
-
if (raw === 'positive' || raw === 'up' || raw === 'thumbs_up') return 'positive';
|
|
131
|
-
if (raw === 'negative' || raw === 'down' || raw === 'thumbs_down') return 'negative';
|
|
132
|
-
return raw || null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function stableEntryKey(entry = {}, source = {}) {
|
|
136
|
-
const id = entry.id || entry.feedbackId || entry.sourceFeedbackId;
|
|
137
|
-
if (id) return `id:${id}`;
|
|
138
|
-
const material = JSON.stringify({
|
|
139
|
-
sourcePath: source.logPath || '',
|
|
140
|
-
sourceIndex: Number.isFinite(source.index) ? source.index : -1,
|
|
141
|
-
entry,
|
|
142
|
-
});
|
|
143
|
-
return `sha:${crypto.createHash('sha256').update(material).digest('hex')}`;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function sortByTimestamp(entries) {
|
|
147
|
-
return entries.sort((a, b) => {
|
|
148
|
-
const at = a.timestamp ? Date.parse(a.timestamp) : 0;
|
|
149
|
-
const bt = b.timestamp ? Date.parse(b.timestamp) : 0;
|
|
150
|
-
return (Number.isFinite(at) ? at : 0) - (Number.isFinite(bt) ? bt : 0);
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function collectAggregateLogEntries(fileName, options = {}) {
|
|
155
|
-
const stores = listFeedbackStoreDirs(options);
|
|
156
|
-
const seen = new Set();
|
|
157
|
-
const entries = [];
|
|
158
|
-
|
|
159
|
-
for (const dir of stores) {
|
|
160
|
-
const logPath = path.join(dir, fileName);
|
|
161
|
-
if (!safeExists(logPath)) continue;
|
|
162
|
-
const rows = readJsonl(logPath, { maxLines: 0 }) || [];
|
|
163
|
-
for (let index = 0; index < rows.length; index += 1) {
|
|
164
|
-
const rawEntry = rows[index];
|
|
165
|
-
const entry = { ...rawEntry };
|
|
166
|
-
if (entry.signal || entry.feedback) entry.signal = normalizeSignal(entry.signal || entry.feedback);
|
|
167
|
-
const key = stableEntryKey(entry, { logPath, index });
|
|
168
|
-
if (seen.has(key)) continue;
|
|
169
|
-
seen.add(key);
|
|
170
|
-
entries.push({ ...entry, sourceFeedbackDir: dir });
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
entries: sortByTimestamp(entries),
|
|
176
|
-
stores,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function summarizeFeedbackEntries(entries) {
|
|
181
|
-
let totalPositive = 0;
|
|
182
|
-
let totalNegative = 0;
|
|
183
|
-
let rubricSamples = 0;
|
|
184
|
-
|
|
185
|
-
for (const entry of entries) {
|
|
186
|
-
if (entry.signal === 'positive') totalPositive += 1;
|
|
187
|
-
if (entry.signal === 'negative') totalNegative += 1;
|
|
188
|
-
if (entry.rubric && entry.rubric.weightedScore != null) rubricSamples += 1;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return { totalPositive, totalNegative, rubricSamples };
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function createRateWindows(total, totalPositive, approvalRate) {
|
|
195
|
-
return {
|
|
196
|
-
'7d': { total: 0, positive: 0, rate: 0 },
|
|
197
|
-
'30d': { total: 0, positive: 0, rate: 0 },
|
|
198
|
-
lifetime: { total, positive: totalPositive, rate: approvalRate },
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function applyEntryToRateWindow(window, entry) {
|
|
203
|
-
window.total += 1;
|
|
204
|
-
if (entry.signal === 'positive') window.positive += 1;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function updateRateWindows(windows, entries, now = Date.now()) {
|
|
208
|
-
const sevenDays = 7 * 24 * 60 * 60 * 1000;
|
|
209
|
-
const thirtyDays = 30 * 24 * 60 * 60 * 1000;
|
|
210
|
-
|
|
211
|
-
for (const entry of entries) {
|
|
212
|
-
const ts = entry.timestamp ? Date.parse(entry.timestamp) : Number.NaN;
|
|
213
|
-
if (!Number.isFinite(ts)) continue;
|
|
214
|
-
const age = now - ts;
|
|
215
|
-
if (age <= sevenDays) applyEntryToRateWindow(windows['7d'], entry);
|
|
216
|
-
if (age <= thirtyDays) applyEntryToRateWindow(windows['30d'], entry);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function finalizeRateWindows(windows) {
|
|
221
|
-
for (const key of ['7d', '30d']) {
|
|
222
|
-
const window = windows[key];
|
|
223
|
-
window.rate = 0;
|
|
224
|
-
if (window.total > 0) {
|
|
225
|
-
window.rate = Math.round((window.positive / window.total) * 1000) / 1000;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function trendFromRateWindows(windows) {
|
|
231
|
-
const hasTrendData = windows['7d'].total > 0 && windows['30d'].total > 0;
|
|
232
|
-
if (hasTrendData) {
|
|
233
|
-
if (windows['7d'].rate > windows['30d'].rate + 0.05) return 'improving';
|
|
234
|
-
if (windows['7d'].rate < windows['30d'].rate - 0.05) return 'degrading';
|
|
235
|
-
}
|
|
236
|
-
return 'stable';
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function computeAggregateFeedbackStats(options = {}) {
|
|
240
|
-
const { entries, stores } = collectAggregateLogEntries(FEEDBACK_LOG, options);
|
|
241
|
-
const memory = collectAggregateLogEntries(MEMORY_LOG, options);
|
|
242
|
-
const { totalPositive, totalNegative, rubricSamples } = summarizeFeedbackEntries(entries);
|
|
243
|
-
const total = totalPositive + totalNegative;
|
|
244
|
-
const approvalRate = total > 0 ? Math.round((totalPositive / total) * 1000) / 1000 : 0;
|
|
245
|
-
const windows = createRateWindows(total, totalPositive, approvalRate);
|
|
246
|
-
updateRateWindows(windows, entries);
|
|
247
|
-
finalizeRateWindows(windows);
|
|
248
|
-
const trend = trendFromRateWindows(windows);
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
total,
|
|
252
|
-
totalPositive,
|
|
253
|
-
totalNegative,
|
|
254
|
-
approvalRate,
|
|
255
|
-
recentRate: windows['7d'].rate || approvalRate,
|
|
256
|
-
trend,
|
|
257
|
-
windows,
|
|
258
|
-
rubric: { samples: rubricSamples || memory.entries.length, blockedPromotions: 0, failingCriteria: {} },
|
|
259
|
-
aggregate: {
|
|
260
|
-
enabled: true,
|
|
261
|
-
stores: stores.length,
|
|
262
|
-
feedbackLogPaths: stores.map((dir) => path.join(dir, FEEDBACK_LOG)),
|
|
263
|
-
memoryStores: memory.stores.length,
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function getAggregateStatuslineCachePath(options = {}) {
|
|
269
|
-
const env = options.env || process.env;
|
|
270
|
-
if (env.THUMBGATE_STATUSLINE_CACHE) return path.resolve(env.THUMBGATE_STATUSLINE_CACHE);
|
|
271
|
-
return path.join(getHomeDir({ env }), '.thumbgate', STATUSLINE_CACHE);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
module.exports = {
|
|
275
|
-
collectAggregateLogEntries,
|
|
276
|
-
computeAggregateFeedbackStats,
|
|
277
|
-
getAggregateStatuslineCachePath,
|
|
278
|
-
listFeedbackStoreDirs,
|
|
279
|
-
normalizeSignal,
|
|
280
|
-
shouldAggregateFeedback,
|
|
281
|
-
};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const crypto = require('crypto');
|
|
4
|
-
|
|
5
|
-
const TRANSPORT_KEYS = new Set([
|
|
6
|
-
'hookeventname',
|
|
7
|
-
'hook_event_name',
|
|
8
|
-
'sessionid',
|
|
9
|
-
'session_id',
|
|
10
|
-
'transcriptpath',
|
|
11
|
-
'transcript_path',
|
|
12
|
-
'timestamp',
|
|
13
|
-
'createdat',
|
|
14
|
-
'created_at',
|
|
15
|
-
'updatedat',
|
|
16
|
-
'updated_at',
|
|
17
|
-
'cwd',
|
|
18
|
-
'pid',
|
|
19
|
-
'processid',
|
|
20
|
-
'process_id',
|
|
21
|
-
'promptid',
|
|
22
|
-
'prompt_id',
|
|
23
|
-
'traceid',
|
|
24
|
-
'trace_id',
|
|
25
|
-
'requestid',
|
|
26
|
-
'request_id',
|
|
27
|
-
'installid',
|
|
28
|
-
'install_id',
|
|
29
|
-
'visitorsessionid',
|
|
30
|
-
'visitor_session_id',
|
|
31
|
-
'toolinput',
|
|
32
|
-
'tool_input',
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
|
-
const TRANSPORT_WORDS = new Set([
|
|
36
|
-
...TRANSPORT_KEYS,
|
|
37
|
-
'hook',
|
|
38
|
-
'event',
|
|
39
|
-
'userpromptsubmit',
|
|
40
|
-
'user_prompt_submit',
|
|
41
|
-
'pretooluse',
|
|
42
|
-
'pre_tool_use',
|
|
43
|
-
'posttooluse',
|
|
44
|
-
'post_tool_use',
|
|
45
|
-
'claude',
|
|
46
|
-
'codex',
|
|
47
|
-
'projects',
|
|
48
|
-
'redacted',
|
|
49
|
-
'tmp',
|
|
50
|
-
'private',
|
|
51
|
-
'folders',
|
|
52
|
-
'json',
|
|
53
|
-
]);
|
|
54
|
-
|
|
55
|
-
function stripEphemeralText(text) {
|
|
56
|
-
if (!text || typeof text !== 'string') return '';
|
|
57
|
-
return String(text)
|
|
58
|
-
.replace(/["']?(?:hook_?event_?name|session_?id|transcript_?path|timestamp|created_?at|updated_?at|cwd|pid|process_?id|prompt_?id|trace_?id|request_?id|install_?id|visitor_?session_?id)["']?\s*[:=]\s*["']?[^"',}\]\s]+["']?/gi, ' ')
|
|
59
|
-
.replace(/\/(?:private\/)?tmp\/[^\s"',}\]]+/gi, ' ')
|
|
60
|
-
.replace(/\/var\/folders\/[^\s"',}\]]+/gi, ' ')
|
|
61
|
-
.replace(/\/Users\/[^/\s]+\/\.(?:claude|codex|thumbgate)\/[^\s"',}\]]+/gi, ' ')
|
|
62
|
-
.replace(/\/Users\/[^/\s]+\/\.config\/thumbgate\/[^\s"',}\]]+/gi, ' ')
|
|
63
|
-
.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, ' ')
|
|
64
|
-
.replace(/\b[0-9a-f]{24,}\b/gi, ' ')
|
|
65
|
-
.replace(/\b\d{4}-\d{2}-\d{2}t\d{2}:\d{2}:\d{2}(?:\.\d+)?z?\b/gi, ' ')
|
|
66
|
-
.replace(/\b\d{10,13}\b/g, ' ')
|
|
67
|
-
.replace(/:\d{4,5}\b/g, ':PORT');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function transportWordsOnly(text) {
|
|
71
|
-
const tokens = String(text || '')
|
|
72
|
-
.toLowerCase()
|
|
73
|
-
.replace(/[^a-z0-9_ -]/g, ' ')
|
|
74
|
-
.split(/\s+/)
|
|
75
|
-
.filter((token) => token.length >= 3);
|
|
76
|
-
if (tokens.length === 0) return true;
|
|
77
|
-
return tokens.every((token) => TRANSPORT_WORDS.has(token));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function sanitizeFeedbackText(text) {
|
|
81
|
-
const stripped = stripEphemeralText(text)
|
|
82
|
-
.replace(/\/Users\/[^\s/]+/g, '/Users/redacted')
|
|
83
|
-
.replace(/\s+/g, ' ')
|
|
84
|
-
.trim();
|
|
85
|
-
if (transportWordsOnly(stripped)) return '';
|
|
86
|
-
return stripped;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function actionFingerprint(parts) {
|
|
90
|
-
const raw = Array.isArray(parts) ? parts.join(' ') : String(parts || '');
|
|
91
|
-
const stable = sanitizeFeedbackText(raw)
|
|
92
|
-
.toLowerCase()
|
|
93
|
-
.replace(/[^a-z0-9._:/ -]/g, ' ')
|
|
94
|
-
.replace(/\s+/g, ' ')
|
|
95
|
-
.trim();
|
|
96
|
-
if (!stable || transportWordsOnly(stable)) return null;
|
|
97
|
-
return crypto.createHash('sha256').update(stable).digest('hex').slice(0, 16);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
module.exports = {
|
|
101
|
-
TRANSPORT_WORDS,
|
|
102
|
-
sanitizeFeedbackText,
|
|
103
|
-
actionFingerprint,
|
|
104
|
-
transportWordsOnly,
|
|
105
|
-
};
|