thumbgate 1.7.0 → 1.8.0
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/adapters/README.md +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +50 -1
- package/adapters/opencode/opencode.json +1 -1
- package/config/mcp-allowlists.json +10 -1
- package/package.json +11 -6
- package/public/index.html +2 -2
- package/scripts/autonomous-workflow.js +377 -0
- package/scripts/billing.js +4 -2
- package/scripts/mailer/resend-mailer.js +210 -40
- package/scripts/statusline-context.js +207 -0
- package/scripts/statusline.sh +31 -14
- package/scripts/tool-registry.js +39 -0
- package/CHANGELOG.md +0 -702
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate-marketplace",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"owner": {
|
|
5
5
|
"name": "Igor Ganapolsky",
|
|
6
6
|
"email": "ig5973700@gmail.com"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"source": "npm",
|
|
14
14
|
"package": "thumbgate"
|
|
15
15
|
},
|
|
16
|
-
"version": "1.
|
|
16
|
+
"version": "1.8.0",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Igor Ganapolsky"
|
|
19
19
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
3
|
"description": "Type 👍 or 👎 on any agent action. ThumbGate captures it, distills a lesson, and blocks the pattern from repeating. One thumbs-down = the agent physically cannot make that mistake again. 33 pre-action gates, budget enforcement, self-protection, and NIST/SOC2 compliance tags.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.8.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igor Ganapolsky"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "ThumbGate — 👍👎 feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
|
|
5
5
|
"homepage": "https://github.com/IgorGanapolsky/thumbgate",
|
|
6
6
|
"transport": "stdio",
|
package/adapters/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- `chatgpt/openapi.yaml`: import into GPT Actions.
|
|
4
4
|
- `gemini/function-declarations.json`: Gemini function-calling definitions.
|
|
5
5
|
- `mcp/server-stdio.js`: underlying local MCP stdio server implementation.
|
|
6
|
-
- `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.
|
|
6
|
+
- `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.8.0 thumbgate serve`.
|
|
7
7
|
- `codex/config.toml`: example Codex MCP profile section using the same version-pinned portable launcher.
|
|
8
8
|
- `amp/skills/thumbgate-feedback/SKILL.md`: Amp skill template.
|
|
9
9
|
- `opencode/opencode.json`: portable OpenCode MCP profile using the same version-pinned portable launcher.
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"thumbgate": {
|
|
4
4
|
"command": "npx",
|
|
5
|
-
"args": ["--yes", "--package", "thumbgate@1.
|
|
5
|
+
"args": ["--yes", "--package", "thumbgate@1.8.0", "thumbgate", "serve"]
|
|
6
6
|
}
|
|
7
7
|
},
|
|
8
8
|
"hooks": {
|
|
9
9
|
"preToolUse": {
|
|
10
10
|
"command": "npx",
|
|
11
|
-
"args": ["--yes", "--package", "thumbgate@1.
|
|
11
|
+
"args": ["--yes", "--package", "thumbgate@1.8.0", "thumbgate", "gate-check"]
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -125,6 +125,8 @@ const {
|
|
|
125
125
|
formatUnifiedContext,
|
|
126
126
|
} = require('../../scripts/context-manager');
|
|
127
127
|
const { exportHfDataset } = require('../../scripts/export-hf-dataset');
|
|
128
|
+
const { distributeContextToAgents } = require('../../scripts/swarm-coordinator');
|
|
129
|
+
const { buildSessionReport } = require('../../scripts/session-report');
|
|
128
130
|
|
|
129
131
|
const PRO_CHECKOUT_URL = 'https://thumbgate-production.up.railway.app/checkout/pro';
|
|
130
132
|
|
|
@@ -146,7 +148,7 @@ const {
|
|
|
146
148
|
finalizeSession: finalizeFeedbackSession,
|
|
147
149
|
} = require('../../scripts/feedback-session');
|
|
148
150
|
|
|
149
|
-
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.
|
|
151
|
+
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.8.0' };
|
|
150
152
|
const COMMERCE_CATEGORIES = [
|
|
151
153
|
'product_recommendation',
|
|
152
154
|
'brand_compliance',
|
|
@@ -736,6 +738,53 @@ async function callToolInner(name, args) {
|
|
|
736
738
|
}
|
|
737
739
|
case 'verify_claim':
|
|
738
740
|
return toTextResult(verifyClaimEvidence(args.claim));
|
|
741
|
+
case 'require_evidence_for_claim': {
|
|
742
|
+
if (!args.claim || typeof args.claim !== 'string') {
|
|
743
|
+
throw new Error('claim is required and must be a string');
|
|
744
|
+
}
|
|
745
|
+
const verification = verifyClaimEvidence(args.claim);
|
|
746
|
+
const mode = args.mode === 'advisory' ? 'advisory' : 'blocking';
|
|
747
|
+
const hasMatchingChecks = Array.isArray(verification.checks) && verification.checks.length > 0;
|
|
748
|
+
const evidenceMissing = hasMatchingChecks && !verification.verified;
|
|
749
|
+
const blocking = mode === 'blocking' && evidenceMissing;
|
|
750
|
+
const missingActions = hasMatchingChecks
|
|
751
|
+
? Array.from(new Set(verification.checks.flatMap((check) => check.missing || [])))
|
|
752
|
+
: [];
|
|
753
|
+
try {
|
|
754
|
+
const { recordAuditEvent } = require('../../scripts/audit-trail');
|
|
755
|
+
recordAuditEvent({
|
|
756
|
+
toolName: 'require_evidence_for_claim',
|
|
757
|
+
toolInput: { claim: args.claim, mode, sessionId: args.sessionId || null },
|
|
758
|
+
decision: blocking ? 'deny' : 'allow',
|
|
759
|
+
gateId: 'completion_claim',
|
|
760
|
+
message: blocking
|
|
761
|
+
? `Completion claim blocked — missing evidence: ${missingActions.join(', ') || 'unknown'}`
|
|
762
|
+
: `Completion claim verified (${verification.verified ? 'evidence present' : 'no matching gate'})`,
|
|
763
|
+
source: 'completion-gate',
|
|
764
|
+
});
|
|
765
|
+
} catch { /* audit write must never break tool response */ }
|
|
766
|
+
return toTextResult({
|
|
767
|
+
claim: args.claim,
|
|
768
|
+
mode,
|
|
769
|
+
blocking,
|
|
770
|
+
verified: verification.verified,
|
|
771
|
+
matchedChecks: hasMatchingChecks,
|
|
772
|
+
missingActions,
|
|
773
|
+
checks: verification.checks,
|
|
774
|
+
sessionId: args.sessionId || null,
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
case 'distribute_context_to_agents':
|
|
778
|
+
return toTextResult(distributeContextToAgents({
|
|
779
|
+
query: args.query || '',
|
|
780
|
+
agents: args.agents,
|
|
781
|
+
maxItems: args.maxItems,
|
|
782
|
+
maxChars: args.maxChars,
|
|
783
|
+
namespaces: Array.isArray(args.namespaces) ? args.namespaces : [],
|
|
784
|
+
ttlMs: args.ttlMs,
|
|
785
|
+
}));
|
|
786
|
+
case 'session_report':
|
|
787
|
+
return toTextResult(buildSessionReport({ windowHours: args.windowHours }));
|
|
739
788
|
case 'check_operational_integrity':
|
|
740
789
|
return toTextResult(evaluateOperationalIntegrity({
|
|
741
790
|
repoPath: args.repoPath,
|
|
@@ -52,6 +52,9 @@
|
|
|
52
52
|
"get_reliability_rules",
|
|
53
53
|
"describe_reliability_entity",
|
|
54
54
|
"report_product_issue",
|
|
55
|
+
"require_evidence_for_claim",
|
|
56
|
+
"distribute_context_to_agents",
|
|
57
|
+
"session_report",
|
|
55
58
|
"perplexity_search",
|
|
56
59
|
"perplexity_ask",
|
|
57
60
|
"perplexity_research",
|
|
@@ -81,7 +84,9 @@
|
|
|
81
84
|
"feedback_stats",
|
|
82
85
|
"feedback_summary",
|
|
83
86
|
"estimate_uncertainty",
|
|
84
|
-
"report_product_issue"
|
|
87
|
+
"report_product_issue",
|
|
88
|
+
"require_evidence_for_claim",
|
|
89
|
+
"session_report"
|
|
85
90
|
],
|
|
86
91
|
"commerce": [
|
|
87
92
|
"capture_feedback",
|
|
@@ -129,6 +134,8 @@
|
|
|
129
134
|
"describe_semantic_entity",
|
|
130
135
|
"get_reliability_rules",
|
|
131
136
|
"describe_reliability_entity",
|
|
137
|
+
"require_evidence_for_claim",
|
|
138
|
+
"session_report",
|
|
132
139
|
"perplexity_search",
|
|
133
140
|
"perplexity_ask"
|
|
134
141
|
],
|
|
@@ -158,6 +165,8 @@
|
|
|
158
165
|
"describe_semantic_entity",
|
|
159
166
|
"get_reliability_rules",
|
|
160
167
|
"describe_reliability_entity",
|
|
168
|
+
"require_evidence_for_claim",
|
|
169
|
+
"session_report",
|
|
161
170
|
"perplexity_search",
|
|
162
171
|
"perplexity_ask"
|
|
163
172
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Self-improving agent governance: type thumbs-up or thumbs-down on any AI agent action. ThumbGate turns every mistake into a prevention rule and blocks the pattern from repeating. One thumbs-down, never again. 33 pre-action gates, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
|
|
5
5
|
"homepage": "https://thumbgate-production.up.railway.app",
|
|
6
6
|
"repository": {
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
".claude-plugin/marketplace.json",
|
|
20
20
|
".claude-plugin/plugin.json",
|
|
21
21
|
".well-known/",
|
|
22
|
-
"CHANGELOG.md",
|
|
23
22
|
"LICENSE",
|
|
24
23
|
"README.md",
|
|
25
24
|
"adapters/amp/skills/thumbgate-feedback/SKILL.md",
|
|
@@ -49,6 +48,7 @@
|
|
|
49
48
|
"scripts/agentic-data-pipeline.js",
|
|
50
49
|
"scripts/analytics-report.js",
|
|
51
50
|
"scripts/analytics-window.js",
|
|
51
|
+
"scripts/autonomous-workflow.js",
|
|
52
52
|
"scripts/async-job-runner.js",
|
|
53
53
|
"scripts/audit-trail.js",
|
|
54
54
|
"scripts/auto-promote-gates.js",
|
|
@@ -173,6 +173,7 @@
|
|
|
173
173
|
"scripts/slo-alert-engine.js",
|
|
174
174
|
"scripts/spec-gate.js",
|
|
175
175
|
"scripts/statusline-cache-path.js",
|
|
176
|
+
"scripts/statusline-context.js",
|
|
176
177
|
"scripts/statusline-lesson.js",
|
|
177
178
|
"scripts/statusline-links.js",
|
|
178
179
|
"scripts/statusline-local-stats.js",
|
|
@@ -251,7 +252,10 @@
|
|
|
251
252
|
"trace:eval": "node scripts/decision-trace.js eval",
|
|
252
253
|
"social:reply-monitor": "node scripts/social-reply-monitor.js",
|
|
253
254
|
"social:reply-monitor:dry": "node scripts/social-reply-monitor.js --dry-run",
|
|
254
|
-
"test": "npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:zernio && npm run test:platform-limits && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:check-congruence && npm run test:tool-registry && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:plan-gate && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:bot-detector && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-bot-guard && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:prompt-eval && npm run test:demo-voiceover && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity && npm run test:token-savings-dashboard && npm run test:cursor-wiring && npm run test:pretooluse-injection && npm run test:recent-corrective-context && npm run test:durability-step && npm run test:mailer && npm run test:brand-assets && npm run test:enforcement-teeth",
|
|
255
|
+
"test": "npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:zernio && npm run test:platform-limits && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:check-congruence && npm run test:tool-registry && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:plan-gate && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:bot-detector && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-bot-guard && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:prompt-eval && npm run test:demo-voiceover && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity && npm run test:token-savings-dashboard && npm run test:cursor-wiring && npm run test:pretooluse-injection && npm run test:recent-corrective-context && npm run test:durability-step && npm run test:mailer && npm run test:brand-assets && npm run test:enforcement-teeth && npm run test:swarm-coordinator && npm run test:session-report && npm run test:require-evidence-gate",
|
|
256
|
+
"test:swarm-coordinator": "node --test tests/swarm-coordinator.test.js",
|
|
257
|
+
"test:session-report": "node --test tests/session-report.test.js",
|
|
258
|
+
"test:require-evidence-gate": "node --test tests/require-evidence-gate.test.js",
|
|
255
259
|
"test:session-health": "node --test tests/session-health-sensor.test.js",
|
|
256
260
|
"test:session-episodes": "node --test tests/session-episode-store.test.js",
|
|
257
261
|
"test:spec-gate": "node --test tests/spec-gate.test.js",
|
|
@@ -265,7 +269,7 @@
|
|
|
265
269
|
"test:multi-hop-recall": "node --test tests/multi-hop-recall.test.js",
|
|
266
270
|
"test:synthetic-dpo": "node --test tests/synthetic-dpo.test.js",
|
|
267
271
|
"test:thumbgate-skill": "node --test tests/thumbgate-skill.test.js",
|
|
268
|
-
"test:statusline": "node --test tests/claude-feedback-sync.test.js tests/statusline.test.js tests/statusline-links.test.js",
|
|
272
|
+
"test:statusline": "node --test tests/claude-feedback-sync.test.js tests/statusline.test.js tests/statusline-context.test.js tests/statusline-links.test.js",
|
|
269
273
|
"test:memory-dedup": "node --test tests/memory-dedup.test.js",
|
|
270
274
|
"test:lesson-db": "node --test tests/lesson-db.test.js",
|
|
271
275
|
"test:lesson-rotation": "node --test tests/lesson-rotation.test.js",
|
|
@@ -483,10 +487,11 @@
|
|
|
483
487
|
"test:demo-voiceover": "node --test tests/demo-voiceover.test.js",
|
|
484
488
|
"test:gate-coherence": "node --test tests/gate-coherence.test.js",
|
|
485
489
|
"test:gate-eval": "node --test tests/gate-eval.test.js",
|
|
486
|
-
"test:high-roi": "node --test tests/high-roi.test.js",
|
|
490
|
+
"test:high-roi": "node --test tests/high-roi.test.js tests/autonomous-workflow.test.js",
|
|
487
491
|
"test:public-static-assets": "node --test tests/public-static-assets.test.js",
|
|
488
492
|
"test:token-savings": "node --test tests/token-savings.test.js",
|
|
489
|
-
"test:workflow-gate-checkpoint": "node --test tests/workflow-gate-checkpoint.test.js",
|
|
493
|
+
"test:workflow-gate-checkpoint": "node --test tests/workflow-gate-checkpoint.test.js tests/autonomous-workflow.test.js",
|
|
494
|
+
"workflow:autonomous": "node scripts/autonomous-workflow.js",
|
|
490
495
|
"test:lesson-export-import": "node --test tests/lesson-export-import.test.js",
|
|
491
496
|
"test:landing-page-claims": "node --test tests/landing-page-claims.test.js",
|
|
492
497
|
"test:dashboard-deeplink-e2e": "node --test tests/dashboard-deeplink-e2e.test.js",
|
package/public/index.html
CHANGED
|
@@ -974,7 +974,7 @@ __GA_BOOTSTRAP__
|
|
|
974
974
|
<!-- HOW IT WORKS -->
|
|
975
975
|
<section class="how-it-works" id="how-it-works">
|
|
976
976
|
<div class="container">
|
|
977
|
-
<div class="section-label">New in v1.
|
|
977
|
+
<div class="section-label">New in v1.8.0</div>
|
|
978
978
|
<h2 class="section-title">Three steps to stop repeated AI failures</h2>
|
|
979
979
|
<div class="steps">
|
|
980
980
|
<div class="step">
|
|
@@ -1330,7 +1330,7 @@ __GA_BOOTSTRAP__
|
|
|
1330
1330
|
<a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
|
|
1331
1331
|
<a href="/blog">Blog</a>
|
|
1332
1332
|
</div>
|
|
1333
|
-
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.
|
|
1333
|
+
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.8.0</span>
|
|
1334
1334
|
</div>
|
|
1335
1335
|
</footer>
|
|
1336
1336
|
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
const { ensureDir } = require('./fs-utils');
|
|
8
|
+
const {
|
|
9
|
+
executeJob,
|
|
10
|
+
readJobState,
|
|
11
|
+
resumeJob,
|
|
12
|
+
} = require('./async-job-runner');
|
|
13
|
+
const {
|
|
14
|
+
createCheckpoint,
|
|
15
|
+
advanceCheckpoint,
|
|
16
|
+
loadCheckpoint,
|
|
17
|
+
saveCheckpoint,
|
|
18
|
+
} = require('./workflow-gate-checkpoint');
|
|
19
|
+
const { appendWorkflowRun } = require('./workflow-runs');
|
|
20
|
+
|
|
21
|
+
function normalizeText(value) {
|
|
22
|
+
if (value === undefined || value === null) return '';
|
|
23
|
+
return String(value).trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function slugify(value, fallback = 'workflow') {
|
|
27
|
+
// Avoid any `-+` quantifier in an edge-anchored regex (Sonar javascript:S5852
|
|
28
|
+
// still flags even the anchored form). Strip edge dashes with a linear scan.
|
|
29
|
+
const collapsed = normalizeText(value).toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
30
|
+
let start = 0;
|
|
31
|
+
let end = collapsed.length;
|
|
32
|
+
while (start < end && collapsed.charCodeAt(start) === 45) start += 1;
|
|
33
|
+
while (end > start && collapsed.charCodeAt(end - 1) === 45) end -= 1;
|
|
34
|
+
const normalized = collapsed.slice(start, end);
|
|
35
|
+
return normalized || fallback;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getWorkflowPaths(workflowId, cwd = process.cwd()) {
|
|
39
|
+
const rootDir = path.join(cwd, '.thumbgate', 'autonomous-workflows', workflowId);
|
|
40
|
+
return {
|
|
41
|
+
rootDir,
|
|
42
|
+
checkpointPath: path.join(rootDir, 'checkpoint.json'),
|
|
43
|
+
reportJsonPath: path.join(rootDir, 'report.json'),
|
|
44
|
+
reportMdPath: path.join(rootDir, 'report.md'),
|
|
45
|
+
planPath: path.join(rootDir, 'plan.json'),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizePlan(input, workflowId) {
|
|
50
|
+
if (Array.isArray(input)) {
|
|
51
|
+
return {
|
|
52
|
+
workflowId,
|
|
53
|
+
summary: input.map((step) => normalizeText(step)).filter(Boolean).join(' | ') || 'Execution plan ready',
|
|
54
|
+
steps: input
|
|
55
|
+
.map((step, index) => ({
|
|
56
|
+
id: `step_${index + 1}`,
|
|
57
|
+
description: normalizeText(step),
|
|
58
|
+
}))
|
|
59
|
+
.filter((step) => step.description),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (input && typeof input === 'object') {
|
|
64
|
+
const steps = Array.isArray(input.steps)
|
|
65
|
+
? input.steps
|
|
66
|
+
.map((step, index) => {
|
|
67
|
+
if (typeof step === 'string') {
|
|
68
|
+
return {
|
|
69
|
+
id: `step_${index + 1}`,
|
|
70
|
+
description: normalizeText(step),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (step && typeof step === 'object') {
|
|
75
|
+
return {
|
|
76
|
+
id: normalizeText(step.id) || `step_${index + 1}`,
|
|
77
|
+
description: normalizeText(step.description || step.summary || step.name),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null;
|
|
82
|
+
})
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
: [];
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
workflowId,
|
|
88
|
+
summary: normalizeText(input.summary) || steps.map((step) => step.description).join(' | ') || 'Execution plan ready',
|
|
89
|
+
steps,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const summary = normalizeText(input) || 'Execution plan ready';
|
|
94
|
+
return {
|
|
95
|
+
workflowId,
|
|
96
|
+
summary,
|
|
97
|
+
steps: summary ? [{ id: 'step_1', description: summary }] : [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildDefaultPlan(spec, workflowId) {
|
|
102
|
+
const executionSteps = Array.isArray(spec.stages)
|
|
103
|
+
? spec.stages.map((stage, index) => normalizeText(stage && (stage.name || stage.context || stage.command)) || `Stage ${index + 1}`)
|
|
104
|
+
: [];
|
|
105
|
+
|
|
106
|
+
return normalizePlan({
|
|
107
|
+
summary: normalizeText(spec.planSummary) || `Run ${executionSteps.length || 0} execution stage(s) and verify output`,
|
|
108
|
+
steps: [
|
|
109
|
+
{ id: 'intent', description: normalizeText(spec.intent) || 'Intent captured' },
|
|
110
|
+
{ id: 'plan', description: 'Execution plan generated' },
|
|
111
|
+
...executionSteps.map((description, index) => ({
|
|
112
|
+
id: `execute_${index + 1}`,
|
|
113
|
+
description,
|
|
114
|
+
})),
|
|
115
|
+
{ id: 'verify', description: 'Verification loop completed' },
|
|
116
|
+
{ id: 'report', description: 'Evidence-backed report recorded' },
|
|
117
|
+
],
|
|
118
|
+
workflowId,
|
|
119
|
+
}, workflowId);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resolvePlan(spec, workflowId) {
|
|
123
|
+
if (typeof spec.plan === 'function') {
|
|
124
|
+
return normalizePlan(spec.plan(spec), workflowId);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (spec.plan) {
|
|
128
|
+
return normalizePlan(spec.plan, workflowId);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return buildDefaultPlan(spec, workflowId);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function buildExecutionJob(spec, workflowId, paths, plan) {
|
|
135
|
+
return {
|
|
136
|
+
id: spec.jobId || `${workflowId}-execution`,
|
|
137
|
+
tags: Array.isArray(spec.tags) ? spec.tags : [],
|
|
138
|
+
skill: spec.skill || 'autonomous-workflow',
|
|
139
|
+
partnerProfile: spec.partnerProfile || null,
|
|
140
|
+
verificationMode: spec.verificationMode === 'none' ? 'none' : 'standard',
|
|
141
|
+
autoImprove: spec.autoImprove !== false,
|
|
142
|
+
recordFeedback: spec.recordFeedback !== false,
|
|
143
|
+
stages: Array.isArray(spec.stages) ? spec.stages : [],
|
|
144
|
+
metadata: {
|
|
145
|
+
workflowId,
|
|
146
|
+
planSummary: plan.summary,
|
|
147
|
+
workflowRoot: paths.rootDir,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function writeWorkflowPlan(paths, plan) {
|
|
153
|
+
ensureDir(paths.rootDir);
|
|
154
|
+
fs.writeFileSync(paths.planPath, `${JSON.stringify(plan, null, 2)}\n`, 'utf8');
|
|
155
|
+
return paths.planPath;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function collectEvidenceArtifacts(paths, executionResult, extraArtifacts = []) {
|
|
159
|
+
return [
|
|
160
|
+
paths.checkpointPath,
|
|
161
|
+
paths.planPath,
|
|
162
|
+
paths.reportJsonPath,
|
|
163
|
+
paths.reportMdPath,
|
|
164
|
+
executionResult && executionResult.jobStatePath ? executionResult.jobStatePath : null,
|
|
165
|
+
...extraArtifacts,
|
|
166
|
+
].filter(Boolean);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function writeWorkflowReport(paths, report) {
|
|
170
|
+
ensureDir(paths.rootDir);
|
|
171
|
+
fs.writeFileSync(paths.reportJsonPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
|
|
172
|
+
|
|
173
|
+
const markdown = [
|
|
174
|
+
`# ${report.workflowName}`,
|
|
175
|
+
'',
|
|
176
|
+
`- Workflow ID: ${report.workflowId}`,
|
|
177
|
+
`- Status: ${report.status}`,
|
|
178
|
+
`- Intent: ${report.intent}`,
|
|
179
|
+
`- Verification accepted: ${report.verification ? String(report.verification.accepted) : 'skipped'}`,
|
|
180
|
+
`- Evidence artifacts: ${report.evidenceArtifacts.length}`,
|
|
181
|
+
'',
|
|
182
|
+
'## Plan',
|
|
183
|
+
'',
|
|
184
|
+
report.plan.summary,
|
|
185
|
+
'',
|
|
186
|
+
...report.plan.steps.map((step) => `- ${step.id}: ${step.description}`),
|
|
187
|
+
'',
|
|
188
|
+
'## Execution',
|
|
189
|
+
'',
|
|
190
|
+
...report.execution.stageHistory.map((stage) => `- ${stage.name} @ ${stage.completedAt}`),
|
|
191
|
+
'',
|
|
192
|
+
'## Evidence Artifacts',
|
|
193
|
+
'',
|
|
194
|
+
...report.evidenceArtifacts.map((artifact) => `- ${artifact}`),
|
|
195
|
+
].join('\n');
|
|
196
|
+
|
|
197
|
+
fs.writeFileSync(paths.reportMdPath, `${markdown}\n`, 'utf8');
|
|
198
|
+
return {
|
|
199
|
+
json: paths.reportJsonPath,
|
|
200
|
+
markdown: paths.reportMdPath,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function recordAutonomousWorkflowRun(spec, report, evidenceArtifacts, feedbackDir) {
|
|
205
|
+
const proofBacked = report.status === 'completed'
|
|
206
|
+
&& (!report.verification || report.verification.accepted)
|
|
207
|
+
&& evidenceArtifacts.length > 0;
|
|
208
|
+
|
|
209
|
+
return appendWorkflowRun({
|
|
210
|
+
workflowId: report.workflowId,
|
|
211
|
+
workflowName: report.workflowName,
|
|
212
|
+
owner: spec.owner || 'automation',
|
|
213
|
+
runtime: 'node',
|
|
214
|
+
status: report.status,
|
|
215
|
+
customerType: spec.customerType || 'internal_dogfood',
|
|
216
|
+
teamId: spec.teamId || null,
|
|
217
|
+
reviewed: proofBacked,
|
|
218
|
+
reviewedBy: proofBacked ? (spec.reviewedBy || 'automation') : null,
|
|
219
|
+
proofBacked,
|
|
220
|
+
proofArtifacts: evidenceArtifacts,
|
|
221
|
+
source: spec.source || 'autonomous-workflow',
|
|
222
|
+
metadata: {
|
|
223
|
+
intent: report.intent,
|
|
224
|
+
planSummary: report.plan.summary,
|
|
225
|
+
verificationAttempts: report.verification ? report.verification.attempts : 0,
|
|
226
|
+
executionJobId: report.execution.jobId,
|
|
227
|
+
},
|
|
228
|
+
}, feedbackDir);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function runAutonomousWorkflow(spec = {}, options = {}) {
|
|
232
|
+
const cwd = options.cwd || process.cwd();
|
|
233
|
+
const workflowId = normalizeText(spec.workflowId) || slugify(spec.name || spec.intent, 'autonomous-workflow');
|
|
234
|
+
const workflowName = normalizeText(spec.name) || `Autonomous workflow ${workflowId}`;
|
|
235
|
+
const intent = normalizeText(spec.intent) || 'Intent not provided';
|
|
236
|
+
const paths = getWorkflowPaths(workflowId, cwd);
|
|
237
|
+
const plan = resolvePlan(spec, workflowId);
|
|
238
|
+
|
|
239
|
+
writeWorkflowPlan(paths, plan);
|
|
240
|
+
|
|
241
|
+
let checkpoint = createCheckpoint({
|
|
242
|
+
workflowId,
|
|
243
|
+
phase: 'intent',
|
|
244
|
+
status: 'running',
|
|
245
|
+
intent: { summary: intent },
|
|
246
|
+
plan,
|
|
247
|
+
evidence: [paths.planPath],
|
|
248
|
+
metadata: {
|
|
249
|
+
workflowName,
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
saveCheckpoint(checkpoint, paths.checkpointPath);
|
|
253
|
+
|
|
254
|
+
checkpoint = advanceCheckpoint(checkpoint, {
|
|
255
|
+
phase: 'plan',
|
|
256
|
+
status: 'running',
|
|
257
|
+
plan,
|
|
258
|
+
evidence: [paths.planPath],
|
|
259
|
+
});
|
|
260
|
+
saveCheckpoint(checkpoint, paths.checkpointPath);
|
|
261
|
+
|
|
262
|
+
const job = buildExecutionJob(spec, workflowId, paths, plan);
|
|
263
|
+
const executionResult = options.resume === true
|
|
264
|
+
? resumeJob(job.id, job)
|
|
265
|
+
: executeJob(job);
|
|
266
|
+
const jobState = readJobState(job.id);
|
|
267
|
+
|
|
268
|
+
checkpoint = advanceCheckpoint(checkpoint, {
|
|
269
|
+
phase: 'verify',
|
|
270
|
+
status: executionResult.status,
|
|
271
|
+
evidence: jobState && jobState.verification ? [paths.checkpointPath] : [],
|
|
272
|
+
metadata: {
|
|
273
|
+
executionJobId: job.id,
|
|
274
|
+
executionStatus: executionResult.status,
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
saveCheckpoint(checkpoint, paths.checkpointPath);
|
|
278
|
+
|
|
279
|
+
const report = {
|
|
280
|
+
workflowId,
|
|
281
|
+
workflowName,
|
|
282
|
+
status: executionResult.status,
|
|
283
|
+
intent,
|
|
284
|
+
plan,
|
|
285
|
+
execution: {
|
|
286
|
+
jobId: job.id,
|
|
287
|
+
status: executionResult.status,
|
|
288
|
+
stageHistory: Array.isArray(jobState && jobState.stageHistory) ? jobState.stageHistory : [],
|
|
289
|
+
checkpointCount: Array.isArray(jobState && jobState.checkpoints) ? jobState.checkpoints.length : 0,
|
|
290
|
+
currentContext: jobState && jobState.currentContext ? jobState.currentContext : '',
|
|
291
|
+
jobStatePath: jobState ? path.join(getFeedbackDir(options.feedbackDir), 'jobs', job.id, 'state.json') : null,
|
|
292
|
+
},
|
|
293
|
+
verification: executionResult.phases ? executionResult.phases.verification : null,
|
|
294
|
+
phases: executionResult.phases || null,
|
|
295
|
+
timestamp: new Date().toISOString(),
|
|
296
|
+
evidenceArtifacts: [],
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const evidenceArtifacts = collectEvidenceArtifacts(paths, report.execution, spec.proofArtifacts);
|
|
300
|
+
report.evidenceArtifacts = evidenceArtifacts;
|
|
301
|
+
|
|
302
|
+
checkpoint = advanceCheckpoint(checkpoint, {
|
|
303
|
+
phase: 'report',
|
|
304
|
+
status: executionResult.status,
|
|
305
|
+
report: {
|
|
306
|
+
status: report.status,
|
|
307
|
+
generatedAt: report.timestamp,
|
|
308
|
+
},
|
|
309
|
+
evidence: evidenceArtifacts,
|
|
310
|
+
});
|
|
311
|
+
saveCheckpoint(checkpoint, paths.checkpointPath);
|
|
312
|
+
|
|
313
|
+
writeWorkflowReport(paths, report);
|
|
314
|
+
report.workflowRun = recordAutonomousWorkflowRun(spec, report, evidenceArtifacts, options.feedbackDir);
|
|
315
|
+
fs.writeFileSync(paths.reportJsonPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
|
|
316
|
+
|
|
317
|
+
return report;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function getFeedbackDir(feedbackDir) {
|
|
321
|
+
if (feedbackDir) return feedbackDir;
|
|
322
|
+
return process.env.THUMBGATE_FEEDBACK_DIR || path.join(process.cwd(), '.thumbgate');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function resumeAutonomousWorkflow(spec = {}, options = {}) {
|
|
326
|
+
return runAutonomousWorkflow(spec, { ...options, resume: true });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function readWorkflowReport(workflowId, options = {}) {
|
|
330
|
+
const paths = getWorkflowPaths(workflowId, options.cwd || process.cwd());
|
|
331
|
+
if (!fs.existsSync(paths.reportJsonPath)) return null;
|
|
332
|
+
return JSON.parse(fs.readFileSync(paths.reportJsonPath, 'utf8'));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function isCliInvocation(argv = process.argv) {
|
|
336
|
+
const invokedPath = argv[1];
|
|
337
|
+
return invokedPath ? path.resolve(invokedPath) === __filename : false;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
341
|
+
const args = {};
|
|
342
|
+
for (const arg of argv) {
|
|
343
|
+
if (!arg.startsWith('--')) continue;
|
|
344
|
+
const [key, ...rest] = arg.slice(2).split('=');
|
|
345
|
+
args[key] = rest.length > 0 ? rest.join('=') : true;
|
|
346
|
+
}
|
|
347
|
+
return args;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (isCliInvocation()) {
|
|
351
|
+
const args = parseArgs();
|
|
352
|
+
if (!args.file) {
|
|
353
|
+
console.error('Usage: node scripts/autonomous-workflow.js --file=workflow.json [--resume]');
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const specPath = path.resolve(args.file);
|
|
358
|
+
const spec = JSON.parse(fs.readFileSync(specPath, 'utf8'));
|
|
359
|
+
const report = args.resume ? resumeAutonomousWorkflow(spec) : runAutonomousWorkflow(spec);
|
|
360
|
+
console.log(JSON.stringify(report, null, 2));
|
|
361
|
+
process.exit(report.status === 'completed' ? 0 : 1);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
module.exports = {
|
|
365
|
+
buildDefaultPlan,
|
|
366
|
+
collectEvidenceArtifacts,
|
|
367
|
+
getWorkflowPaths,
|
|
368
|
+
normalizePlan,
|
|
369
|
+
parseArgs,
|
|
370
|
+
readWorkflowReport,
|
|
371
|
+
recordAutonomousWorkflowRun,
|
|
372
|
+
resumeAutonomousWorkflow,
|
|
373
|
+
runAutonomousWorkflow,
|
|
374
|
+
slugify,
|
|
375
|
+
writeWorkflowPlan,
|
|
376
|
+
writeWorkflowReport,
|
|
377
|
+
};
|