rlhf-feedback-loop 0.6.10 → 0.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +120 -74
  3. package/adapters/README.md +3 -3
  4. package/adapters/amp/skills/rlhf-feedback/SKILL.md +2 -0
  5. package/adapters/chatgpt/INSTALL.md +6 -3
  6. package/adapters/chatgpt/openapi.yaml +5 -2
  7. package/adapters/claude/.mcp.json +3 -3
  8. package/adapters/codex/config.toml +3 -3
  9. package/adapters/gemini/function-declarations.json +2 -2
  10. package/adapters/mcp/server-stdio.js +19 -5
  11. package/bin/cli.js +295 -25
  12. package/openapi/openapi.yaml +5 -2
  13. package/package.json +25 -9
  14. package/scripts/a2ui-engine.js +73 -0
  15. package/scripts/adk-consolidator.js +267 -0
  16. package/scripts/billing.js +192 -681
  17. package/scripts/code-reasoning.js +26 -1
  18. package/scripts/context-engine.js +86 -4
  19. package/scripts/contextfs.js +130 -0
  20. package/scripts/disagreement-mining.js +315 -0
  21. package/scripts/export-kto-pairs.js +310 -0
  22. package/scripts/feedback-ingest-watcher.js +290 -0
  23. package/scripts/feedback-loop.js +153 -8
  24. package/scripts/feedback-quality.js +139 -0
  25. package/scripts/feedback-schema.js +31 -5
  26. package/scripts/feedback-to-memory.js +13 -1
  27. package/scripts/hook-auto-capture.sh +6 -0
  28. package/scripts/hook-stop-self-score.sh +51 -0
  29. package/scripts/install-mcp.js +168 -0
  30. package/scripts/intent-router.js +88 -0
  31. package/scripts/jsonl-watcher.js +151 -0
  32. package/scripts/local-model-profile.js +207 -0
  33. package/scripts/pr-manager.js +112 -0
  34. package/scripts/prove-adapters.js +137 -15
  35. package/scripts/prove-attribution.js +6 -6
  36. package/scripts/prove-automation.js +41 -8
  37. package/scripts/prove-data-quality.js +16 -8
  38. package/scripts/prove-intelligence.js +7 -4
  39. package/scripts/prove-lancedb.js +7 -7
  40. package/scripts/prove-local-intelligence.js +244 -0
  41. package/scripts/prove-loop-closure.js +16 -8
  42. package/scripts/prove-training-export.js +7 -4
  43. package/scripts/prove-workflow-contract.js +116 -0
  44. package/scripts/reminder-engine.js +132 -0
  45. package/scripts/risk-scorer.js +458 -0
  46. package/scripts/rlaif-self-audit.js +7 -1
  47. package/scripts/self-heal.js +24 -4
  48. package/scripts/status-dashboard.js +155 -0
  49. package/scripts/sync-version.js +159 -0
  50. package/scripts/test-coverage.js +76 -0
  51. package/scripts/validate-workflow-contract.js +287 -0
  52. package/scripts/vector-store.js +115 -17
  53. package/src/api/server.js +372 -25
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Development Kit (ADK) Memory Consolidator
4
+ *
5
+ * 'Always-On' background service that reads disparate feedback logs and uses
6
+ * Gemini (Flash-Lite/Flash) to actively consolidate, compress, and dream up
7
+ * generalized prevention rules. This moves the system from 'passive logging'
8
+ * to 'active semantic memory consolidation'.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const { GoogleGenAI } = require('@google/genai');
16
+
17
+ const PROJECT_ROOT = path.join(__dirname, '..');
18
+ const { getFeedbackPaths, readJSONL } = require('./feedback-loop');
19
+ const { compactContext } = require('./context-engine');
20
+ const { resolveModelRole } = require('./local-model-profile');
21
+ const { trackEvent, shouldInjectReminder, injectReminder } = require('./reminder-engine');
22
+ const { validateApiKey, reportUsageToStripe } = require('./billing');
23
+
24
+ // Keep track of the last processed ID to avoid re-consolidating the exact same logs
25
+ const STATE_FILE = process.env.ADK_STATE_FILE || path.join(PROJECT_ROOT, '.rlhf', 'adk-state.json');
26
+
27
+ function ensureDir(dirPath) {
28
+ if (!fs.existsSync(dirPath)) {
29
+ fs.mkdirSync(dirPath, { recursive: true });
30
+ }
31
+ }
32
+
33
+ function loadState() {
34
+ if (fs.existsSync(STATE_FILE)) {
35
+ try {
36
+ return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
37
+ } catch {
38
+ return { lastProcessedFeedbackId: null };
39
+ }
40
+ }
41
+ return { lastProcessedFeedbackId: null };
42
+ }
43
+
44
+ function saveState(state) {
45
+ ensureDir(path.dirname(STATE_FILE));
46
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
47
+ }
48
+
49
+ const { createRuleProposal, createReasoningTrace } = require('./a2ui-engine');
50
+
51
+ function buildFakeConsolidation(anchorLogs, newLogs) {
52
+ const combined = [...anchorLogs, ...newLogs].filter(Boolean);
53
+ const connectedLogIds = combined.slice(0, 3).map((log) => log.id).filter(Boolean);
54
+ const issueHints = combined
55
+ .map((log) => log.whatWentWrong || log.context || '')
56
+ .map((text) => String(text).trim())
57
+ .filter(Boolean);
58
+
59
+ const primaryHint = issueHints[0] || 'Environment and rollout checks were skipped';
60
+ return {
61
+ consolidatedInsights: [
62
+ {
63
+ pattern: primaryHint,
64
+ rule: 'ALWAYS verify environment, approvals, and rollout prerequisites before executing workflow changes',
65
+ severity: 'high',
66
+ connectedLogIds,
67
+ },
68
+ ],
69
+ a2uiPayload: {
70
+ reasoningGraph: {
71
+ summary: 'Synthetic consolidation used for hermetic test mode.',
72
+ connections: connectedLogIds.slice(1).map((id) => ({
73
+ from: connectedLogIds[0],
74
+ to: id,
75
+ label: 'Shared failure pattern',
76
+ })),
77
+ },
78
+ },
79
+ };
80
+ }
81
+
82
+ async function consolidateMemory() {
83
+ const requestedFakeConsolidation = process.env.ADK_FAKE_CONSOLIDATION === 'true';
84
+ const useFakeConsolidation = requestedFakeConsolidation && process.env.NODE_ENV === 'test';
85
+ const apiKey = process.env.GEMINI_API_KEY;
86
+ if (requestedFakeConsolidation && !useFakeConsolidation) {
87
+ console.warn('[ADK Consolidator] Ignoring ADK_FAKE_CONSOLIDATION outside test mode.');
88
+ }
89
+ if (!useFakeConsolidation && !apiKey) {
90
+ console.warn('[ADK Consolidator] GEMINI_API_KEY is not set. Skipping active consolidation.');
91
+ return;
92
+ }
93
+
94
+ const ai = useFakeConsolidation ? null : new GoogleGenAI({ apiKey });
95
+ const paths = getFeedbackPaths();
96
+ const state = loadState();
97
+
98
+ const allLogs = readJSONL(paths.FEEDBACK_LOG_PATH);
99
+
100
+ if (allLogs.length === 0) {
101
+ console.log('[ADK Consolidator] No logs to consolidate.');
102
+ return;
103
+ }
104
+
105
+ // 1. Anchor-Memories: Always include the first 5 "foundational" logs of the session.
106
+ // These act as 'attention sinks' that provide global context and numerical anchors
107
+ // for the model's reasoning stability.
108
+ const anchorLogs = allLogs.slice(0, 5);
109
+
110
+ // 2. Incremental Window: Find where we left off
111
+ const hasPriorState = Boolean(state.lastProcessedFeedbackId);
112
+ let newLogs = [];
113
+ if (hasPriorState) {
114
+ const lastIdx = allLogs.findIndex(l => l.id === state.lastProcessedFeedbackId);
115
+ if (lastIdx !== -1) {
116
+ newLogs = allLogs.slice(lastIdx + 1);
117
+ } else {
118
+ newLogs = allLogs.slice(-50);
119
+ }
120
+ } else {
121
+ newLogs = allLogs.slice(-50);
122
+ }
123
+
124
+ // Filter anchors out of newLogs if they overlap to save tokens
125
+ const rawNewLogs = newLogs.filter(nl => !anchorLogs.some(al => al.id === nl.id));
126
+
127
+ if (rawNewLogs.length === 0 && anchorLogs.length > 0 && hasPriorState) {
128
+ console.log('[ADK Consolidator] No new logs since last consolidation cycle.');
129
+ return;
130
+ }
131
+
132
+ // Adaptive context compaction: reduce prompt size before sending to Gemini
133
+ const compactionResult = compactContext(rawNewLogs, anchorLogs, { windowSize: 30, perEntryMaxChars: 512 });
134
+ const filteredNewLogs = compactionResult.entries.filter(e => !anchorLogs.some(a => a.id === e.id));
135
+ if (compactionResult.compacted) {
136
+ console.log(`[ADK Consolidator] Context compacted: removed ${compactionResult.removedCount} entries (stage ${compactionResult.stage}).`);
137
+ }
138
+
139
+ // Resolve model via role router instead of hardcoding
140
+ const modelConfig = resolveModelRole('normal');
141
+ const activationLabel = useFakeConsolidation ? `Gemini test stub (${modelConfig.model})` : `Gemini (${modelConfig.model})`;
142
+ console.log(`[ADK Consolidator] Activating ${activationLabel} with ${anchorLogs.length} anchors and ${filteredNewLogs.length} new events...`);
143
+
144
+ const prompt = `
145
+ You are the Agent Development Kit (ADK) 'Always-On' Memory Consolidator.
146
+ Synthesize the latest feedback into generalized prevention rules AND dynamic A2UI components.
147
+
148
+ Foundational Anchors (Numerical Sinks):
149
+ ${JSON.stringify(anchorLogs.map(l => ({ id: l.id, signal: l.signal, context: l.context, whatWentWrong: l.whatWentWrong })), null, 2)}
150
+
151
+ Latest Feedback Events (Spikes):
152
+ ${JSON.stringify(filteredNewLogs.map(l => ({ id: l.id, signal: l.signal, context: l.context, whatWentWrong: l.whatWentWrong })), null, 2)}
153
+
154
+ Output ONLY valid JSON:
155
+ {
156
+ "consolidatedInsights": [
157
+ {
158
+ "pattern": "Underlying flaw",
159
+ "rule": "ALWAYS/NEVER directive",
160
+ "severity": "critical|high|medium|low",
161
+ "connectedLogIds": ["fb_1", "fb_2"]
162
+ }
163
+ ],
164
+ "a2uiPayload": {
165
+ "reasoningGraph": {
166
+ "summary": "Synthesis summary",
167
+ "connections": [{"from": "fb_1", "to": "fb_2", "label": "Same environment issue"}]
168
+ }
169
+ }
170
+ }
171
+ `;
172
+
173
+ try {
174
+ const result = useFakeConsolidation
175
+ ? buildFakeConsolidation(anchorLogs, filteredNewLogs)
176
+ : JSON.parse((await ai.models.generateContent({
177
+ model: modelConfig.model,
178
+ contents: prompt,
179
+ config: { responseMimeType: 'application/json' }
180
+ })).text);
181
+ console.log('[ADK Consolidator] Consolidation complete.');
182
+
183
+ if (result.consolidatedInsights) {
184
+ // Append to markdown (legacy fallback)
185
+ appendRules(result.consolidatedInsights, paths.PREVENTION_RULES_PATH);
186
+
187
+ // Track guardrail spikes and emit reminders when threshold is met
188
+ const criticalInsights = result.consolidatedInsights.filter(
189
+ i => i.severity === 'critical' || i.severity === 'high',
190
+ );
191
+ if (criticalInsights.length > 0) {
192
+ trackEvent('guardrail_spike');
193
+ if (shouldInjectReminder('guardrail_spike')) {
194
+ const topRule = criticalInsights[0].rule;
195
+ const reminderTurns = injectReminder([], 'guardrail_spike', { rule: topRule });
196
+ const reminderPath = path.join(PROJECT_ROOT, '.rlhf', `reminder_${Date.now()}.json`);
197
+ fs.writeFileSync(reminderPath, JSON.stringify(reminderTurns[0], null, 2));
198
+ console.log(`[ADK Consolidator] Emitted system reminder: ${reminderPath}`);
199
+ }
200
+ }
201
+
202
+ // Emit A2UI components (New Model)
203
+ result.consolidatedInsights.forEach(insight => {
204
+ const proposal = createRuleProposal(insight.pattern, insight.rule, insight.severity);
205
+ const a2uiPath = path.join(PROJECT_ROOT, '.rlhf', `a2ui_proposal_${Date.now()}.json`);
206
+ fs.writeFileSync(a2uiPath, JSON.stringify(proposal, null, 2));
207
+ console.log(`[ADK Consolidator] Emitted A2UI Proposal: ${a2uiPath}`);
208
+ });
209
+ }
210
+
211
+ state.lastProcessedFeedbackId = newLogs[newLogs.length - 1].id;
212
+ saveState(state);
213
+
214
+ // Usage-based billing: Report this consolidation to Stripe if a valid API key exists
215
+ const cloudKey = process.env.RLHF_API_KEY;
216
+ if (cloudKey) {
217
+ const validation = validateApiKey(cloudKey);
218
+ if (validation.valid && validation.metadata.subscriptionItemId) {
219
+ await reportUsageToStripe(validation.metadata.subscriptionItemId);
220
+ console.log(`[ADK Consolidator] Billable event reported to Stripe for customer: ${validation.metadata.customerId}`);
221
+ }
222
+ }
223
+
224
+ } catch (err) {
225
+ console.error('[ADK Consolidator] Consolidation failed:', err.message);
226
+ }
227
+ }
228
+
229
+ function appendRules(insights, rulesPath) {
230
+ let existingContent = '';
231
+ if (fs.existsSync(rulesPath)) {
232
+ existingContent = fs.readFileSync(rulesPath, 'utf-8');
233
+ } else {
234
+ existingContent = '# Prevention Rules\n\nGenerated from active semantic memory consolidation.\n\n';
235
+ }
236
+
237
+ let newRulesBlock = '\n## ADK Semantic Consolidations\n';
238
+ const timestamp = new Date().toISOString();
239
+ insights.forEach(insight => {
240
+ newRulesBlock += `- [${insight.severity.toUpperCase()}] **${insight.pattern}**\n - Rule: ${insight.rule} *(Consolidated at ${timestamp})*\n`;
241
+ });
242
+
243
+ const updatedContent = existingContent + newRulesBlock;
244
+ ensureDir(path.dirname(rulesPath));
245
+ fs.writeFileSync(rulesPath, updatedContent);
246
+ console.log(`[ADK Consolidator] Appended ${insights.length} new consolidated rules to ${rulesPath}`);
247
+ }
248
+
249
+ if (require.main === module) {
250
+ const args = process.argv.slice(2);
251
+ const isWatchMode = args.includes('--watch');
252
+
253
+ if (isWatchMode) {
254
+ console.log('[ADK Consolidator] Started in Always-On Watch Mode (interval: 5 minutes)');
255
+ consolidateMemory(); // Run once immediately
256
+ setInterval(() => {
257
+ consolidateMemory();
258
+ }, 5 * 60 * 1000); // Check every 5 minutes
259
+ } else {
260
+ consolidateMemory().then(() => {
261
+ console.log('[ADK Consolidator] Cycle finished.');
262
+ process.exit(0);
263
+ });
264
+ }
265
+ }
266
+
267
+ module.exports = { consolidateMemory };