rlhf-feedback-loop 0.6.11 → 0.6.13
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/CHANGELOG.md +10 -0
- package/README.md +116 -74
- package/adapters/README.md +3 -3
- package/adapters/amp/skills/rlhf-feedback/SKILL.md +2 -0
- package/adapters/chatgpt/INSTALL.md +7 -4
- package/adapters/chatgpt/openapi.yaml +6 -3
- package/adapters/claude/.mcp.json +3 -3
- package/adapters/codex/config.toml +3 -3
- package/adapters/gemini/function-declarations.json +2 -2
- package/adapters/mcp/server-stdio.js +19 -5
- package/bin/cli.js +302 -32
- package/openapi/openapi.yaml +6 -3
- package/package.json +22 -9
- package/scripts/a2ui-engine.js +73 -0
- package/scripts/adk-consolidator.js +126 -32
- package/scripts/billing.js +192 -685
- package/scripts/context-engine.js +81 -0
- package/scripts/export-kto-pairs.js +310 -0
- package/scripts/feedback-ingest-watcher.js +290 -0
- package/scripts/feedback-loop.js +154 -9
- package/scripts/feedback-quality.js +139 -0
- package/scripts/feedback-schema.js +31 -5
- package/scripts/feedback-to-memory.js +13 -1
- package/scripts/generate-paperbanana-diagrams.sh +1 -1
- package/scripts/hook-auto-capture.sh +6 -0
- package/scripts/hook-stop-self-score.sh +51 -0
- package/scripts/install-mcp.js +168 -0
- package/scripts/jsonl-watcher.js +155 -0
- package/scripts/local-model-profile.js +207 -0
- package/scripts/pr-manager.js +112 -0
- package/scripts/prove-adapters.js +137 -15
- package/scripts/prove-automation.js +41 -8
- package/scripts/prove-lancedb.js +1 -1
- package/scripts/prove-local-intelligence.js +244 -0
- package/scripts/prove-workflow-contract.js +116 -0
- package/scripts/reminder-engine.js +132 -0
- package/scripts/risk-scorer.js +458 -0
- package/scripts/rlaif-self-audit.js +7 -1
- package/scripts/status-dashboard.js +155 -0
- package/scripts/test-coverage.js +1 -1
- package/scripts/validate-workflow-contract.js +287 -0
- package/scripts/vector-store.js +115 -17
- package/src/api/server.js +372 -25
package/bin/cli.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* npx rlhf-feedback-loop capture # capture feedback
|
|
8
8
|
* npx rlhf-feedback-loop export-dpo # export DPO training pairs
|
|
9
9
|
* npx rlhf-feedback-loop stats # feedback analytics + Revenue-at-Risk
|
|
10
|
-
* npx rlhf-feedback-loop pro # upgrade to
|
|
10
|
+
* npx rlhf-feedback-loop pro # upgrade to Context Gateway
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
'use strict';
|
|
@@ -39,23 +39,146 @@ function pkgVersion() {
|
|
|
39
39
|
// --- Platform auto-detection helpers ---
|
|
40
40
|
|
|
41
41
|
const HOME = process.env.HOME || process.env.USERPROFILE || '';
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
const MCP_SERVER_NAME = 'rlhf';
|
|
43
|
+
const LEGACY_MCP_SERVER_NAMES = ['rlhf', 'rlhf-feedback-loop', 'rlhf_feedback_loop'];
|
|
44
|
+
const PORTABLE_MCP_COMMAND = 'npx';
|
|
45
|
+
const LOCAL_MCP_COMMAND = 'node';
|
|
46
|
+
|
|
47
|
+
function portableMcpArgs() {
|
|
48
|
+
return ['-y', `rlhf-feedback-loop@${pkgVersion()}`, 'serve'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function localServerEntryPath() {
|
|
52
|
+
return path.join(PKG_ROOT, 'adapters', 'mcp', 'server-stdio.js');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function shouldUseLocalServerEntry() {
|
|
56
|
+
return fs.existsSync(path.join(PKG_ROOT, '.git'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function portableMcpEntry() {
|
|
60
|
+
return {
|
|
61
|
+
command: PORTABLE_MCP_COMMAND,
|
|
62
|
+
args: portableMcpArgs(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function localMcpEntry() {
|
|
67
|
+
return {
|
|
68
|
+
command: LOCAL_MCP_COMMAND,
|
|
69
|
+
args: [localServerEntryPath()],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function mcpEntriesMatch(entry, expectedEntry) {
|
|
74
|
+
return Boolean(
|
|
75
|
+
entry &&
|
|
76
|
+
expectedEntry &&
|
|
77
|
+
entry.command === expectedEntry.command &&
|
|
78
|
+
Array.isArray(entry.args) &&
|
|
79
|
+
Array.isArray(expectedEntry.args) &&
|
|
80
|
+
entry.args.length === expectedEntry.args.length &&
|
|
81
|
+
entry.args.every((arg, index) => arg === expectedEntry.args[index])
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function escapeRegExp(value) {
|
|
86
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function formatTomlStringArray(values) {
|
|
90
|
+
return `[${values.map((value) => JSON.stringify(value)).join(', ')}]`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function canonicalMcpEntry() {
|
|
94
|
+
return shouldUseLocalServerEntry() ? localMcpEntry() : portableMcpEntry();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function mcpSectionBlock(name = MCP_SERVER_NAME) {
|
|
98
|
+
const entry = canonicalMcpEntry();
|
|
99
|
+
return `[mcp_servers.${name}]\ncommand = "${entry.command}"\nargs = ${formatTomlStringArray(entry.args)}\n`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function upsertCodexServerConfig(content) {
|
|
103
|
+
const canonicalBlock = mcpSectionBlock();
|
|
104
|
+
const sections = LEGACY_MCP_SERVER_NAMES.map((name) => ({
|
|
105
|
+
name,
|
|
106
|
+
regex: new RegExp(`^\\[mcp_servers\\.${escapeRegExp(name)}\\]\\n[\\s\\S]*?(?=^\\[|$)`, 'm'),
|
|
107
|
+
}));
|
|
108
|
+
const matches = sections
|
|
109
|
+
.map((section) => ({ ...section, match: content.match(section.regex) }))
|
|
110
|
+
.filter((section) => section.match);
|
|
111
|
+
|
|
112
|
+
if (matches.length === 0) {
|
|
113
|
+
const prefix = content.trimEnd();
|
|
114
|
+
return {
|
|
115
|
+
changed: true,
|
|
116
|
+
content: `${prefix}${prefix ? '\n\n' : ''}${canonicalBlock}`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let nextContent = content;
|
|
121
|
+
let changed = false;
|
|
122
|
+
let canonicalPresent = false;
|
|
123
|
+
|
|
124
|
+
for (const section of matches) {
|
|
125
|
+
const normalized = canonicalBlock;
|
|
126
|
+
const current = section.match[0];
|
|
127
|
+
|
|
128
|
+
if (section.name === MCP_SERVER_NAME) {
|
|
129
|
+
canonicalPresent = true;
|
|
130
|
+
if (current !== normalized) {
|
|
131
|
+
nextContent = nextContent.replace(section.regex, normalized);
|
|
132
|
+
changed = true;
|
|
133
|
+
}
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
nextContent = nextContent.replace(section.regex, '');
|
|
138
|
+
changed = true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!canonicalPresent) {
|
|
142
|
+
const prefix = nextContent.trimEnd();
|
|
143
|
+
nextContent = `${prefix}${prefix ? '\n\n' : ''}${canonicalBlock}`;
|
|
144
|
+
changed = true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
changed,
|
|
149
|
+
content: nextContent.endsWith('\n') ? nextContent : `${nextContent}\n`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
46
152
|
|
|
47
153
|
function mergeMcpJson(filePath, label) {
|
|
154
|
+
const canonicalEntry = canonicalMcpEntry();
|
|
48
155
|
if (!fs.existsSync(filePath)) {
|
|
49
156
|
const dir = path.dirname(filePath);
|
|
50
157
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
51
|
-
fs.writeFileSync(filePath, JSON.stringify({ mcpServers: {
|
|
158
|
+
fs.writeFileSync(filePath, JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: canonicalEntry } }, null, 2) + '\n');
|
|
52
159
|
console.log(` ${label}: wrote ${path.relative(CWD, filePath)}`);
|
|
53
160
|
return true;
|
|
54
161
|
}
|
|
55
162
|
const existing = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
56
|
-
if (existing.mcpServers && existing.mcpServers['rlhf-feedback-loop']) return false;
|
|
57
163
|
existing.mcpServers = existing.mcpServers || {};
|
|
58
|
-
|
|
164
|
+
|
|
165
|
+
let changed = false;
|
|
166
|
+
const currentEntry = existing.mcpServers[MCP_SERVER_NAME];
|
|
167
|
+
if (!mcpEntriesMatch(currentEntry, canonicalEntry)) {
|
|
168
|
+
existing.mcpServers[MCP_SERVER_NAME] = canonicalEntry;
|
|
169
|
+
changed = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const legacyName of LEGACY_MCP_SERVER_NAMES) {
|
|
173
|
+
if (legacyName === MCP_SERVER_NAME) continue;
|
|
174
|
+
if (Object.prototype.hasOwnProperty.call(existing.mcpServers, legacyName)) {
|
|
175
|
+
delete existing.mcpServers[legacyName];
|
|
176
|
+
changed = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!changed) return false;
|
|
181
|
+
|
|
59
182
|
fs.writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\n');
|
|
60
183
|
console.log(` ${label}: updated ${path.relative(CWD, filePath)}`);
|
|
61
184
|
return true;
|
|
@@ -73,12 +196,37 @@ function whichExists(cmd) {
|
|
|
73
196
|
}
|
|
74
197
|
|
|
75
198
|
function setupClaude() {
|
|
76
|
-
|
|
199
|
+
const mcpChanged = mergeMcpJson(path.join(CWD, '.mcp.json'), 'Claude Code');
|
|
200
|
+
|
|
201
|
+
// Upsert Stop hook into .claude/settings.json for autonomous self-scoring
|
|
202
|
+
const settingsPath = path.join(CWD, '.claude', 'settings.json');
|
|
203
|
+
const stopHookCommand = 'bash scripts/hook-stop-self-score.sh';
|
|
204
|
+
|
|
205
|
+
let settings = { hooks: {} };
|
|
206
|
+
if (fs.existsSync(settingsPath)) {
|
|
207
|
+
try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (_) { /* fresh */ }
|
|
208
|
+
}
|
|
209
|
+
settings.hooks = settings.hooks || {};
|
|
210
|
+
|
|
211
|
+
const stopAlreadyPresent = (settings.hooks.Stop || [])
|
|
212
|
+
.some(entry => (entry.hooks || []).some(h => h.command === stopHookCommand));
|
|
213
|
+
|
|
214
|
+
let hooksChanged = false;
|
|
215
|
+
if (!stopAlreadyPresent) {
|
|
216
|
+
settings.hooks.Stop = settings.hooks.Stop || [];
|
|
217
|
+
settings.hooks.Stop.push({ hooks: [{ type: 'command', command: stopHookCommand }] });
|
|
218
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
219
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
220
|
+
console.log(' Claude Code: installed Stop hook in .claude/settings.json');
|
|
221
|
+
hooksChanged = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return mcpChanged || hooksChanged;
|
|
77
225
|
}
|
|
78
226
|
|
|
79
227
|
function setupCodex() {
|
|
80
228
|
const configPath = path.join(HOME, '.codex', 'config.toml');
|
|
81
|
-
const block =
|
|
229
|
+
const block = mcpSectionBlock();
|
|
82
230
|
if (!fs.existsSync(configPath)) {
|
|
83
231
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
84
232
|
fs.writeFileSync(configPath, block);
|
|
@@ -86,8 +234,9 @@ function setupCodex() {
|
|
|
86
234
|
return true;
|
|
87
235
|
}
|
|
88
236
|
const content = fs.readFileSync(configPath, 'utf8');
|
|
89
|
-
|
|
90
|
-
|
|
237
|
+
const updated = upsertCodexServerConfig(content);
|
|
238
|
+
if (!updated.changed) return false;
|
|
239
|
+
fs.writeFileSync(configPath, updated.content);
|
|
91
240
|
console.log(' Codex: appended MCP server to ~/.codex/config.toml');
|
|
92
241
|
return true;
|
|
93
242
|
}
|
|
@@ -96,9 +245,24 @@ function setupGemini() {
|
|
|
96
245
|
const settingsPath = path.join(HOME, '.gemini', 'settings.json');
|
|
97
246
|
if (fs.existsSync(settingsPath)) {
|
|
98
247
|
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
99
|
-
if (settings.mcpServers && settings.mcpServers['rlhf-feedback-loop']) return false;
|
|
100
248
|
settings.mcpServers = settings.mcpServers || {};
|
|
101
|
-
|
|
249
|
+
let changed = false;
|
|
250
|
+
const canonicalEntry = canonicalMcpEntry();
|
|
251
|
+
|
|
252
|
+
if (!mcpEntriesMatch(settings.mcpServers[MCP_SERVER_NAME], canonicalEntry)) {
|
|
253
|
+
settings.mcpServers[MCP_SERVER_NAME] = canonicalEntry;
|
|
254
|
+
changed = true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for (const legacyName of LEGACY_MCP_SERVER_NAMES) {
|
|
258
|
+
if (legacyName === MCP_SERVER_NAME) continue;
|
|
259
|
+
if (Object.prototype.hasOwnProperty.call(settings.mcpServers, legacyName)) {
|
|
260
|
+
delete settings.mcpServers[legacyName];
|
|
261
|
+
changed = true;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!changed) return false;
|
|
102
266
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
103
267
|
console.log(' Gemini: updated ~/.gemini/settings.json');
|
|
104
268
|
return true;
|
|
@@ -293,7 +457,7 @@ function stats() {
|
|
|
293
457
|
console.log(` Repeated Failures detected: ${data.totalNegative}`);
|
|
294
458
|
console.log(` Estimated Operational Loss: $${revenueAtRisk}`);
|
|
295
459
|
console.log(' Action Required: Run "npx rlhf-feedback-loop rules" to generate guardrails.');
|
|
296
|
-
console.log(' Strategic Recommendation: Upgrade to
|
|
460
|
+
console.log(' Strategic Recommendation: Upgrade to Context Gateway to sync these rules across your team.');
|
|
297
461
|
console.log(' Run: npx rlhf-feedback-loop pro');
|
|
298
462
|
} else {
|
|
299
463
|
console.log('\n✅ System is currently high-reliability. No immediate revenue loss detected.');
|
|
@@ -302,7 +466,7 @@ function stats() {
|
|
|
302
466
|
|
|
303
467
|
function pro() {
|
|
304
468
|
const stripeUrl = 'https://buy.stripe.com/bJe14neyU4r4f0leOD3sI02';
|
|
305
|
-
console.log('\n🚀
|
|
469
|
+
console.log('\n🚀 MCP Memory Gateway — Context Gateway');
|
|
306
470
|
console.log('─'.repeat(50));
|
|
307
471
|
console.log('Unlock the full Agentic Control Plane:');
|
|
308
472
|
console.log(' - Hosted Team API (Shared memory across all repos)');
|
|
@@ -320,6 +484,65 @@ function summary() {
|
|
|
320
484
|
console.log(feedbackSummary(Number(args.recent || 20)));
|
|
321
485
|
}
|
|
322
486
|
|
|
487
|
+
function modelFit() {
|
|
488
|
+
const { writeModelFitReport } = require(path.join(PKG_ROOT, 'scripts', 'local-model-profile'));
|
|
489
|
+
const { reportPath, report } = writeModelFitReport();
|
|
490
|
+
console.log(JSON.stringify({ reportPath, report }, null, 2));
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function risk() {
|
|
494
|
+
const args = parseArgs(process.argv.slice(3));
|
|
495
|
+
const riskScorer = require(path.join(PKG_ROOT, 'scripts', 'risk-scorer'));
|
|
496
|
+
|
|
497
|
+
if (args.context || args.tags || args.skill || args.domain || args['rubric-scores'] || args.guardrails) {
|
|
498
|
+
const { inferDomain } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
|
|
499
|
+
const { buildRubricEvaluation } = require(path.join(PKG_ROOT, 'scripts', 'rubric-engine'));
|
|
500
|
+
const historyRows = riskScorer.readJSONL(riskScorer.sequencePathFor());
|
|
501
|
+
const tags = String(args.tags || '')
|
|
502
|
+
.split(',')
|
|
503
|
+
.map((tag) => tag.trim())
|
|
504
|
+
.filter(Boolean);
|
|
505
|
+
|
|
506
|
+
let rubric = null;
|
|
507
|
+
if (args['rubric-scores'] || args.guardrails) {
|
|
508
|
+
const evaluation = buildRubricEvaluation({
|
|
509
|
+
rubricScores: args['rubric-scores'],
|
|
510
|
+
guardrails: args.guardrails,
|
|
511
|
+
});
|
|
512
|
+
rubric = {
|
|
513
|
+
rubricId: evaluation.rubricId,
|
|
514
|
+
weightedScore: evaluation.weightedScore,
|
|
515
|
+
failingCriteria: evaluation.failingCriteria,
|
|
516
|
+
failingGuardrails: evaluation.failingGuardrails,
|
|
517
|
+
judgeDisagreements: evaluation.judgeDisagreements,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const candidate = riskScorer.buildRiskCandidate({
|
|
522
|
+
context: args.context || '',
|
|
523
|
+
tags,
|
|
524
|
+
skill: args.skill || null,
|
|
525
|
+
domain: args.domain || inferDomain(tags, args.context || ''),
|
|
526
|
+
rubric,
|
|
527
|
+
filePathCount: Number(args['file-count'] || 0),
|
|
528
|
+
errorType: args['error-type'] || null,
|
|
529
|
+
}, historyRows);
|
|
530
|
+
const model = riskScorer.loadRiskModel() || riskScorer.trainAndPersistRiskModel().model;
|
|
531
|
+
console.log(JSON.stringify({
|
|
532
|
+
prediction: riskScorer.predictRisk(model, candidate),
|
|
533
|
+
candidate,
|
|
534
|
+
}, null, 2));
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const { model, modelPath } = riskScorer.trainAndPersistRiskModel();
|
|
539
|
+
console.log(JSON.stringify({
|
|
540
|
+
modelPath,
|
|
541
|
+
metrics: model.metrics,
|
|
542
|
+
summary: riskScorer.getRiskSummary(),
|
|
543
|
+
}, null, 2));
|
|
544
|
+
}
|
|
545
|
+
|
|
323
546
|
function exportDpo() {
|
|
324
547
|
const extraArgs = process.argv.slice(3).join(' ');
|
|
325
548
|
try {
|
|
@@ -359,7 +582,7 @@ function prove() {
|
|
|
359
582
|
const script = path.join(PKG_ROOT, 'scripts', `prove-${target}.js`);
|
|
360
583
|
if (!fs.existsSync(script)) {
|
|
361
584
|
console.error(`Unknown proof target: ${target}`);
|
|
362
|
-
console.error('Available: adapters, automation, attribution, lancedb, data-quality, intelligence, loop-closure, training-export');
|
|
585
|
+
console.error('Available: adapters, automation, attribution, lancedb, data-quality, intelligence, local-intelligence, loop-closure, training-export');
|
|
363
586
|
process.exit(1);
|
|
364
587
|
}
|
|
365
588
|
try {
|
|
@@ -369,40 +592,65 @@ function prove() {
|
|
|
369
592
|
}
|
|
370
593
|
}
|
|
371
594
|
|
|
372
|
-
function
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
595
|
+
function watchCmd() {
|
|
596
|
+
const args = parseArgs(process.argv.slice(3));
|
|
597
|
+
const { watch, once } = require(path.join(PKG_ROOT, 'scripts', 'jsonl-watcher'));
|
|
598
|
+
const sourceFilter = args.source || undefined;
|
|
599
|
+
if (args.once) {
|
|
600
|
+
once(sourceFilter);
|
|
601
|
+
} else {
|
|
602
|
+
watch(sourceFilter);
|
|
381
603
|
}
|
|
604
|
+
}
|
|
382
605
|
|
|
606
|
+
function status() {
|
|
607
|
+
const statusDashboard = require(path.join(PKG_ROOT, 'scripts', 'status-dashboard'));
|
|
608
|
+
const { getFeedbackPaths } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
|
|
609
|
+
const { FEEDBACK_DIR } = getFeedbackPaths();
|
|
610
|
+
const data = statusDashboard.generateStatus(FEEDBACK_DIR);
|
|
611
|
+
// printDashboard writes directly to stdout when run as main;
|
|
612
|
+
// for CLI we call the same renderer
|
|
613
|
+
statusDashboard.printDashboard
|
|
614
|
+
? statusDashboard.printDashboard(data)
|
|
615
|
+
: console.log(JSON.stringify(data, null, 2));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function serve() {
|
|
383
619
|
// Start MCP server over stdio
|
|
384
620
|
const mcpServer = path.join(PKG_ROOT, 'adapters', 'mcp', 'server-stdio.js');
|
|
385
621
|
const { startStdioServer } = require(mcpServer);
|
|
386
622
|
startStdioServer();
|
|
623
|
+
// Start watcher as a background daemon alongside MCP server
|
|
624
|
+
try {
|
|
625
|
+
const { watch } = require(path.join(PKG_ROOT, 'scripts', 'jsonl-watcher'));
|
|
626
|
+
watch();
|
|
627
|
+
} catch (_) { /* watcher is non-critical */ }
|
|
387
628
|
}
|
|
388
629
|
|
|
389
630
|
function install() {
|
|
390
|
-
console.log('Installing
|
|
631
|
+
console.log('Installing MCP Memory Gateway as a global MCP skill...');
|
|
391
632
|
const results = [
|
|
392
633
|
setupClaude(),
|
|
393
634
|
setupCodex(),
|
|
394
635
|
setupGemini(),
|
|
395
|
-
setupCursor()
|
|
636
|
+
setupCursor(),
|
|
637
|
+
setupAmp()
|
|
396
638
|
];
|
|
397
639
|
const success = results.some(r => r === true);
|
|
398
640
|
if (success) {
|
|
399
|
-
console.log('\nSuccess!
|
|
641
|
+
console.log('\nSuccess! MCP Memory Gateway is now available to your agents.');
|
|
400
642
|
console.log('Try asking your agent: "Capture positive feedback for this task"');
|
|
401
643
|
} else {
|
|
402
|
-
console.log('\
|
|
644
|
+
console.log('\nMCP Memory Gateway is already configured.');
|
|
403
645
|
}
|
|
404
646
|
}
|
|
405
647
|
|
|
648
|
+
function installMcp() {
|
|
649
|
+
const { installMcp: doInstall, parseFlags } = require(path.join(PKG_ROOT, 'scripts', 'install-mcp'));
|
|
650
|
+
const flags = parseFlags(process.argv.slice(3));
|
|
651
|
+
doInstall(flags);
|
|
652
|
+
}
|
|
653
|
+
|
|
406
654
|
function startApi() {
|
|
407
655
|
const serverPath = path.join(PKG_ROOT, 'src', 'api', 'server.js');
|
|
408
656
|
try {
|
|
@@ -418,21 +666,28 @@ function help() {
|
|
|
418
666
|
console.log('');
|
|
419
667
|
console.log('Commands:');
|
|
420
668
|
console.log(' init Scaffold .rlhf/ config + MCP server in current project');
|
|
669
|
+
console.log(' install-mcp Install RLHF MCP server into Claude Code settings (--project for local)');
|
|
421
670
|
console.log(' serve Start MCP server (stdio) — for claude/codex/gemini mcp add');
|
|
422
671
|
console.log(' capture [flags] Capture feedback (--feedback=up|down --context="..." --tags="...")');
|
|
423
672
|
console.log(' stats Show feedback analytics + Revenue-at-Risk');
|
|
424
673
|
console.log(' summary Human-readable feedback summary');
|
|
674
|
+
console.log(' model-fit Detect the current local embedding profile and write evidence report');
|
|
675
|
+
console.log(' risk [flags] Train or query the boosted local risk scorer');
|
|
425
676
|
console.log(' export-dpo Export DPO training pairs (prompt/chosen/rejected JSONL)');
|
|
426
677
|
console.log(' rules Generate prevention rules from repeated failures');
|
|
427
678
|
console.log(' self-heal Run self-healing check and auto-fix');
|
|
428
|
-
console.log(' pro Upgrade to
|
|
429
|
-
console.log(' prove [--target=X] Run proof harness (adapters|automation|attribution|lancedb|...)');
|
|
679
|
+
console.log(' pro Upgrade to Context Gateway ($10/mo)');
|
|
680
|
+
console.log(' prove [--target=X] Run proof harness (adapters|automation|attribution|lancedb|local-intelligence|...)');
|
|
681
|
+
console.log(' watch [flags] Watch .rlhf/ for external signals and ingest through pipeline (--once, --source=X)');
|
|
682
|
+
console.log(' status Show learning curve dashboard — approval trend + failure domains');
|
|
430
683
|
console.log(' start-api Start the RLHF HTTPS API server');
|
|
431
684
|
console.log(' help Show this help message');
|
|
432
685
|
console.log('');
|
|
433
686
|
console.log('Examples:');
|
|
434
687
|
console.log(' npx rlhf-feedback-loop init');
|
|
435
688
|
console.log(' npx rlhf-feedback-loop stats');
|
|
689
|
+
console.log(' npx rlhf-feedback-loop model-fit');
|
|
690
|
+
console.log(' npx rlhf-feedback-loop risk');
|
|
436
691
|
console.log(' npx rlhf-feedback-loop pro');
|
|
437
692
|
}
|
|
438
693
|
|
|
@@ -443,6 +698,9 @@ switch (COMMAND) {
|
|
|
443
698
|
case 'install':
|
|
444
699
|
install();
|
|
445
700
|
break;
|
|
701
|
+
case 'install-mcp':
|
|
702
|
+
installMcp();
|
|
703
|
+
break;
|
|
446
704
|
case 'serve':
|
|
447
705
|
case 'mcp':
|
|
448
706
|
serve();
|
|
@@ -457,6 +715,12 @@ switch (COMMAND) {
|
|
|
457
715
|
case 'summary':
|
|
458
716
|
summary();
|
|
459
717
|
break;
|
|
718
|
+
case 'model-fit':
|
|
719
|
+
modelFit();
|
|
720
|
+
break;
|
|
721
|
+
case 'risk':
|
|
722
|
+
risk();
|
|
723
|
+
break;
|
|
460
724
|
case 'export-dpo':
|
|
461
725
|
case 'dpo':
|
|
462
726
|
exportDpo();
|
|
@@ -473,6 +737,12 @@ switch (COMMAND) {
|
|
|
473
737
|
case 'prove':
|
|
474
738
|
prove();
|
|
475
739
|
break;
|
|
740
|
+
case 'watch':
|
|
741
|
+
watchCmd();
|
|
742
|
+
break;
|
|
743
|
+
case 'status':
|
|
744
|
+
status();
|
|
745
|
+
break;
|
|
476
746
|
case 'start-api':
|
|
477
747
|
startApi();
|
|
478
748
|
break;
|
package/openapi/openapi.yaml
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
|
-
title:
|
|
3
|
+
title: MCP Memory Gateway API
|
|
4
4
|
version: 1.2.0
|
|
5
5
|
description: |
|
|
6
6
|
Production API for feedback capture, schema-validated memory promotion,
|
|
7
7
|
prevention rule generation, and DPO export.
|
|
8
|
+
Bare up/down signals are logged immediately, but the API returns
|
|
9
|
+
clarification_required until one sentence explains what worked or failed.
|
|
8
10
|
servers:
|
|
9
11
|
- url: https://rlhf-feedback-loop-710216278770.us-central1.run.app
|
|
10
12
|
security:
|
|
@@ -32,13 +34,14 @@ components:
|
|
|
32
34
|
type: string
|
|
33
35
|
CaptureFeedbackRequest:
|
|
34
36
|
type: object
|
|
35
|
-
required: [signal
|
|
37
|
+
required: [signal]
|
|
36
38
|
properties:
|
|
37
39
|
signal:
|
|
38
40
|
type: string
|
|
39
41
|
enum: [up, down, positive, negative]
|
|
40
42
|
context:
|
|
41
43
|
type: string
|
|
44
|
+
description: One-sentence reason describing what worked or failed
|
|
42
45
|
whatWentWrong:
|
|
43
46
|
type: string
|
|
44
47
|
whatToChange:
|
|
@@ -155,7 +158,7 @@ paths:
|
|
|
155
158
|
'200':
|
|
156
159
|
description: Feedback accepted and promoted to memory
|
|
157
160
|
'422':
|
|
158
|
-
description: Feedback
|
|
161
|
+
description: Feedback logged only; clarification required or promotion rejected
|
|
159
162
|
'401':
|
|
160
163
|
description: Unauthorized
|
|
161
164
|
/v1/feedback/stats:
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rlhf-feedback-loop",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "
|
|
5
|
-
"homepage": "https://
|
|
3
|
+
"version": "0.6.13",
|
|
4
|
+
"description": "Feedback-Driven Development (FDD) for AI agents — capture preference signals, steer behavior via Thompson Sampling, and export KTO/DPO training pairs for downstream fine-tuning.",
|
|
5
|
+
"homepage": "https://rlhf-feedback-loop-production.up.railway.app",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/IgorGanapolsky/rlhf-feedback-loop.git"
|
|
@@ -28,39 +28,45 @@
|
|
|
28
28
|
],
|
|
29
29
|
"type": "commonjs",
|
|
30
30
|
"scripts": {
|
|
31
|
-
"test": "npm run test:schema && npm run test:loop && npm run test:dpo && 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:billing && npm run test:cli",
|
|
31
|
+
"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:workflow && npm run test:billing && npm run test:cli && npm run test:watcher",
|
|
32
32
|
"test:e2e": "node --test tests/e2e-pipeline.test.js",
|
|
33
33
|
"test:schema": "node scripts/feedback-schema.js --test",
|
|
34
34
|
"test:loop": "node scripts/feedback-loop.js --test",
|
|
35
35
|
"test:dpo": "node scripts/export-dpo-pairs.js --test",
|
|
36
|
-
"test:
|
|
37
|
-
"test:
|
|
36
|
+
"test:kto": "node --test tests/export-kto.test.js",
|
|
37
|
+
"test:api": "node --test tests/api-server.test.js tests/api-auth-config.test.js tests/mcp-server.test.js tests/adapters.test.js tests/openapi-parity.test.js tests/budget-guard.test.js tests/contextfs.test.js tests/mcp-policy.test.js tests/subagent-profiles.test.js tests/intent-router.test.js tests/rubric-engine.test.js tests/self-healing-check.test.js tests/self-heal.test.js tests/feedback-schema.test.js tests/thompson-sampling.test.js tests/feedback-sequences.test.js tests/diversity-tracking.test.js tests/vector-store.test.js tests/feedback-attribution.test.js tests/hybrid-feedback-context.test.js tests/loop-closure.test.js tests/code-reasoning.test.js tests/feedback-loop.test.js tests/feedback-inbox-read.test.js tests/feedback-to-memory.test.js tests/test-coverage.test.js tests/version-metadata.test.js tests/local-model-profile.test.js tests/risk-scorer.test.js tests/context-compaction.test.js tests/reminder-engine.test.js",
|
|
38
|
+
"test:proof": "node --test --test-concurrency=1 tests/prove-adapters.test.js tests/prove-automation.test.js tests/prove-attribution.test.js tests/prove-lancedb.test.js tests/prove-data-quality.test.js tests/prove-intelligence.test.js tests/prove-loop-closure.test.js tests/prove-subway-upgrades.test.js tests/prove-training-export.test.js tests/prove-local-intelligence.test.js tests/prove-workflow-contract.test.js",
|
|
38
39
|
"test:rlaif": "node --test tests/rlaif-self-audit.test.js tests/dpo-optimizer.test.js tests/meta-policy.test.js",
|
|
39
40
|
"test:attribution": "node --test tests/feedback-attribution.test.js tests/hybrid-feedback-context.test.js",
|
|
40
41
|
"test:quality": "node --test tests/validate-feedback.test.js",
|
|
41
42
|
"test:intelligence": "node --test tests/intelligence.test.js",
|
|
42
43
|
"test:training-export": "node --test tests/training-export.test.js",
|
|
43
44
|
"test:deployment": "node --test tests/deployment.test.js",
|
|
45
|
+
"test:workflow": "node --test tests/workflow-contract.test.js",
|
|
44
46
|
"test:billing": "node --test tests/billing.test.js",
|
|
45
|
-
"test:cli": "node --test tests/cli.test.js",
|
|
47
|
+
"test:cli": "node --test tests/cli.test.js tests/feedback-normalize.test.js tests/install-mcp.test.js",
|
|
46
48
|
"test:coverage": "node scripts/test-coverage.js",
|
|
47
49
|
"start:api": "node src/api/server.js",
|
|
48
50
|
"start:mcp": "node adapters/mcp/server-stdio.js",
|
|
51
|
+
"mcp:install": "node scripts/install-mcp.js",
|
|
49
52
|
"feedback:capture": "node .claude/scripts/feedback/capture-feedback.js",
|
|
50
53
|
"feedback:stats": "node .claude/scripts/feedback/capture-feedback.js --stats",
|
|
51
54
|
"feedback:summary": "node .claude/scripts/feedback/capture-feedback.js --summary",
|
|
52
55
|
"feedback:rules": "node .claude/scripts/feedback/capture-feedback.js --rules",
|
|
53
56
|
"feedback:export:dpo": "node scripts/export-dpo-pairs.js --from-local --output .claude/memory/feedback/dpo-pairs.jsonl",
|
|
57
|
+
"feedback:export:kto": "node scripts/export-kto-pairs.js --from-local --output .claude/memory/feedback/kto-pairs.jsonl",
|
|
54
58
|
"budget:status": "node scripts/budget-guard.js --status",
|
|
55
59
|
"diagrams:paperbanana": "bash scripts/generate-paperbanana-diagrams.sh",
|
|
56
60
|
"prove:adapters": "node scripts/prove-adapters.js",
|
|
57
61
|
"prove:automation": "node scripts/prove-automation.js",
|
|
62
|
+
"prove:workflow-contract": "node scripts/prove-workflow-contract.js",
|
|
58
63
|
"prove:lancedb": "node scripts/prove-lancedb.js",
|
|
59
64
|
"prove:rlaif": "node scripts/prove-rlaif.js",
|
|
60
65
|
"prove:attribution": "node scripts/prove-attribution.js",
|
|
61
66
|
"prove:data-quality": "node scripts/prove-data-quality.js",
|
|
62
67
|
"prove:loop-closure": "node scripts/prove-loop-closure.js",
|
|
63
68
|
"prove:intelligence": "node scripts/prove-intelligence.js",
|
|
69
|
+
"prove:local-intelligence": "node scripts/prove-local-intelligence.js",
|
|
64
70
|
"prove:training-export": "node scripts/prove-training-export.js",
|
|
65
71
|
"prove:v2-milestone": "node scripts/prove-v2-milestone.js",
|
|
66
72
|
"feedback:export:pytorch": "node scripts/export-training.js --pytorch",
|
|
@@ -77,8 +83,14 @@
|
|
|
77
83
|
"ml:incremental": "python3 scripts/train_from_feedback.py --incremental",
|
|
78
84
|
"ml:reliability": "python3 scripts/train_from_feedback.py --reliability",
|
|
79
85
|
"ml:sample": "python3 scripts/train_from_feedback.py --sample",
|
|
86
|
+
"ml:model-fit": "node scripts/local-model-profile.js",
|
|
87
|
+
"ml:risk": "node scripts/risk-scorer.js",
|
|
80
88
|
"adk:consolidate": "node scripts/adk-consolidator.js",
|
|
81
|
-
"adk:watch": "node scripts/adk-consolidator.js --watch"
|
|
89
|
+
"adk:watch": "node scripts/adk-consolidator.js --watch",
|
|
90
|
+
"pr:manage": "node scripts/pr-manager.js",
|
|
91
|
+
"watch": "node scripts/jsonl-watcher.js",
|
|
92
|
+
"status": "node scripts/status-dashboard.js",
|
|
93
|
+
"test:watcher": "node --test tests/jsonl-watcher.test.js"
|
|
82
94
|
},
|
|
83
95
|
"keywords": [
|
|
84
96
|
"rlhf",
|
|
@@ -121,7 +133,8 @@
|
|
|
121
133
|
"@google/genai": "^1.44.0",
|
|
122
134
|
"@huggingface/transformers": "^3.8.1",
|
|
123
135
|
"@lancedb/lancedb": "^0.26.2",
|
|
124
|
-
"apache-arrow": "^18.1.0"
|
|
136
|
+
"apache-arrow": "^18.1.0",
|
|
137
|
+
"stripe": "^20.4.1"
|
|
125
138
|
},
|
|
126
139
|
"mcpName": "io.github.IgorGanapolsky/rlhf-feedback-loop"
|
|
127
140
|
}
|