thumbgate 1.3.0 → 1.4.1
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/README.md +25 -0
- package/.claude-plugin/marketplace.json +32 -13
- package/.claude-plugin/plugin.json +15 -2
- package/.well-known/llms.txt +60 -0
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +242 -126
- package/adapters/README.md +1 -1
- package/adapters/chatgpt/INSTALL.md +59 -4
- package/adapters/chatgpt/openapi.yaml +168 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/mcp/server-stdio.js +84 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +204 -13
- package/bin/postinstall.js +8 -2
- package/config/budget.json +18 -0
- package/config/gates/code-edit.json +61 -0
- package/config/gates/db-write.json +61 -0
- package/config/gates/default.json +154 -3
- package/config/gates/deploy.json +61 -0
- package/config/github-about.json +2 -1
- package/config/merge-quality-checks.json +23 -0
- package/openapi/openapi.yaml +168 -0
- package/package.json +47 -11
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-codex-bridge/.mcp.json +1 -1
- package/plugins/claude-codex-bridge/scripts/codex-bridge.js +1 -3
- package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-profile/.mcp.json +1 -1
- package/plugins/codex-profile/INSTALL.md +27 -4
- package/plugins/codex-profile/README.md +33 -9
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
- package/plugins/opencode-profile/INSTALL.md +1 -1
- package/public/blog.html +73 -0
- package/public/compare/mem0.html +189 -0
- package/public/compare/speclock.html +180 -0
- package/public/compare.html +10 -2
- package/public/guide.html +2 -2
- package/public/guides/claude-code-prevent-repeated-mistakes.html +161 -0
- package/public/guides/codex-cli-guardrails.html +158 -0
- package/public/guides/cursor-prevent-repeated-mistakes.html +161 -0
- package/public/guides/pre-action-gates.html +162 -0
- package/public/guides/stop-repeated-ai-agent-mistakes.html +159 -0
- package/public/index.html +172 -65
- package/public/lessons.html +33 -24
- package/public/llm-context.md +140 -0
- package/public/pro.html +24 -22
- package/scripts/access-anomaly-detector.js +1 -1
- package/scripts/adk-consolidator.js +1 -5
- package/scripts/agent-security-hardening.js +4 -6
- package/scripts/agentic-data-pipeline.js +1 -3
- package/scripts/async-job-runner.js +1 -5
- package/scripts/audit-trail.js +1 -5
- package/scripts/auto-promote-gates.js +5 -3
- package/scripts/background-agent-governance.js +2 -10
- package/scripts/billing-setup.js +109 -0
- package/scripts/billing.js +2 -16
- package/scripts/budget-enforcer.js +173 -0
- package/scripts/build-claude-mcpb.js +71 -5
- package/scripts/build-codex-plugin.js +152 -0
- package/scripts/check-congruence.js +132 -14
- package/scripts/commercial-offer.js +5 -7
- package/scripts/content-engine/linkedin-content-generator.js +154 -0
- package/scripts/content-engine/output/linkedin-memento-validation.md +17 -0
- package/scripts/content-engine/output/linkedin-posts-2026-04-09.md +175 -0
- package/scripts/content-engine/reddit-thread-finder.js +154 -0
- package/scripts/context-engine.js +21 -6
- package/scripts/contextfs.js +1 -21
- package/scripts/dashboard.js +20 -0
- package/scripts/decision-journal.js +341 -0
- package/scripts/delegation-runtime.js +1 -5
- package/scripts/distribution-surfaces.js +54 -0
- package/scripts/document-intake.js +927 -0
- package/scripts/ephemeral-agent-store.js +1 -8
- package/scripts/evolution-state.js +1 -5
- package/scripts/experiment-tracker.js +1 -5
- package/scripts/export-databricks-bundle.js +1 -5
- package/scripts/export-hf-dataset.js +1 -5
- package/scripts/export-training.js +1 -5
- package/scripts/feedback-attribution.js +1 -16
- package/scripts/feedback-history-distiller.js +1 -16
- package/scripts/feedback-loop.js +1 -5
- package/scripts/feedback-root-consolidator.js +2 -21
- package/scripts/feedback-session.js +49 -0
- package/scripts/feedback-to-rules.js +215 -36
- package/scripts/filesystem-search.js +1 -9
- package/scripts/fs-utils.js +104 -0
- package/scripts/gates-engine.js +200 -11
- package/scripts/github-about.js +32 -8
- package/scripts/gtm-revenue-loop.js +1 -5
- package/scripts/harness-selector.js +148 -0
- package/scripts/hosted-config.js +2 -0
- package/scripts/hosted-job-launcher.js +1 -5
- package/scripts/hybrid-feedback-context.js +33 -49
- package/scripts/intervention-policy.js +58 -1
- package/scripts/lesson-db.js +3 -18
- package/scripts/lesson-inference.js +194 -16
- package/scripts/lesson-retrieval.js +60 -24
- package/scripts/llm-client.js +59 -0
- package/scripts/managed-lesson-agent.js +183 -0
- package/scripts/marketing-experiment.js +8 -22
- package/scripts/meta-agent-loop.js +624 -0
- package/scripts/metered-billing.js +1 -1
- package/scripts/money-watcher.js +1 -4
- package/scripts/obsidian-export.js +1 -5
- package/scripts/operational-integrity.js +15 -3
- package/scripts/operational-summary.js +41 -5
- package/scripts/org-dashboard.js +6 -1
- package/scripts/per-step-scoring.js +2 -4
- package/scripts/pr-manager.js +201 -19
- package/scripts/pro-features.js +3 -2
- package/scripts/prompt-dlp.js +3 -3
- package/scripts/prove-adapters.js +1 -5
- package/scripts/prove-attribution.js +1 -5
- package/scripts/prove-automation.js +1 -3
- package/scripts/prove-cloudflare-sandbox.js +1 -3
- package/scripts/prove-data-pipeline.js +1 -3
- package/scripts/prove-intelligence.js +1 -3
- package/scripts/prove-lancedb.js +1 -5
- package/scripts/prove-local-intelligence.js +1 -3
- package/scripts/prove-packaged-runtime.js +75 -9
- package/scripts/prove-predictive-insights.js +1 -3
- package/scripts/prove-training-export.js +1 -3
- package/scripts/prove-workflow-contract.js +1 -5
- package/scripts/ralph-loop.js +376 -0
- package/scripts/ralph-mode-ci.js +331 -0
- package/scripts/rate-limiter.js +3 -1
- package/scripts/reddit-dm-outreach.js +14 -4
- package/scripts/rotate-stripe-webhook-secret.js +314 -0
- package/scripts/schedule-manager.js +3 -5
- package/scripts/security-scanner.js +448 -0
- package/scripts/self-distill-agent.js +579 -0
- package/scripts/semantic-dedup.js +115 -0
- package/scripts/skill-exporter.js +1 -3
- package/scripts/skill-generator.js +1 -5
- package/scripts/social-analytics/engagement-audit.js +1 -18
- package/scripts/social-analytics/pollers/linkedin.js +26 -16
- package/scripts/social-analytics/publishers/linkedin.js +1 -1
- package/scripts/social-analytics/publishers/zernio.js +51 -0
- package/scripts/social-pipeline.js +1 -3
- package/scripts/social-post-hourly.js +47 -4
- package/scripts/statusline-links.js +6 -5
- package/scripts/statusline.sh +29 -153
- package/scripts/sync-branch-protection.js +340 -0
- package/scripts/tessl-export.js +1 -3
- package/scripts/thumbgate-search.js +32 -1
- package/scripts/tool-kpi-tracker.js +1 -1
- package/scripts/tool-registry.js +106 -2
- package/scripts/vector-store.js +1 -5
- package/scripts/weekly-auto-post.js +1 -1
- package/scripts/workflow-sentinel.js +91 -0
- package/skills/thumbgate/SKILL.md +1 -1
- package/src/api/server.js +296 -7
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/social-analytics/db/social-analytics.db-shm +0 -0
- /package/scripts/social-analytics/db/{social-analytics.db-wal → analytics.sqlite} +0 -0
|
@@ -23,6 +23,7 @@ const path = require('path');
|
|
|
23
23
|
const { getFeedbackPaths, readJSONL } = require('./feedback-loop');
|
|
24
24
|
const { loadModel, saveModel, updateModel, samplePosteriors, getReliability } = require('./thompson-sampling');
|
|
25
25
|
const { buildResearchBrief } = require('./hf-papers');
|
|
26
|
+
const { ensureDir, readJsonl } = require('./fs-utils');
|
|
26
27
|
|
|
27
28
|
// ---------------------------------------------------------------------------
|
|
28
29
|
// Constants
|
|
@@ -80,27 +81,12 @@ function getMarketingPaths() {
|
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
function ensureDir(dirPath) {
|
|
84
|
-
if (!fs.existsSync(dirPath)) {
|
|
85
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
84
|
|
|
89
85
|
function appendJSONL(filePath, record) {
|
|
90
86
|
ensureDir(path.dirname(filePath));
|
|
91
87
|
fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`);
|
|
92
88
|
}
|
|
93
89
|
|
|
94
|
-
function readJSONLFile(filePath) {
|
|
95
|
-
if (!fs.existsSync(filePath)) return [];
|
|
96
|
-
const lines = fs.readFileSync(filePath, 'utf-8').trim().split('\n').filter(Boolean);
|
|
97
|
-
const records = [];
|
|
98
|
-
for (const line of lines) {
|
|
99
|
-
try { records.push(JSON.parse(line)); } catch { /* skip bad lines */ }
|
|
100
|
-
}
|
|
101
|
-
return records;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
90
|
// ---------------------------------------------------------------------------
|
|
105
91
|
// Variant Generation
|
|
106
92
|
// ---------------------------------------------------------------------------
|
|
@@ -300,7 +286,7 @@ function recordVariantMetrics(params) {
|
|
|
300
286
|
}
|
|
301
287
|
|
|
302
288
|
const paths = getMarketingPaths();
|
|
303
|
-
const variants =
|
|
289
|
+
const variants = readJsonl(paths.variantsPath);
|
|
304
290
|
const variant = variants.find(v => v.id === params.variantId);
|
|
305
291
|
|
|
306
292
|
if (!variant) {
|
|
@@ -333,7 +319,7 @@ function selectWinners(params) {
|
|
|
333
319
|
}
|
|
334
320
|
|
|
335
321
|
const paths = getMarketingPaths();
|
|
336
|
-
const allVariants =
|
|
322
|
+
const allVariants = readJsonl(paths.variantsPath);
|
|
337
323
|
|
|
338
324
|
// Get latest state of each variant in this batch (last write wins)
|
|
339
325
|
const batchMap = new Map();
|
|
@@ -479,7 +465,7 @@ function extractPattern(variant) {
|
|
|
479
465
|
*/
|
|
480
466
|
function getWinningPatterns(channel, limit = 5) {
|
|
481
467
|
const paths = getMarketingPaths();
|
|
482
|
-
const winners =
|
|
468
|
+
const winners = readJsonl(paths.winnersPath);
|
|
483
469
|
return winners
|
|
484
470
|
.filter(w => w.channel === channel)
|
|
485
471
|
.slice(-limit)
|
|
@@ -496,7 +482,7 @@ function getWinningPatterns(channel, limit = 5) {
|
|
|
496
482
|
*/
|
|
497
483
|
function getKnowledgeBase(opts = {}) {
|
|
498
484
|
const paths = getMarketingPaths();
|
|
499
|
-
let entries =
|
|
485
|
+
let entries = readJsonl(paths.knowledgePath);
|
|
500
486
|
if (opts.channel) {
|
|
501
487
|
entries = entries.filter(e => e.channel === opts.channel);
|
|
502
488
|
}
|
|
@@ -509,9 +495,9 @@ function getKnowledgeBase(opts = {}) {
|
|
|
509
495
|
|
|
510
496
|
function updateMarketingProgress() {
|
|
511
497
|
const paths = getMarketingPaths();
|
|
512
|
-
const experiments =
|
|
513
|
-
const knowledge =
|
|
514
|
-
const winners =
|
|
498
|
+
const experiments = readJsonl(paths.experimentsPath);
|
|
499
|
+
const knowledge = readJsonl(paths.knowledgePath);
|
|
500
|
+
const winners = readJsonl(paths.winnersPath);
|
|
515
501
|
|
|
516
502
|
const channelStats = {};
|
|
517
503
|
for (const ch of MARKETING_CHANNELS) {
|
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Meta-Agent Loop — Automated Harness Self-Improvement
|
|
6
|
+
*
|
|
7
|
+
* Inspired by the "Auto Agent" architecture:
|
|
8
|
+
* - Task Agent does the work; Meta Agent observes outcomes and rewrites the harness.
|
|
9
|
+
*
|
|
10
|
+
* This runner closes the self-improvement loop without human feedback:
|
|
11
|
+
*
|
|
12
|
+
* 1. Read gate-program.md for the domain's success definition
|
|
13
|
+
* 2. Pull recent failures from feedback-log.jsonl
|
|
14
|
+
* 3. Generate N candidate rule mutations via LLM (or heuristic fallback)
|
|
15
|
+
* 4. Evaluate each candidate by replaying it against the lesson DB:
|
|
16
|
+
* hit-rate = failures it would have caught / total failures
|
|
17
|
+
* fp-rate = successes it would have blocked / total successes
|
|
18
|
+
* score = hit-rate - (fp_weight * fp-rate)
|
|
19
|
+
* 5. Promote candidates whose score beats the current baseline
|
|
20
|
+
* 6. Revert (discard) candidates that regress
|
|
21
|
+
* 7. Write promoted rules to auto-promoted-gates.json + prevention-rules.md
|
|
22
|
+
* 8. Record results in evolution-state.json with a rollback snapshot
|
|
23
|
+
* 9. [optional] Run workspace evolution to auto-tune Thompson Sampling hyperparameters
|
|
24
|
+
* when >= EVOLVE_MIN_FAILURES failures are present (--evolve flag or THUMBGATE_META_EVOLVE=1)
|
|
25
|
+
*
|
|
26
|
+
* Runs autonomously at session end (Stop hook) or on demand:
|
|
27
|
+
* node scripts/meta-agent-loop.js
|
|
28
|
+
* node scripts/meta-agent-loop.js --dry-run
|
|
29
|
+
* node scripts/meta-agent-loop.js --status
|
|
30
|
+
* node scripts/meta-agent-loop.js --evolve (also runs workspace evolution)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const fs = require('fs');
|
|
34
|
+
const path = require('path');
|
|
35
|
+
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
36
|
+
const { parseFeedbackFile, classifySignal, promoteToGates } = require('./feedback-to-rules');
|
|
37
|
+
const { loadAutoGates, saveAutoGates, getAutoGatesPath, patternToGateId } = require('./auto-promote-gates');
|
|
38
|
+
const { readEvolutionState, writeEvolutionState, captureEvolutionSnapshot, applyAcceptedMutation } = require('./evolution-state');
|
|
39
|
+
const { isAvailable, callClaude, MODELS } = require('./llm-client');
|
|
40
|
+
const { ensureParentDir } = require('./fs-utils');
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Constants
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
const GATE_PROGRAM_PATHS = [
|
|
47
|
+
path.join(process.cwd(), 'gate-program.md'),
|
|
48
|
+
path.join(process.cwd(), '..', 'gate-program.md'),
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const CANDIDATES_PER_RUN = 5;
|
|
52
|
+
const FP_WEIGHT = 2.0; // false positives penalised 2× vs true positives
|
|
53
|
+
const MIN_SCORE_THRESHOLD = 0.1; // candidate must score at least 0.1 to be promoted
|
|
54
|
+
const MAX_PROMOTED_PER_RUN = 3; // at most 3 new rules per overnight run
|
|
55
|
+
const RECENT_WINDOW_DAYS = 14; // look back 14 days for failures
|
|
56
|
+
const EVOLVE_MIN_FAILURES = 5; // minimum failures before workspace evolution runs
|
|
57
|
+
|
|
58
|
+
const META_RUNS_PATH = path.join(
|
|
59
|
+
require('os').homedir(),
|
|
60
|
+
'.thumbgate',
|
|
61
|
+
'meta-agent-runs.jsonl'
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// 1. Read gate-program.md
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
function readGateProgram() {
|
|
69
|
+
for (const p of GATE_PROGRAM_PATHS) {
|
|
70
|
+
if (fs.existsSync(p)) {
|
|
71
|
+
return fs.readFileSync(p, 'utf-8');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function extractSuccessDefinition(gateProgramText) {
|
|
78
|
+
if (!gateProgramText) return '';
|
|
79
|
+
const match = gateProgramText.match(/## Success Looks Like([\s\S]*?)(?=##|$)/);
|
|
80
|
+
return match ? match[1].trim() : '';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function extractBlockPatterns(gateProgramText) {
|
|
84
|
+
if (!gateProgramText) return [];
|
|
85
|
+
const match = gateProgramText.match(/## Patterns to Block[\s\S]*?\n([\s\S]*?)(?=##|$)/);
|
|
86
|
+
if (!match) return [];
|
|
87
|
+
return match[1]
|
|
88
|
+
.split('\n')
|
|
89
|
+
.filter((l) => /^\d+\./.test(l.trim()))
|
|
90
|
+
.map((l) => l.replace(/^\d+\.\s*\*\*[^*]+\*\*\s*—?\s*/, '').trim())
|
|
91
|
+
.filter(Boolean);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// 2. Pull recent failures
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
function getRecentFailures(feedbackLogPath, windowDays = RECENT_WINDOW_DAYS) {
|
|
99
|
+
const entries = parseFeedbackFile(feedbackLogPath);
|
|
100
|
+
const cutoff = Date.now() - windowDays * 24 * 60 * 60 * 1000;
|
|
101
|
+
|
|
102
|
+
return entries.filter((e) => {
|
|
103
|
+
if (classifySignal(e) !== 'negative') return false;
|
|
104
|
+
const ts = e.timestamp ? new Date(e.timestamp).getTime() : 0;
|
|
105
|
+
return ts >= cutoff;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function getRecentSuccesses(feedbackLogPath, windowDays = RECENT_WINDOW_DAYS) {
|
|
110
|
+
const entries = parseFeedbackFile(feedbackLogPath);
|
|
111
|
+
const cutoff = Date.now() - windowDays * 24 * 60 * 60 * 1000;
|
|
112
|
+
|
|
113
|
+
return entries.filter((e) => {
|
|
114
|
+
if (classifySignal(e) !== 'positive') return false;
|
|
115
|
+
const ts = e.timestamp ? new Date(e.timestamp).getTime() : 0;
|
|
116
|
+
return ts >= cutoff;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// 3. Candidate rule generation
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
const CANDIDATE_SYSTEM_PROMPT = `You are a meta-agent for ThumbGate, an AI coding agent safety system.
|
|
125
|
+
|
|
126
|
+
Your job: Given recent failure events and a domain success definition, generate
|
|
127
|
+
candidate prevention rules that would have caught these failures WITHOUT blocking
|
|
128
|
+
legitimate successful actions.
|
|
129
|
+
|
|
130
|
+
Return ONLY a JSON array of candidate rule objects (no markdown fences):
|
|
131
|
+
[
|
|
132
|
+
{
|
|
133
|
+
"pattern": "<JavaScript regex to match against tool call context/command>",
|
|
134
|
+
"action": "block" | "warn",
|
|
135
|
+
"message": "<why this is blocked/warned, shown to the agent>",
|
|
136
|
+
"severity": "critical" | "high" | "medium",
|
|
137
|
+
"rationale": "<why this rule would catch the failure pattern>"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
Rules:
|
|
142
|
+
- Pattern must be a valid JavaScript regex string (used with new RegExp(pattern, 'i'))
|
|
143
|
+
- Prefer specific patterns. "force.*push.*main" beats "push"
|
|
144
|
+
- Use "block" for destructive/irreversible actions, "warn" for review-needed
|
|
145
|
+
- Each rule should catch at least one of the listed failures
|
|
146
|
+
- Do NOT generate rules so broad they would block common, successful operations
|
|
147
|
+
- Max ${CANDIDATES_PER_RUN} candidates`;
|
|
148
|
+
|
|
149
|
+
async function generateCandidatesViaLLM(failures, successDef, blockPatterns) {
|
|
150
|
+
if (!isAvailable()) return null;
|
|
151
|
+
|
|
152
|
+
const failureBatch = failures
|
|
153
|
+
.slice(0, 20)
|
|
154
|
+
.map((e, i) => {
|
|
155
|
+
const ctx = (e.context || e.whatWentWrong || '').slice(0, 200);
|
|
156
|
+
const tags = (e.tags || []).join(', ');
|
|
157
|
+
return `${i + 1}. ${ctx}${tags ? ` [tags: ${tags}]` : ''}`;
|
|
158
|
+
})
|
|
159
|
+
.join('\n');
|
|
160
|
+
|
|
161
|
+
const userPrompt = [
|
|
162
|
+
`## Success Definition\n${successDef || '(not specified)'}`,
|
|
163
|
+
`## Known Block Patterns from gate-program.md\n${blockPatterns.map((p, i) => `${i + 1}. ${p}`).join('\n') || '(none)'}`,
|
|
164
|
+
`## Recent Failures (${failures.length} total, showing up to 20)\n${failureBatch || '(none)'}`,
|
|
165
|
+
`Generate ${CANDIDATES_PER_RUN} candidate prevention rules that would catch these failures.`,
|
|
166
|
+
].join('\n\n');
|
|
167
|
+
|
|
168
|
+
const raw = await callClaude({
|
|
169
|
+
systemPrompt: CANDIDATE_SYSTEM_PROMPT,
|
|
170
|
+
userPrompt,
|
|
171
|
+
model: MODELS.FAST,
|
|
172
|
+
maxTokens: 1200,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (!raw) return null;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const parsed = JSON.parse(raw);
|
|
179
|
+
if (!Array.isArray(parsed)) return null;
|
|
180
|
+
return parsed
|
|
181
|
+
.filter((r) => r.pattern && r.action && r.message && r.severity)
|
|
182
|
+
.slice(0, CANDIDATES_PER_RUN);
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function generateCandidatesHeuristic(failures, blockPatterns) {
|
|
189
|
+
// Fallback when no LLM is available: derive candidates from:
|
|
190
|
+
// (a) gate-program.md block patterns
|
|
191
|
+
// (b) top repeated failure contexts
|
|
192
|
+
const candidates = [];
|
|
193
|
+
|
|
194
|
+
// From gate-program.md block patterns
|
|
195
|
+
for (const pattern of blockPatterns.slice(0, 3)) {
|
|
196
|
+
const keywords = pattern
|
|
197
|
+
.toLowerCase()
|
|
198
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
199
|
+
.split(/\s+/)
|
|
200
|
+
.filter((w) => w.length > 4)
|
|
201
|
+
.slice(0, 3);
|
|
202
|
+
if (keywords.length >= 2) {
|
|
203
|
+
candidates.push({
|
|
204
|
+
pattern: keywords.join('.*'),
|
|
205
|
+
action: 'block',
|
|
206
|
+
message: `Blocked by gate-program.md rule: ${pattern.slice(0, 80)}`,
|
|
207
|
+
severity: 'high',
|
|
208
|
+
rationale: 'Derived from gate-program.md block pattern',
|
|
209
|
+
source: 'heuristic',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// From top repeated failure contexts
|
|
215
|
+
const ctxCounts = {};
|
|
216
|
+
for (const f of failures) {
|
|
217
|
+
const ctx = (f.context || f.whatWentWrong || '').trim();
|
|
218
|
+
if (ctx.length < 10) continue;
|
|
219
|
+
const key = ctx.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').slice(0, 80);
|
|
220
|
+
ctxCounts[key] = (ctxCounts[key] || 0) + 1;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const topContexts = Object.entries(ctxCounts)
|
|
224
|
+
.filter(([, c]) => c >= 2)
|
|
225
|
+
.sort((a, b) => b[1] - a[1])
|
|
226
|
+
.slice(0, 3);
|
|
227
|
+
|
|
228
|
+
for (const [ctx] of topContexts) {
|
|
229
|
+
const keywords = ctx.split(/\s+/).filter((w) => w.length > 4).slice(0, 3);
|
|
230
|
+
if (keywords.length >= 2) {
|
|
231
|
+
candidates.push({
|
|
232
|
+
pattern: keywords.join('.*'),
|
|
233
|
+
action: 'warn',
|
|
234
|
+
message: `Repeated failure pattern: ${ctx.slice(0, 80)}`,
|
|
235
|
+
severity: 'medium',
|
|
236
|
+
rationale: `Appeared ${ctxCounts[ctx]}× in recent failures`,
|
|
237
|
+
source: 'heuristic',
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return candidates.slice(0, CANDIDATES_PER_RUN);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// 4. Evaluate candidates
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
function matchesEntry(pattern, entry) {
|
|
250
|
+
try {
|
|
251
|
+
const re = new RegExp(pattern, 'i');
|
|
252
|
+
const text = [
|
|
253
|
+
entry.context,
|
|
254
|
+
entry.whatWentWrong,
|
|
255
|
+
entry.whatToChange,
|
|
256
|
+
(entry.tags || []).join(' '),
|
|
257
|
+
].filter(Boolean).join(' ');
|
|
258
|
+
return re.test(text);
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function scoreCandidate(candidate, failures, successes) {
|
|
265
|
+
if (!failures.length && !successes.length) return { score: 0, hitRate: 0, fpRate: 0 };
|
|
266
|
+
|
|
267
|
+
const hits = failures.filter((f) => matchesEntry(candidate.pattern, f)).length;
|
|
268
|
+
const fps = successes.filter((s) => matchesEntry(candidate.pattern, s)).length;
|
|
269
|
+
|
|
270
|
+
const hitRate = failures.length > 0 ? hits / failures.length : 0;
|
|
271
|
+
const fpRate = successes.length > 0 ? fps / successes.length : 0;
|
|
272
|
+
const score = hitRate - FP_WEIGHT * fpRate;
|
|
273
|
+
|
|
274
|
+
return { score, hitRate, fpRate, hits, fps };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
// 5. Promote / revert
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
function buildPromotedGate(candidate, metrics, runId) {
|
|
282
|
+
return {
|
|
283
|
+
id: patternToGateId(`meta-${candidate.pattern}`),
|
|
284
|
+
pattern: candidate.pattern,
|
|
285
|
+
action: candidate.action,
|
|
286
|
+
message: candidate.message,
|
|
287
|
+
severity: candidate.severity,
|
|
288
|
+
occurrences: metrics.hits,
|
|
289
|
+
promotedAt: new Date().toISOString(),
|
|
290
|
+
source: 'meta-agent',
|
|
291
|
+
runId,
|
|
292
|
+
score: parseFloat(metrics.score.toFixed(3)),
|
|
293
|
+
hitRate: parseFloat(metrics.hitRate.toFixed(3)),
|
|
294
|
+
fpRate: parseFloat(metrics.fpRate.toFixed(3)),
|
|
295
|
+
rationale: candidate.rationale || '',
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function writePreventionRulesFromGates(autoGatesData, rulesPath) {
|
|
300
|
+
const lines = [
|
|
301
|
+
'# Prevention Rules (Meta-Agent Generated)',
|
|
302
|
+
`# Updated: ${new Date().toISOString()}`,
|
|
303
|
+
'',
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
for (const gate of autoGatesData.gates) {
|
|
307
|
+
const prefix = gate.action === 'block' ? '[BLOCK]' : '[WARN]';
|
|
308
|
+
lines.push(`- ${prefix} ${gate.message}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!autoGatesData.gates.length) {
|
|
312
|
+
lines.push('- No prevention rules active.');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const dir = path.dirname(rulesPath);
|
|
316
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
317
|
+
fs.writeFileSync(rulesPath, lines.join('\n') + '\n');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// 6. Persistence
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
function appendRunManifest(manifest) {
|
|
326
|
+
ensureParentDir(META_RUNS_PATH);
|
|
327
|
+
fs.appendFileSync(META_RUNS_PATH, JSON.stringify(manifest) + '\n');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function readRunManifests() {
|
|
331
|
+
if (!fs.existsSync(META_RUNS_PATH)) return [];
|
|
332
|
+
const raw = fs.readFileSync(META_RUNS_PATH, 'utf-8').trim();
|
|
333
|
+
if (!raw) return [];
|
|
334
|
+
return raw.split('\n').map((l) => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// 7. Main entry point
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
|
|
341
|
+
async function runMetaAgentLoop({ dryRun = false, verbose = false } = {}) {
|
|
342
|
+
const feedbackDir = resolveFeedbackDir();
|
|
343
|
+
const feedbackLogPath = path.join(feedbackDir, 'feedback-log.jsonl');
|
|
344
|
+
const autoGatesPath = getAutoGatesPath();
|
|
345
|
+
const rulesPath = path.join(process.cwd(), '.thumbgate', 'prevention-rules.md');
|
|
346
|
+
|
|
347
|
+
const runId = `meta_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
348
|
+
const startedAt = new Date().toISOString();
|
|
349
|
+
|
|
350
|
+
// Step 1: Read gate-program.md
|
|
351
|
+
const gateProgramText = readGateProgram();
|
|
352
|
+
const successDef = extractSuccessDefinition(gateProgramText);
|
|
353
|
+
const blockPatterns = extractBlockPatterns(gateProgramText);
|
|
354
|
+
|
|
355
|
+
if (verbose) {
|
|
356
|
+
process.stdout.write(`[meta-agent] run=${runId}\n`);
|
|
357
|
+
process.stdout.write(`[meta-agent] gate-program.md ${gateProgramText ? 'found' : 'NOT FOUND — using heuristics only'}\n`);
|
|
358
|
+
process.stdout.write(`[meta-agent] block patterns from gate-program.md: ${blockPatterns.length}\n`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Step 2: Pull recent failures + successes
|
|
362
|
+
const failures = getRecentFailures(feedbackLogPath);
|
|
363
|
+
const successes = getRecentSuccesses(feedbackLogPath);
|
|
364
|
+
|
|
365
|
+
if (verbose) {
|
|
366
|
+
process.stdout.write(`[meta-agent] failures (${RECENT_WINDOW_DAYS}d): ${failures.length}, successes: ${successes.length}\n`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Step 3: Generate candidate rules
|
|
370
|
+
let candidates = null;
|
|
371
|
+
const analysisMode = isAvailable() ? 'llm' : 'heuristic';
|
|
372
|
+
|
|
373
|
+
if (isAvailable()) {
|
|
374
|
+
candidates = await generateCandidatesViaLLM(failures, successDef, blockPatterns);
|
|
375
|
+
}
|
|
376
|
+
if (!candidates || candidates.length === 0) {
|
|
377
|
+
candidates = generateCandidatesHeuristic(failures, blockPatterns);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (verbose) {
|
|
381
|
+
process.stdout.write(`[meta-agent] candidates generated: ${candidates.length} (mode=${analysisMode})\n`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Step 4: Score each candidate
|
|
385
|
+
const evaluated = candidates.map((c) => ({
|
|
386
|
+
candidate: c,
|
|
387
|
+
metrics: scoreCandidate(c, failures, successes),
|
|
388
|
+
})).sort((a, b) => b.metrics.score - a.metrics.score);
|
|
389
|
+
|
|
390
|
+
// Step 5: Select promotions
|
|
391
|
+
const toPromote = evaluated
|
|
392
|
+
.filter((e) => e.metrics.score >= MIN_SCORE_THRESHOLD)
|
|
393
|
+
.slice(0, MAX_PROMOTED_PER_RUN);
|
|
394
|
+
|
|
395
|
+
const toRevert = evaluated.filter((e) => e.metrics.score < MIN_SCORE_THRESHOLD);
|
|
396
|
+
|
|
397
|
+
if (verbose) {
|
|
398
|
+
process.stdout.write(`[meta-agent] candidates above threshold: ${toPromote.length}, below: ${toRevert.length}\n`);
|
|
399
|
+
for (const { candidate, metrics } of evaluated) {
|
|
400
|
+
const mark = metrics.score >= MIN_SCORE_THRESHOLD ? 'KEEP' : 'REVERT';
|
|
401
|
+
process.stdout.write(
|
|
402
|
+
`[meta-agent] [${mark}] score=${metrics.score.toFixed(3)} hit=${metrics.hitRate.toFixed(2)} fp=${metrics.fpRate.toFixed(2)} — ${candidate.pattern}\n`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Step 6: Persist promoted rules (unless dry-run)
|
|
408
|
+
const promotedGates = [];
|
|
409
|
+
|
|
410
|
+
if (!dryRun && toPromote.length > 0) {
|
|
411
|
+
// Snapshot before mutating
|
|
412
|
+
captureEvolutionSnapshot({
|
|
413
|
+
label: `meta-agent-pre-${runId}`,
|
|
414
|
+
reason: 'meta-agent-loop',
|
|
415
|
+
source: 'meta-agent-loop',
|
|
416
|
+
metadata: { runId, candidateCount: candidates.length, failureCount: failures.length },
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const autoGatesData = loadAutoGates();
|
|
420
|
+
|
|
421
|
+
for (const { candidate, metrics } of toPromote) {
|
|
422
|
+
const gate = buildPromotedGate(candidate, metrics, runId);
|
|
423
|
+
// Avoid duplicates by id
|
|
424
|
+
const existingIdx = autoGatesData.gates.findIndex((g) => g.id === gate.id);
|
|
425
|
+
if (existingIdx !== -1) {
|
|
426
|
+
autoGatesData.gates[existingIdx] = { ...autoGatesData.gates[existingIdx], ...gate };
|
|
427
|
+
} else {
|
|
428
|
+
autoGatesData.gates.push(gate);
|
|
429
|
+
}
|
|
430
|
+
promotedGates.push(gate);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Enforce max gates (10 free, rotate oldest)
|
|
434
|
+
const MAX_GATES = 10;
|
|
435
|
+
if (autoGatesData.gates.length > MAX_GATES) {
|
|
436
|
+
autoGatesData.gates = autoGatesData.gates.slice(-MAX_GATES);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
saveAutoGates(autoGatesData);
|
|
440
|
+
writePreventionRulesFromGates(autoGatesData, rulesPath);
|
|
441
|
+
|
|
442
|
+
// Record in evolution-state
|
|
443
|
+
const state = readEvolutionState();
|
|
444
|
+
writeEvolutionState({
|
|
445
|
+
...state,
|
|
446
|
+
settings: {
|
|
447
|
+
...state.settings,
|
|
448
|
+
last_meta_agent_run: runId,
|
|
449
|
+
last_meta_agent_at: startedAt,
|
|
450
|
+
meta_agent_total_promoted: (state.settings.meta_agent_total_promoted || 0) + toPromote.length,
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Step 9: Workspace evolution — auto-tune Thompson Sampling hyperparameters.
|
|
456
|
+
// Runs when: not dry-run, --evolve flag or THUMBGATE_META_EVOLVE=1, and
|
|
457
|
+
// enough failure signal exists (>= EVOLVE_MIN_FAILURES).
|
|
458
|
+
let evolutionResult = null;
|
|
459
|
+
const shouldEvolve = !dryRun && (process.env.THUMBGATE_META_EVOLVE === '1');
|
|
460
|
+
if (shouldEvolve && failures.length >= EVOLVE_MIN_FAILURES) {
|
|
461
|
+
try {
|
|
462
|
+
const { runWorkspaceEvolution, recommendEvolutionTarget } = require('./workspace-evolver');
|
|
463
|
+
const failureTags = failures.flatMap((f) => f.tags || []);
|
|
464
|
+
const dominantFailureType = toRevert.length > toPromote.length ? 'decision' : 'execution';
|
|
465
|
+
const targetName = recommendEvolutionTarget({ failureType: dominantFailureType, tags: failureTags });
|
|
466
|
+
|
|
467
|
+
if (verbose) {
|
|
468
|
+
process.stdout.write(`[meta-agent] running workspace evolution: target=${targetName}\n`);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
evolutionResult = runWorkspaceEvolution({
|
|
472
|
+
targetName,
|
|
473
|
+
primaryCommands: ['node --test tests/meta-agent-loop.test.js'],
|
|
474
|
+
timeoutMs: 30000,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
if (verbose) {
|
|
478
|
+
const status = evolutionResult.skipped ? 'skipped' : (evolutionResult.kept ? 'kept' : 'reverted');
|
|
479
|
+
process.stdout.write(`[meta-agent] evolution: target=${targetName} status=${status}\n`);
|
|
480
|
+
if (!evolutionResult.skipped) {
|
|
481
|
+
process.stdout.write(`[meta-agent] evolution: ${evolutionResult.currentValue} → ${evolutionResult.nextValue} (kept=${evolutionResult.kept})\n`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
} catch (err) {
|
|
485
|
+
if (verbose) process.stdout.write(`[meta-agent] workspace evolution failed (non-fatal): ${err.message}\n`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const completedAt = new Date().toISOString();
|
|
490
|
+
const manifest = {
|
|
491
|
+
runId,
|
|
492
|
+
startedAt,
|
|
493
|
+
completedAt,
|
|
494
|
+
dryRun,
|
|
495
|
+
analysisMode,
|
|
496
|
+
gateProgramFound: Boolean(gateProgramText),
|
|
497
|
+
failureCount: failures.length,
|
|
498
|
+
successCount: successes.length,
|
|
499
|
+
candidateCount: candidates.length,
|
|
500
|
+
promotedCount: toPromote.length,
|
|
501
|
+
revertedCount: toRevert.length,
|
|
502
|
+
promoted: promotedGates.map((g) => ({ id: g.id, action: g.action, score: g.score, pattern: g.pattern })),
|
|
503
|
+
reverted: toRevert.map(({ candidate, metrics }) => ({
|
|
504
|
+
pattern: candidate.pattern,
|
|
505
|
+
score: parseFloat(metrics.score.toFixed(3)),
|
|
506
|
+
})),
|
|
507
|
+
evolution: evolutionResult
|
|
508
|
+
? {
|
|
509
|
+
target: evolutionResult.target?.name,
|
|
510
|
+
from: evolutionResult.currentValue,
|
|
511
|
+
to: evolutionResult.nextValue,
|
|
512
|
+
kept: evolutionResult.kept,
|
|
513
|
+
skipped: evolutionResult.skipped || false,
|
|
514
|
+
}
|
|
515
|
+
: null,
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
if (!dryRun) {
|
|
519
|
+
appendRunManifest(manifest);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return manifest;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ---------------------------------------------------------------------------
|
|
526
|
+
// 8. Status
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
528
|
+
|
|
529
|
+
function getMetaAgentStatus() {
|
|
530
|
+
const runs = readRunManifests();
|
|
531
|
+
if (runs.length === 0) return null;
|
|
532
|
+
const last = runs[runs.length - 1];
|
|
533
|
+
return {
|
|
534
|
+
totalRuns: runs.length,
|
|
535
|
+
lastRunId: last.runId,
|
|
536
|
+
lastRunAt: last.completedAt,
|
|
537
|
+
lastAnalysisMode: last.analysisMode,
|
|
538
|
+
lastFailureCount: last.failureCount,
|
|
539
|
+
lastCandidateCount: last.candidateCount,
|
|
540
|
+
lastPromotedCount: last.promotedCount,
|
|
541
|
+
lastRevertedCount: last.revertedCount,
|
|
542
|
+
totalPromoted: runs.reduce((s, r) => s + (r.promotedCount || 0), 0),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// ---------------------------------------------------------------------------
|
|
547
|
+
// 9. CLI
|
|
548
|
+
// ---------------------------------------------------------------------------
|
|
549
|
+
|
|
550
|
+
async function main() {
|
|
551
|
+
const args = process.argv.slice(2);
|
|
552
|
+
const dryRun = args.includes('--dry-run');
|
|
553
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
554
|
+
|
|
555
|
+
if (args.includes('--status')) {
|
|
556
|
+
const status = getMetaAgentStatus();
|
|
557
|
+
if (!status) {
|
|
558
|
+
console.log('No meta-agent runs recorded yet.');
|
|
559
|
+
} else {
|
|
560
|
+
console.log(JSON.stringify(status, null, 2));
|
|
561
|
+
}
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const mode = dryRun ? 'DRY RUN' : 'LIVE';
|
|
566
|
+
console.log(`Meta-agent loop starting [${mode}]...`);
|
|
567
|
+
|
|
568
|
+
const manifest = await runMetaAgentLoop({ dryRun, verbose: verbose || true });
|
|
569
|
+
|
|
570
|
+
console.log(`Run ID : ${manifest.runId}`);
|
|
571
|
+
console.log(`Analysis mode : ${manifest.analysisMode}`);
|
|
572
|
+
console.log(`Gate program : ${manifest.gateProgramFound ? 'found' : 'not found'}`);
|
|
573
|
+
console.log(`Failures (${RECENT_WINDOW_DAYS}d): ${manifest.failureCount}`);
|
|
574
|
+
console.log(`Candidates : ${manifest.candidateCount}`);
|
|
575
|
+
console.log(`Promoted : ${manifest.promotedCount}`);
|
|
576
|
+
console.log(`Reverted : ${manifest.revertedCount}`);
|
|
577
|
+
|
|
578
|
+
if (manifest.promoted.length > 0) {
|
|
579
|
+
console.log('\nPromoted rules:');
|
|
580
|
+
for (const g of manifest.promoted) {
|
|
581
|
+
console.log(` [${g.action.toUpperCase()}] score=${g.score} — ${g.pattern}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (manifest.reverted.length > 0 && verbose) {
|
|
586
|
+
console.log('\nReverted (below threshold):');
|
|
587
|
+
for (const r of manifest.reverted) {
|
|
588
|
+
console.log(` score=${r.score} — ${r.pattern}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (dryRun) {
|
|
593
|
+
console.log('\n[DRY RUN] No rules written.');
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (require.main === module) {
|
|
598
|
+
main().catch((err) => {
|
|
599
|
+
console.error('Meta-agent loop failed:', err.message);
|
|
600
|
+
process.exitCode = 1;
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
module.exports = {
|
|
605
|
+
runMetaAgentLoop,
|
|
606
|
+
getMetaAgentStatus,
|
|
607
|
+
readGateProgram,
|
|
608
|
+
extractSuccessDefinition,
|
|
609
|
+
extractBlockPatterns,
|
|
610
|
+
getRecentFailures,
|
|
611
|
+
getRecentSuccesses,
|
|
612
|
+
generateCandidatesHeuristic,
|
|
613
|
+
scoreCandidate,
|
|
614
|
+
buildPromotedGate,
|
|
615
|
+
writePreventionRulesFromGates,
|
|
616
|
+
appendRunManifest,
|
|
617
|
+
readRunManifests,
|
|
618
|
+
matchesEntry,
|
|
619
|
+
META_RUNS_PATH,
|
|
620
|
+
CANDIDATES_PER_RUN,
|
|
621
|
+
MIN_SCORE_THRESHOLD,
|
|
622
|
+
FP_WEIGHT,
|
|
623
|
+
EVOLVE_MIN_FAILURES,
|
|
624
|
+
};
|