thumbgate 1.27.11 → 1.27.12
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/config/builtin-lessons.json +23 -0
- package/package.json +1 -1
- package/scripts/gates-engine.js +49 -0
- package/scripts/lesson-search.js +15 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"lessons": [
|
|
4
|
+
{
|
|
5
|
+
"id": "builtin-response-quality-shallow-positive-closeout",
|
|
6
|
+
"title": "MISTAKE: Assistant gave shallow acknowledgement after user said thumbs up",
|
|
7
|
+
"content": "What went wrong: Assistant gave shallow acknowledgement after positive feedback, such as thumbs up, perfect, good, or thank you, instead of staying quiet or giving an evidence checkpoint.\nHow to avoid: If the previous user message is positive feedback and the proposed final response is a low-value social closeout, block it and require either silence-level brevity or a compact evidence checkpoint with proof, result, residual risk, and next state.\nAction needed: Enforce this at the final-response boundary, not only as PreToolUse context.\nReasoning: Stored lessons and retrieval are not enough when the model can still ignore the lesson at generation time.",
|
|
8
|
+
"category": "error",
|
|
9
|
+
"importance": "high",
|
|
10
|
+
"tags": [
|
|
11
|
+
"feedback",
|
|
12
|
+
"negative",
|
|
13
|
+
"response-quality",
|
|
14
|
+
"final-response",
|
|
15
|
+
"positive-feedback",
|
|
16
|
+
"shallow-closeout",
|
|
17
|
+
"enforcement"
|
|
18
|
+
],
|
|
19
|
+
"timestamp": "2026-06-20T19:01:58.000Z",
|
|
20
|
+
"source": "packaged-builtin"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.27.
|
|
3
|
+
"version": "1.27.12",
|
|
4
4
|
"description": "ThumbGate self-improving agent governance: thumbs-up/down turns every mistake into a prevention rule and blocks repeat patterns. 36 pre-action checks, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
|
|
5
5
|
"homepage": "https://thumbgate.ai",
|
|
6
6
|
"repository": {
|
package/scripts/gates-engine.js
CHANGED
|
@@ -16,6 +16,13 @@ const {
|
|
|
16
16
|
const {
|
|
17
17
|
evaluateWorkflowSentinel,
|
|
18
18
|
} = require('./workflow-sentinel');
|
|
19
|
+
const {
|
|
20
|
+
extractPayloadText,
|
|
21
|
+
extractPayloadPreviousUserText,
|
|
22
|
+
hasPositiveFeedback,
|
|
23
|
+
isLowValueCloseout,
|
|
24
|
+
buildResponseQualityReason,
|
|
25
|
+
} = require('./hook-stop-anti-claim');
|
|
19
26
|
const {
|
|
20
27
|
recordDecisionEvaluation,
|
|
21
28
|
recordDecisionOutcome,
|
|
@@ -2585,6 +2592,39 @@ function buildReminderOutput(context) {
|
|
|
2585
2592
|
});
|
|
2586
2593
|
}
|
|
2587
2594
|
|
|
2595
|
+
function inferHookEventName(input = {}) {
|
|
2596
|
+
const explicit = input.hook_event_name || input.hookEventName || input.event || input.lifecycle;
|
|
2597
|
+
if (explicit) return String(explicit);
|
|
2598
|
+
return extractPayloadText(input) ? 'Stop' : 'PreToolUse';
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
function buildResponseQualityBlockOutput(reason, input = {}) {
|
|
2602
|
+
return JSON.stringify({
|
|
2603
|
+
decision: 'block',
|
|
2604
|
+
reason,
|
|
2605
|
+
hookSpecificOutput: {
|
|
2606
|
+
hookEventName: inferHookEventName(input),
|
|
2607
|
+
permissionDecision: 'deny',
|
|
2608
|
+
permissionDecisionReason: reason,
|
|
2609
|
+
},
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
function evaluateFinalResponseQualityGate(input = {}) {
|
|
2614
|
+
const finalText = extractPayloadText(input) || process.env.CLAUDE_RESPONSE || '';
|
|
2615
|
+
const previousUserText = extractPayloadPreviousUserText(input)
|
|
2616
|
+
|| process.env.CLAUDE_PREVIOUS_USER_TEXT
|
|
2617
|
+
|| process.env.CLAUDE_PREVIOUS_USER
|
|
2618
|
+
|| '';
|
|
2619
|
+
|
|
2620
|
+
if (!finalText || !hasPositiveFeedback(previousUserText)) return null;
|
|
2621
|
+
if (!isLowValueCloseout(finalText, '')) return null;
|
|
2622
|
+
recordStat('response-quality-shallow-closeout', 'block', null, {
|
|
2623
|
+
hookEventName: inferHookEventName(input),
|
|
2624
|
+
});
|
|
2625
|
+
return buildResponseQualityBlockOutput(buildResponseQualityReason(), input);
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2588
2628
|
// ---------------------------------------------------------------------------
|
|
2589
2629
|
// Upgrade nudge: surfaces Pro value at usage milestones and trial expiry.
|
|
2590
2630
|
// Block-action Pro CTA: brief upgrade mention after a deny/warn decision.
|
|
@@ -2915,6 +2955,9 @@ function mergeContextStrings(...ctxs) {
|
|
|
2915
2955
|
}
|
|
2916
2956
|
|
|
2917
2957
|
async function runAsync(input) {
|
|
2958
|
+
const responseQualityGate = evaluateFinalResponseQualityGate(input);
|
|
2959
|
+
if (responseQualityGate) return responseQualityGate;
|
|
2960
|
+
|
|
2918
2961
|
const secretGuard = evaluateSecretGuard(input);
|
|
2919
2962
|
if (secretGuard) {
|
|
2920
2963
|
return formatOutput(secretGuard);
|
|
@@ -2962,6 +3005,9 @@ async function runAsync(input) {
|
|
|
2962
3005
|
}
|
|
2963
3006
|
|
|
2964
3007
|
function run(input) {
|
|
3008
|
+
const responseQualityGate = evaluateFinalResponseQualityGate(input);
|
|
3009
|
+
if (responseQualityGate) return responseQualityGate;
|
|
3010
|
+
|
|
2965
3011
|
const secretGuard = evaluateSecretGuard(input);
|
|
2966
3012
|
if (secretGuard) {
|
|
2967
3013
|
return formatOutput(secretGuard);
|
|
@@ -3296,6 +3342,9 @@ module.exports = {
|
|
|
3296
3342
|
evaluateGatesAsync,
|
|
3297
3343
|
computeExecutableHash,
|
|
3298
3344
|
formatOutput,
|
|
3345
|
+
inferHookEventName,
|
|
3346
|
+
buildResponseQualityBlockOutput,
|
|
3347
|
+
evaluateFinalResponseQualityGate,
|
|
3299
3348
|
isApprovalGatesEnabled,
|
|
3300
3349
|
run,
|
|
3301
3350
|
runAsync,
|
package/scripts/lesson-search.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
+
const fs = require('node:fs');
|
|
4
5
|
const { readJSONL, getFeedbackPaths } = require('./feedback-loop');
|
|
5
6
|
const { buildMemoryLifecycleView, scoreHybridMemoryMatch } = require('./agent-memory-lifecycle');
|
|
6
7
|
const { loadOptionalModule } = require('./private-core-boundary');
|
|
@@ -166,6 +167,17 @@ function resolveLessonPaths(options = {}) {
|
|
|
166
167
|
};
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
function readPackagedBuiltinLessons() {
|
|
171
|
+
if (process.env.THUMBGATE_DISABLE_BUILTIN_LESSONS === '1') return [];
|
|
172
|
+
const builtinPath = path.resolve(__dirname, '..', 'config', 'builtin-lessons.json');
|
|
173
|
+
try {
|
|
174
|
+
const parsed = JSON.parse(fs.readFileSync(builtinPath, 'utf8'));
|
|
175
|
+
return Array.isArray(parsed.lessons) ? parsed.lessons : [];
|
|
176
|
+
} catch {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
169
181
|
function readPreventionRuleMatches(queryText, limit = 3, options = {}) {
|
|
170
182
|
const { PREVENTION_RULES_PATH } = resolveLessonPaths(options);
|
|
171
183
|
if (!PREVENTION_RULES_PATH) return [];
|
|
@@ -481,7 +493,9 @@ function searchLessons(query = '', options = {}) {
|
|
|
481
493
|
const sqliteResults = tryFts5Search(query, options);
|
|
482
494
|
if (sqliteResults) return sqliteResults;
|
|
483
495
|
|
|
484
|
-
const
|
|
496
|
+
const localMemories = readJSONL(MEMORY_LOG_PATH);
|
|
497
|
+
const builtinMemories = options.includeBuiltinLessons === false ? [] : readPackagedBuiltinLessons();
|
|
498
|
+
const memories = [...builtinMemories, ...localMemories];
|
|
485
499
|
const feedbackEntries = readJSONL(FEEDBACK_LOG_PATH);
|
|
486
500
|
const feedbackById = new Map(feedbackEntries.map((entry) => [entry.id, entry]));
|
|
487
501
|
const parsedLimit = Number(options.limit || 10);
|