@timmeck/brain-core 2.36.56 → 2.36.58
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/README.md +6 -0
- package/command-center.html +133 -2
- package/dist/action/__tests__/action-bridge.test.d.ts +1 -0
- package/dist/action/__tests__/action-bridge.test.js +145 -0
- package/dist/action/__tests__/action-bridge.test.js.map +1 -0
- package/dist/action/__tests__/content-handler.test.d.ts +1 -0
- package/dist/action/__tests__/content-handler.test.js +100 -0
- package/dist/action/__tests__/content-handler.test.js.map +1 -0
- package/dist/action/__tests__/trade-handler.test.d.ts +1 -0
- package/dist/action/__tests__/trade-handler.test.js +165 -0
- package/dist/action/__tests__/trade-handler.test.js.map +1 -0
- package/dist/action/action-bridge.d.ts +95 -0
- package/dist/action/action-bridge.js +242 -0
- package/dist/action/action-bridge.js.map +1 -0
- package/dist/action/handlers/content-handler.d.ts +23 -0
- package/dist/action/handlers/content-handler.js +30 -0
- package/dist/action/handlers/content-handler.js.map +1 -0
- package/dist/action/handlers/index.d.ts +4 -0
- package/dist/action/handlers/index.js +3 -0
- package/dist/action/handlers/index.js.map +1 -0
- package/dist/action/handlers/trade-handler.d.ts +43 -0
- package/dist/action/handlers/trade-handler.js +43 -0
- package/dist/action/handlers/trade-handler.js.map +1 -0
- package/dist/action/index.d.ts +4 -0
- package/dist/action/index.js +3 -0
- package/dist/action/index.js.map +1 -0
- package/dist/agent-training/__tests__/agent-trainer.test.d.ts +1 -0
- package/dist/agent-training/__tests__/agent-trainer.test.js +158 -0
- package/dist/agent-training/__tests__/agent-trainer.test.js.map +1 -0
- package/dist/agent-training/__tests__/sub-agent-factory.test.d.ts +1 -0
- package/dist/agent-training/__tests__/sub-agent-factory.test.js +100 -0
- package/dist/agent-training/__tests__/sub-agent-factory.test.js.map +1 -0
- package/dist/agent-training/__tests__/sub-agent.test.d.ts +1 -0
- package/dist/agent-training/__tests__/sub-agent.test.js +102 -0
- package/dist/agent-training/__tests__/sub-agent.test.js.map +1 -0
- package/dist/agent-training/index.d.ts +4 -0
- package/dist/agent-training/index.js +2 -0
- package/dist/agent-training/index.js.map +1 -1
- package/dist/agent-training/sub-agent-factory.d.ts +36 -0
- package/dist/agent-training/sub-agent-factory.js +128 -0
- package/dist/agent-training/sub-agent-factory.js.map +1 -0
- package/dist/agent-training/sub-agent.d.ts +57 -0
- package/dist/agent-training/sub-agent.js +135 -0
- package/dist/agent-training/sub-agent.js.map +1 -0
- package/dist/chat/__tests__/chat-engine.test.d.ts +1 -0
- package/dist/chat/__tests__/chat-engine.test.js +128 -0
- package/dist/chat/__tests__/chat-engine.test.js.map +1 -0
- package/dist/chat/chat-engine.d.ts +55 -0
- package/dist/chat/chat-engine.js +186 -0
- package/dist/chat/chat-engine.js.map +1 -0
- package/dist/chat/index.d.ts +2 -0
- package/dist/chat/index.js +2 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/codegen/__tests__/code-forge.test.d.ts +1 -0
- package/dist/codegen/__tests__/code-forge.test.js +105 -0
- package/dist/codegen/__tests__/code-forge.test.js.map +1 -0
- package/dist/codegen/code-forge.d.ts +87 -0
- package/dist/codegen/code-forge.js +211 -0
- package/dist/codegen/code-forge.js.map +1 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +1 -0
- package/dist/codegen/index.js.map +1 -1
- package/dist/content/__tests__/auto-publisher.test.d.ts +1 -0
- package/dist/content/__tests__/auto-publisher.test.js +125 -0
- package/dist/content/__tests__/auto-publisher.test.js.map +1 -0
- package/dist/content/__tests__/content-forge.test.d.ts +1 -0
- package/dist/content/__tests__/content-forge.test.js +117 -0
- package/dist/content/__tests__/content-forge.test.js.map +1 -0
- package/dist/content/auto-publisher.d.ts +51 -0
- package/dist/content/auto-publisher.js +147 -0
- package/dist/content/auto-publisher.js.map +1 -0
- package/dist/content/content-forge.d.ts +106 -0
- package/dist/content/content-forge.js +232 -0
- package/dist/content/content-forge.js.map +1 -0
- package/dist/content/index.d.ts +4 -0
- package/dist/content/index.js +3 -0
- package/dist/content/index.js.map +1 -0
- package/dist/creative/__tests__/creative-engine.test.d.ts +1 -0
- package/dist/creative/__tests__/creative-engine.test.js +151 -0
- package/dist/creative/__tests__/creative-engine.test.js.map +1 -0
- package/dist/cross-brain/__tests__/signal-router.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/signal-router.test.js +159 -0
- package/dist/cross-brain/__tests__/signal-router.test.js.map +1 -0
- package/dist/cross-brain/signal-router.d.ts +57 -0
- package/dist/cross-brain/signal-router.js +148 -0
- package/dist/cross-brain/signal-router.js.map +1 -0
- package/dist/dashboard/__tests__/command-center-server.test.js +31 -0
- package/dist/dashboard/__tests__/command-center-server.test.js.map +1 -1
- package/dist/dashboard/command-center-server.d.ts +11 -0
- package/dist/dashboard/command-center-server.js +72 -0
- package/dist/dashboard/command-center-server.js.map +1 -1
- package/dist/dream/__tests__/dream-engine.test.d.ts +1 -0
- package/dist/dream/__tests__/dream-engine.test.js +184 -0
- package/dist/dream/__tests__/dream-engine.test.js.map +1 -0
- package/dist/feedback/__tests__/feedback-router.test.d.ts +1 -0
- package/dist/feedback/__tests__/feedback-router.test.js +173 -0
- package/dist/feedback/__tests__/feedback-router.test.js.map +1 -0
- package/dist/feedback/feedback-router.d.ts +53 -0
- package/dist/feedback/feedback-router.js +193 -0
- package/dist/feedback/feedback-router.js.map +1 -0
- package/dist/feedback/index.d.ts +2 -0
- package/dist/feedback/index.js +1 -0
- package/dist/feedback/index.js.map +1 -1
- package/dist/goals/__tests__/goal-engine.test.d.ts +1 -0
- package/dist/goals/__tests__/goal-engine.test.js +203 -0
- package/dist/goals/__tests__/goal-engine.test.js.map +1 -0
- package/dist/guardrails/__tests__/guardrail-engine.test.d.ts +1 -0
- package/dist/guardrails/__tests__/guardrail-engine.test.js +343 -0
- package/dist/guardrails/__tests__/guardrail-engine.test.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/metacognition/__tests__/auto-experiment-engine.test.d.ts +1 -0
- package/dist/metacognition/__tests__/auto-experiment-engine.test.js +218 -0
- package/dist/metacognition/__tests__/auto-experiment-engine.test.js.map +1 -0
- package/dist/metacognition/__tests__/evolution-engine.test.d.ts +1 -0
- package/dist/metacognition/__tests__/evolution-engine.test.js +473 -0
- package/dist/metacognition/__tests__/evolution-engine.test.js.map +1 -0
- package/dist/metacognition/__tests__/parameter-registry.test.d.ts +1 -0
- package/dist/metacognition/__tests__/parameter-registry.test.js +223 -0
- package/dist/metacognition/__tests__/parameter-registry.test.js.map +1 -0
- package/dist/prediction/__tests__/prediction-engine.test.d.ts +1 -0
- package/dist/prediction/__tests__/prediction-engine.test.js +244 -0
- package/dist/prediction/__tests__/prediction-engine.test.js.map +1 -0
- package/dist/research/research-orchestrator.d.ts +12 -0
- package/dist/research/research-orchestrator.js +109 -0
- package/dist/research/research-orchestrator.js.map +1 -1
- package/dist/sandbox/__tests__/code-sandbox.test.d.ts +1 -0
- package/dist/sandbox/__tests__/code-sandbox.test.js +182 -0
- package/dist/sandbox/__tests__/code-sandbox.test.js.map +1 -0
- package/dist/scanner/__tests__/signal-scanner.test.d.ts +1 -0
- package/dist/scanner/__tests__/signal-scanner.test.js +142 -0
- package/dist/scanner/__tests__/signal-scanner.test.js.map +1 -0
- package/dist/self-modification/__tests__/self-modification-engine.test.d.ts +1 -0
- package/dist/self-modification/__tests__/self-modification-engine.test.js +255 -0
- package/dist/self-modification/__tests__/self-modification-engine.test.js.map +1 -0
- package/dist/strategy/__tests__/strategy-forge.test.d.ts +1 -0
- package/dist/strategy/__tests__/strategy-forge.test.js +190 -0
- package/dist/strategy/__tests__/strategy-forge.test.js.map +1 -0
- package/dist/strategy/__tests__/strategy-mutator.test.d.ts +1 -0
- package/dist/strategy/__tests__/strategy-mutator.test.js +141 -0
- package/dist/strategy/__tests__/strategy-mutator.test.js.map +1 -0
- package/dist/strategy/index.d.ts +4 -0
- package/dist/strategy/index.js +3 -0
- package/dist/strategy/index.js.map +1 -0
- package/dist/strategy/strategy-forge.d.ts +114 -0
- package/dist/strategy/strategy-forge.js +296 -0
- package/dist/strategy/strategy-forge.js.map +1 -0
- package/dist/strategy/strategy-mutator.d.ts +42 -0
- package/dist/strategy/strategy-mutator.js +140 -0
- package/dist/strategy/strategy-mutator.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
4
|
+
getLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }),
|
|
5
|
+
}));
|
|
6
|
+
import { DreamEngine, runDreamMigration } from '../dream-engine.js';
|
|
7
|
+
describe('DreamEngine', () => {
|
|
8
|
+
let db;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
db = new Database(':memory:');
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
db.close();
|
|
14
|
+
});
|
|
15
|
+
// ── 1. Creation ────────────────────────────────────────
|
|
16
|
+
it('creates an instance and runs migration', () => {
|
|
17
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
18
|
+
expect(engine).toBeDefined();
|
|
19
|
+
// Migration should have created the tables
|
|
20
|
+
const tables = db.prepare(`
|
|
21
|
+
SELECT name FROM sqlite_master WHERE type = 'table' AND name IN ('dream_history', 'dream_state', 'dream_retrospective')
|
|
22
|
+
ORDER BY name
|
|
23
|
+
`).all();
|
|
24
|
+
expect(tables.map(t => t.name)).toEqual(['dream_history', 'dream_retrospective', 'dream_state']);
|
|
25
|
+
});
|
|
26
|
+
// ── 2. getStatus (initial) ─────────────────────────────
|
|
27
|
+
it('returns initial status with zero counters', () => {
|
|
28
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
29
|
+
const status = engine.getStatus();
|
|
30
|
+
expect(status.running).toBe(false);
|
|
31
|
+
expect(status.totalCycles).toBe(0);
|
|
32
|
+
expect(status.lastDreamAt).toBeNull();
|
|
33
|
+
expect(status.totals.memoriesConsolidated).toBe(0);
|
|
34
|
+
expect(status.totals.synapsesPruned).toBe(0);
|
|
35
|
+
expect(status.totals.memoriesArchived).toBe(0);
|
|
36
|
+
});
|
|
37
|
+
// ── 3. recordActivity ─────────────────────────────────
|
|
38
|
+
it('recordActivity updates the last activity timestamp', () => {
|
|
39
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
40
|
+
const before = Date.now();
|
|
41
|
+
engine.recordActivity();
|
|
42
|
+
const after = Date.now();
|
|
43
|
+
// Access private field via cast — recordActivity should have set lastActivityTimestamp
|
|
44
|
+
// We verify indirectly: start + timer callback should NOT trigger if just recorded activity
|
|
45
|
+
// Direct test: engine should not throw and should update internal state
|
|
46
|
+
expect(() => engine.recordActivity()).not.toThrow();
|
|
47
|
+
// Verify the engine is still functional after recording activity
|
|
48
|
+
const status = engine.getStatus();
|
|
49
|
+
expect(status.running).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
// ── 4. consolidate (basic) ────────────────────────────
|
|
52
|
+
it('runs a consolidation cycle and persists history', () => {
|
|
53
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
54
|
+
const report = engine.consolidate('manual');
|
|
55
|
+
expect(report.cycleId).toMatch(/^dream-test-/);
|
|
56
|
+
expect(report.trigger).toBe('manual');
|
|
57
|
+
expect(report.duration).toBeGreaterThanOrEqual(0);
|
|
58
|
+
expect(report.replay.memoriesReplayed).toBe(0); // no memories table
|
|
59
|
+
expect(report.pruning.synapsesPruned).toBe(0); // no synapses table
|
|
60
|
+
expect(report.compression.memoriesConsolidated).toBe(0);
|
|
61
|
+
expect(report.decay.memoriesDecayed).toBe(0);
|
|
62
|
+
expect(report.principlesDiscovered).toBe(0);
|
|
63
|
+
expect(report.journalEntryId).toBeNull();
|
|
64
|
+
// Status should reflect one cycle
|
|
65
|
+
const status = engine.getStatus();
|
|
66
|
+
expect(status.totalCycles).toBe(1);
|
|
67
|
+
expect(status.lastDreamAt).toBeGreaterThan(0);
|
|
68
|
+
});
|
|
69
|
+
// ── 5. consolidate with synapses ──────────────────────
|
|
70
|
+
it('prunes weak synapses during consolidation', () => {
|
|
71
|
+
// Create synapses table so the consolidator can interact with it
|
|
72
|
+
db.exec(`
|
|
73
|
+
CREATE TABLE synapses (
|
|
74
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
75
|
+
source_type TEXT NOT NULL,
|
|
76
|
+
source_id TEXT NOT NULL,
|
|
77
|
+
target_type TEXT NOT NULL,
|
|
78
|
+
target_id TEXT NOT NULL,
|
|
79
|
+
weight REAL NOT NULL DEFAULT 0.5,
|
|
80
|
+
last_activated_at TEXT,
|
|
81
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
82
|
+
);
|
|
83
|
+
`);
|
|
84
|
+
// Insert a weak synapse (below default threshold 0.15)
|
|
85
|
+
db.prepare(`INSERT INTO synapses (source_type, source_id, target_type, target_id, weight) VALUES (?, ?, ?, ?, ?)`).run('memory', '1', 'memory', '2', 0.05);
|
|
86
|
+
// Insert a strong synapse (above threshold)
|
|
87
|
+
db.prepare(`INSERT INTO synapses (source_type, source_id, target_type, target_id, weight) VALUES (?, ?, ?, ?, ?)`).run('memory', '3', 'memory', '4', 0.8);
|
|
88
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
89
|
+
const report = engine.consolidate('manual');
|
|
90
|
+
expect(report.pruning.synapsesPruned).toBe(1);
|
|
91
|
+
// Only the strong synapse should remain
|
|
92
|
+
const remaining = db.prepare('SELECT COUNT(*) as cnt FROM synapses').get();
|
|
93
|
+
expect(remaining.cnt).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
// ── 6. getHistory ─────────────────────────────────────
|
|
96
|
+
it('returns dream history ordered by timestamp descending', () => {
|
|
97
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
98
|
+
engine.consolidate('manual');
|
|
99
|
+
engine.consolidate('idle');
|
|
100
|
+
engine.consolidate('auto');
|
|
101
|
+
const history = engine.getHistory();
|
|
102
|
+
expect(history).toHaveLength(3);
|
|
103
|
+
// Most recent first
|
|
104
|
+
expect(history[0].trigger).toBe('auto');
|
|
105
|
+
expect(history[1].trigger).toBe('idle');
|
|
106
|
+
expect(history[2].trigger).toBe('manual');
|
|
107
|
+
});
|
|
108
|
+
it('respects the limit parameter for getHistory', () => {
|
|
109
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
110
|
+
engine.consolidate('manual');
|
|
111
|
+
engine.consolidate('idle');
|
|
112
|
+
engine.consolidate('auto');
|
|
113
|
+
const history = engine.getHistory(2);
|
|
114
|
+
expect(history).toHaveLength(2);
|
|
115
|
+
});
|
|
116
|
+
// ── 7. getPruningEfficiency ───────────────────────────
|
|
117
|
+
it('returns default pruning efficiency when no retrospective data', () => {
|
|
118
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
119
|
+
const efficiency = engine.getPruningEfficiency();
|
|
120
|
+
expect(efficiency.totalPruned).toBe(0);
|
|
121
|
+
expect(efficiency.totalReappeared).toBe(0);
|
|
122
|
+
expect(efficiency.avgRegretScore).toBe(0);
|
|
123
|
+
expect(efficiency.efficiencyRate).toBe(1);
|
|
124
|
+
});
|
|
125
|
+
it('calculates pruning efficiency from retrospective records', () => {
|
|
126
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
127
|
+
// Manually insert retrospective records with known regret scores
|
|
128
|
+
db.prepare(`
|
|
129
|
+
INSERT INTO dream_retrospective (dream_cycle_id, pruned_items, reappeared_count, regret_score, lesson)
|
|
130
|
+
VALUES (?, ?, ?, ?, ?)
|
|
131
|
+
`).run('cycle-1', JSON.stringify([{ synapseId: 1, weight: 0.1 }, { synapseId: 2, weight: 0.12 }]), 1, 0.5, 'test lesson');
|
|
132
|
+
db.prepare(`
|
|
133
|
+
INSERT INTO dream_retrospective (dream_cycle_id, pruned_items, reappeared_count, regret_score, lesson)
|
|
134
|
+
VALUES (?, ?, ?, ?, ?)
|
|
135
|
+
`).run('cycle-2', JSON.stringify([{ synapseId: 3, weight: 0.08 }]), 0, 0.0, 'no regret');
|
|
136
|
+
const efficiency = engine.getPruningEfficiency();
|
|
137
|
+
// Only records with regret_score > 0 OR reappeared_count > 0 are included (cycle-1 matches)
|
|
138
|
+
expect(efficiency.totalPruned).toBeGreaterThanOrEqual(1);
|
|
139
|
+
expect(efficiency.efficiencyRate).toBeLessThanOrEqual(1);
|
|
140
|
+
expect(efficiency.efficiencyRate).toBe(1 - efficiency.avgRegretScore);
|
|
141
|
+
});
|
|
142
|
+
// ── 8. analyzeRetrospective ───────────────────────────
|
|
143
|
+
it('analyzes retrospective and produces regret scores', () => {
|
|
144
|
+
const engine = new DreamEngine(db, { brainName: 'test' });
|
|
145
|
+
// Insert a retrospective record with pruned items
|
|
146
|
+
const prunedItems = [{ synapseId: 100, weight: 0.1 }, { synapseId: 101, weight: 0.12 }];
|
|
147
|
+
db.prepare(`
|
|
148
|
+
INSERT INTO dream_retrospective (dream_cycle_id, pruned_items)
|
|
149
|
+
VALUES (?, ?)
|
|
150
|
+
`).run('dream-test-retro-1', JSON.stringify(prunedItems));
|
|
151
|
+
const results = engine.analyzeRetrospective(5);
|
|
152
|
+
expect(results).toHaveLength(1);
|
|
153
|
+
expect(results[0].dreamCycleId).toBe('dream-test-retro-1');
|
|
154
|
+
expect(results[0].prunedItems).toHaveLength(2);
|
|
155
|
+
expect(typeof results[0].regretScore).toBe('number');
|
|
156
|
+
expect(results[0].lesson).toBeTruthy();
|
|
157
|
+
});
|
|
158
|
+
// ── 9. start and stop ─────────────────────────────────
|
|
159
|
+
it('start sets running to true, stop resets it', () => {
|
|
160
|
+
const engine = new DreamEngine(db, { brainName: 'test', intervalMs: 60_000 });
|
|
161
|
+
expect(engine.getStatus().running).toBe(false);
|
|
162
|
+
engine.start();
|
|
163
|
+
expect(engine.getStatus().running).toBe(true);
|
|
164
|
+
// Starting again should be a no-op (no duplicate timers)
|
|
165
|
+
engine.start();
|
|
166
|
+
expect(engine.getStatus().running).toBe(true);
|
|
167
|
+
engine.stop();
|
|
168
|
+
expect(engine.getStatus().running).toBe(false);
|
|
169
|
+
// Stopping again should be safe
|
|
170
|
+
engine.stop();
|
|
171
|
+
expect(engine.getStatus().running).toBe(false);
|
|
172
|
+
});
|
|
173
|
+
// ── 10. runDreamMigration is idempotent ───────────────
|
|
174
|
+
it('runDreamMigration can be called multiple times safely', () => {
|
|
175
|
+
runDreamMigration(db);
|
|
176
|
+
runDreamMigration(db);
|
|
177
|
+
runDreamMigration(db);
|
|
178
|
+
// Tables should still exist with correct structure
|
|
179
|
+
const state = db.prepare('SELECT * FROM dream_state WHERE id = 1').get();
|
|
180
|
+
expect(state).toBeDefined();
|
|
181
|
+
expect(state.total_cycles).toBe(0);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=dream-engine.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dream-engine.test.js","sourceRoot":"","sources":["../../../src/dream/__tests__/dream-engine.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
4
|
+
getLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }),
|
|
5
|
+
}));
|
|
6
|
+
import { FeedbackRouter } from '../feedback-router.js';
|
|
7
|
+
describe('FeedbackRouter', () => {
|
|
8
|
+
let db;
|
|
9
|
+
beforeEach(() => { db = new Database(':memory:'); });
|
|
10
|
+
afterEach(() => { db.close(); });
|
|
11
|
+
it('creates with empty state', () => {
|
|
12
|
+
const router = new FeedbackRouter(db);
|
|
13
|
+
const status = router.getStatus();
|
|
14
|
+
expect(status.sources).toBe(0);
|
|
15
|
+
expect(status.totalProcessed).toBe(0);
|
|
16
|
+
expect(status.totalActions).toBe(0);
|
|
17
|
+
});
|
|
18
|
+
it('registers sources', () => {
|
|
19
|
+
const router = new FeedbackRouter(db);
|
|
20
|
+
router.addSource({ name: 'test', fetch: () => [] });
|
|
21
|
+
router.addSource({ name: 'test2', fetch: () => [] });
|
|
22
|
+
expect(router.getStatus().sources).toBe(2);
|
|
23
|
+
});
|
|
24
|
+
it('processes AB test winner', async () => {
|
|
25
|
+
const router = new FeedbackRouter(db);
|
|
26
|
+
const actions = [];
|
|
27
|
+
router.setActionHandler(async (a) => { actions.push(a); });
|
|
28
|
+
router.addSource({
|
|
29
|
+
name: 'ab-test',
|
|
30
|
+
fetch: () => [{
|
|
31
|
+
source: 'ab-test',
|
|
32
|
+
type: 'ab_winner',
|
|
33
|
+
data: { winner: 'b', metric: 'click_rate', testId: 42 },
|
|
34
|
+
confidence: 0.85,
|
|
35
|
+
}],
|
|
36
|
+
});
|
|
37
|
+
const result = await router.processAll();
|
|
38
|
+
expect(result.items).toBe(1);
|
|
39
|
+
expect(result.actions).toBe(1);
|
|
40
|
+
expect(actions[0].type).toBe('adjust_parameter');
|
|
41
|
+
expect(actions[0].payload.value).toBe('b');
|
|
42
|
+
});
|
|
43
|
+
it('skips low-confidence AB winners', async () => {
|
|
44
|
+
const router = new FeedbackRouter(db);
|
|
45
|
+
const actions = [];
|
|
46
|
+
router.setActionHandler(async (a) => { actions.push(a); });
|
|
47
|
+
router.addSource({
|
|
48
|
+
name: 'ab-test',
|
|
49
|
+
fetch: () => [{
|
|
50
|
+
source: 'ab-test',
|
|
51
|
+
type: 'ab_winner',
|
|
52
|
+
data: { winner: 'a' },
|
|
53
|
+
confidence: 0.3,
|
|
54
|
+
}],
|
|
55
|
+
});
|
|
56
|
+
await router.processAll();
|
|
57
|
+
expect(actions).toHaveLength(0);
|
|
58
|
+
});
|
|
59
|
+
it('processes competitor insights with topic seed', async () => {
|
|
60
|
+
const router = new FeedbackRouter(db);
|
|
61
|
+
const actions = [];
|
|
62
|
+
router.setActionHandler(async (a) => { actions.push(a); });
|
|
63
|
+
router.addSource({
|
|
64
|
+
name: 'competitor',
|
|
65
|
+
fetch: () => [{
|
|
66
|
+
source: 'competitor',
|
|
67
|
+
type: 'competitor_insight',
|
|
68
|
+
data: { verdict: 'Competitor posts more frequently', topicSeed: 'AI trends', competitorId: 5 },
|
|
69
|
+
confidence: 0.7,
|
|
70
|
+
}],
|
|
71
|
+
});
|
|
72
|
+
await router.processAll();
|
|
73
|
+
expect(actions).toHaveLength(2); // frequency + topic seed
|
|
74
|
+
expect(actions[0].type).toBe('adjust_parameter');
|
|
75
|
+
expect(actions[1].type).toBe('creative_seed');
|
|
76
|
+
});
|
|
77
|
+
it('processes user patterns', async () => {
|
|
78
|
+
const router = new FeedbackRouter(db);
|
|
79
|
+
const actions = [];
|
|
80
|
+
router.setActionHandler(async (a) => { actions.push(a); });
|
|
81
|
+
router.addSource({
|
|
82
|
+
name: 'user-model',
|
|
83
|
+
fetch: () => [{
|
|
84
|
+
source: 'user-model',
|
|
85
|
+
type: 'user_pattern',
|
|
86
|
+
data: { activeHours: [9, 10, 14, 15, 16] },
|
|
87
|
+
confidence: 0.6,
|
|
88
|
+
}],
|
|
89
|
+
});
|
|
90
|
+
await router.processAll();
|
|
91
|
+
expect(actions).toHaveLength(1);
|
|
92
|
+
expect(actions[0].payload.parameter).toBe('optimal_posting_hours');
|
|
93
|
+
});
|
|
94
|
+
it('processes engagement feedback', async () => {
|
|
95
|
+
const router = new FeedbackRouter(db);
|
|
96
|
+
const actions = [];
|
|
97
|
+
router.setActionHandler(async (a) => { actions.push(a); });
|
|
98
|
+
router.addSource({
|
|
99
|
+
name: 'engagement',
|
|
100
|
+
fetch: () => [{
|
|
101
|
+
source: 'engagement',
|
|
102
|
+
type: 'engagement_feedback',
|
|
103
|
+
data: { avgEngagement: 42.5 },
|
|
104
|
+
confidence: 0.9,
|
|
105
|
+
}],
|
|
106
|
+
});
|
|
107
|
+
await router.processAll();
|
|
108
|
+
expect(actions).toHaveLength(1);
|
|
109
|
+
expect(actions[0].payload.value).toBe(42.5);
|
|
110
|
+
});
|
|
111
|
+
it('handles source errors gracefully', async () => {
|
|
112
|
+
const router = new FeedbackRouter(db);
|
|
113
|
+
router.addSource({
|
|
114
|
+
name: 'broken',
|
|
115
|
+
fetch: () => { throw new Error('DB locked'); },
|
|
116
|
+
});
|
|
117
|
+
const result = await router.processAll();
|
|
118
|
+
expect(result.items).toBe(0);
|
|
119
|
+
});
|
|
120
|
+
it('handles action handler errors gracefully', async () => {
|
|
121
|
+
const router = new FeedbackRouter(db);
|
|
122
|
+
router.setActionHandler(async () => { throw new Error('handler fail'); });
|
|
123
|
+
router.addSource({
|
|
124
|
+
name: 'test',
|
|
125
|
+
fetch: () => [{ source: 'test', type: 'engagement_feedback', data: { avgEngagement: 10 }, confidence: 0.9 }],
|
|
126
|
+
});
|
|
127
|
+
const result = await router.processAll();
|
|
128
|
+
expect(result.actions).toBe(1); // action counted even if handler fails
|
|
129
|
+
});
|
|
130
|
+
it('processes multiple sources in one run', async () => {
|
|
131
|
+
const router = new FeedbackRouter(db);
|
|
132
|
+
const actions = [];
|
|
133
|
+
router.setActionHandler(async (a) => { actions.push(a); });
|
|
134
|
+
router.addSource({ name: 'src1', fetch: () => [{ source: 'src1', type: 'engagement_feedback', data: { avgEngagement: 10 }, confidence: 0.9 }] });
|
|
135
|
+
router.addSource({ name: 'src2', fetch: () => [{ source: 'src2', type: 'user_pattern', data: { activeHours: [8] }, confidence: 0.6 }] });
|
|
136
|
+
const result = await router.processAll();
|
|
137
|
+
expect(result.items).toBe(2);
|
|
138
|
+
expect(result.actions).toBe(2);
|
|
139
|
+
});
|
|
140
|
+
it('logs items without actions', async () => {
|
|
141
|
+
const router = new FeedbackRouter(db);
|
|
142
|
+
router.addSource({
|
|
143
|
+
name: 'test',
|
|
144
|
+
fetch: () => [{ source: 'test', type: 'custom', data: { foo: 'bar' }, confidence: 0.3 }],
|
|
145
|
+
});
|
|
146
|
+
await router.processAll();
|
|
147
|
+
const status = router.getStatus();
|
|
148
|
+
expect(status.totalProcessed).toBe(1);
|
|
149
|
+
expect(status.totalActions).toBe(0);
|
|
150
|
+
});
|
|
151
|
+
it('passes custom items with high confidence and actionType', async () => {
|
|
152
|
+
const router = new FeedbackRouter(db);
|
|
153
|
+
const actions = [];
|
|
154
|
+
router.setActionHandler(async (a) => { actions.push(a); });
|
|
155
|
+
router.addSource({
|
|
156
|
+
name: 'custom',
|
|
157
|
+
fetch: () => [{ source: 'custom', type: 'custom', data: { actionType: 'special_action', key: 'val' }, confidence: 0.9 }],
|
|
158
|
+
});
|
|
159
|
+
await router.processAll();
|
|
160
|
+
expect(actions).toHaveLength(1);
|
|
161
|
+
expect(actions[0].type).toBe('special_action');
|
|
162
|
+
});
|
|
163
|
+
it('updates status after processing', async () => {
|
|
164
|
+
const router = new FeedbackRouter(db);
|
|
165
|
+
router.addSource({ name: 's', fetch: () => [{ source: 's', type: 'engagement_feedback', data: { avgEngagement: 5 }, confidence: 0.8 }] });
|
|
166
|
+
router.setActionHandler(async () => { });
|
|
167
|
+
await router.processAll();
|
|
168
|
+
const status = router.getStatus();
|
|
169
|
+
expect(status.totalProcessed).toBeGreaterThan(0);
|
|
170
|
+
expect(status.lastRunAt).toBeTruthy();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
//# sourceMappingURL=feedback-router.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-router.test.js","sourceRoot":"","sources":["../../../src/feedback/__tests__/feedback-router.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export interface FeedbackSource {
|
|
3
|
+
name: string;
|
|
4
|
+
/** Fetch actionable items from this source */
|
|
5
|
+
fetch: () => FeedbackItem[] | Promise<FeedbackItem[]>;
|
|
6
|
+
}
|
|
7
|
+
export interface FeedbackItem {
|
|
8
|
+
source: string;
|
|
9
|
+
type: 'ab_winner' | 'competitor_insight' | 'user_pattern' | 'engagement_feedback' | 'custom';
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
confidence: number;
|
|
12
|
+
}
|
|
13
|
+
export interface FeedbackAction {
|
|
14
|
+
type: string;
|
|
15
|
+
payload: Record<string, unknown>;
|
|
16
|
+
source: string;
|
|
17
|
+
confidence: number;
|
|
18
|
+
}
|
|
19
|
+
export interface FeedbackRouterStatus {
|
|
20
|
+
sources: number;
|
|
21
|
+
totalProcessed: number;
|
|
22
|
+
totalActions: number;
|
|
23
|
+
lastRunAt: string | null;
|
|
24
|
+
}
|
|
25
|
+
type ActionHandler = (action: FeedbackAction) => void | Promise<void>;
|
|
26
|
+
export declare function runFeedbackRouterMigration(db: Database.Database): void;
|
|
27
|
+
/**
|
|
28
|
+
* FeedbackRouter — collects feedback from multiple sources (ABTest, Competitor, UserModel, etc.)
|
|
29
|
+
* and generates ActionBridge proposals or direct engine adjustments.
|
|
30
|
+
*/
|
|
31
|
+
export declare class FeedbackRouter {
|
|
32
|
+
private readonly db;
|
|
33
|
+
private readonly sources;
|
|
34
|
+
private actionHandler;
|
|
35
|
+
private readonly stmtInsert;
|
|
36
|
+
private readonly stmtCount;
|
|
37
|
+
private readonly stmtLastRun;
|
|
38
|
+
constructor(db: Database.Database);
|
|
39
|
+
/** Register a feedback source */
|
|
40
|
+
addSource(source: FeedbackSource): void;
|
|
41
|
+
/** Set handler for generated actions (typically ActionBridge.propose) */
|
|
42
|
+
setActionHandler(handler: ActionHandler): void;
|
|
43
|
+
/** Process all sources and generate actions */
|
|
44
|
+
processAll(): Promise<{
|
|
45
|
+
items: number;
|
|
46
|
+
actions: number;
|
|
47
|
+
}>;
|
|
48
|
+
/** Get status */
|
|
49
|
+
getStatus(): FeedbackRouterStatus;
|
|
50
|
+
/** Route a feedback item to concrete actions */
|
|
51
|
+
private routeItem;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
// ── Migration ──────────────────────────────────────────────
|
|
3
|
+
export function runFeedbackRouterMigration(db) {
|
|
4
|
+
db.exec(`
|
|
5
|
+
CREATE TABLE IF NOT EXISTS feedback_router_log (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
source TEXT NOT NULL,
|
|
8
|
+
type TEXT NOT NULL,
|
|
9
|
+
action_type TEXT,
|
|
10
|
+
payload TEXT DEFAULT '{}',
|
|
11
|
+
confidence REAL DEFAULT 0,
|
|
12
|
+
timestamp INTEGER NOT NULL
|
|
13
|
+
);
|
|
14
|
+
CREATE INDEX IF NOT EXISTS idx_frl_source ON feedback_router_log(source);
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_frl_timestamp ON feedback_router_log(timestamp);
|
|
16
|
+
`);
|
|
17
|
+
}
|
|
18
|
+
// ── Router ──────────────────────────────────────────────────
|
|
19
|
+
const log = getLogger();
|
|
20
|
+
/**
|
|
21
|
+
* FeedbackRouter — collects feedback from multiple sources (ABTest, Competitor, UserModel, etc.)
|
|
22
|
+
* and generates ActionBridge proposals or direct engine adjustments.
|
|
23
|
+
*/
|
|
24
|
+
export class FeedbackRouter {
|
|
25
|
+
db;
|
|
26
|
+
sources = [];
|
|
27
|
+
actionHandler = null;
|
|
28
|
+
stmtInsert;
|
|
29
|
+
stmtCount;
|
|
30
|
+
stmtLastRun;
|
|
31
|
+
constructor(db) {
|
|
32
|
+
this.db = db;
|
|
33
|
+
runFeedbackRouterMigration(db);
|
|
34
|
+
this.stmtInsert = db.prepare(`INSERT INTO feedback_router_log (source, type, action_type, payload, confidence, timestamp) VALUES (?, ?, ?, ?, ?, ?)`);
|
|
35
|
+
this.stmtCount = db.prepare(`SELECT COUNT(*) as count FROM feedback_router_log`);
|
|
36
|
+
this.stmtLastRun = db.prepare(`SELECT MAX(timestamp) as ts FROM feedback_router_log`);
|
|
37
|
+
}
|
|
38
|
+
/** Register a feedback source */
|
|
39
|
+
addSource(source) {
|
|
40
|
+
this.sources.push(source);
|
|
41
|
+
}
|
|
42
|
+
/** Set handler for generated actions (typically ActionBridge.propose) */
|
|
43
|
+
setActionHandler(handler) {
|
|
44
|
+
this.actionHandler = handler;
|
|
45
|
+
}
|
|
46
|
+
/** Process all sources and generate actions */
|
|
47
|
+
async processAll() {
|
|
48
|
+
let totalItems = 0;
|
|
49
|
+
let totalActions = 0;
|
|
50
|
+
for (const source of this.sources) {
|
|
51
|
+
try {
|
|
52
|
+
const items = await source.fetch();
|
|
53
|
+
totalItems += items.length;
|
|
54
|
+
for (const item of items) {
|
|
55
|
+
const actions = this.routeItem(item);
|
|
56
|
+
for (const action of actions) {
|
|
57
|
+
this.stmtInsert.run(item.source, item.type, action.type, JSON.stringify(action.payload), action.confidence, Date.now());
|
|
58
|
+
if (this.actionHandler) {
|
|
59
|
+
try {
|
|
60
|
+
await this.actionHandler(action);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
log.warn(`[feedback-router] Action handler error: ${err.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
totalActions++;
|
|
67
|
+
}
|
|
68
|
+
// Log items that didn't produce actions
|
|
69
|
+
if (actions.length === 0) {
|
|
70
|
+
this.stmtInsert.run(item.source, item.type, null, JSON.stringify(item.data), item.confidence, Date.now());
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
log.warn(`[feedback-router] Source "${source.name}" error: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
log.info(`[feedback-router] Processed ${totalItems} items → ${totalActions} actions`);
|
|
79
|
+
return { items: totalItems, actions: totalActions };
|
|
80
|
+
}
|
|
81
|
+
/** Get status */
|
|
82
|
+
getStatus() {
|
|
83
|
+
const count = this.stmtCount.get().count;
|
|
84
|
+
const lastTs = this.stmtLastRun.get().ts;
|
|
85
|
+
const actionCount = this.db.prepare(`SELECT COUNT(*) as count FROM feedback_router_log WHERE action_type IS NOT NULL`).get().count;
|
|
86
|
+
return {
|
|
87
|
+
sources: this.sources.length,
|
|
88
|
+
totalProcessed: count,
|
|
89
|
+
totalActions: actionCount,
|
|
90
|
+
lastRunAt: lastTs ? new Date(lastTs).toISOString() : null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// ── Private ──────────────────────────────────────────────
|
|
94
|
+
/** Route a feedback item to concrete actions */
|
|
95
|
+
routeItem(item) {
|
|
96
|
+
const actions = [];
|
|
97
|
+
switch (item.type) {
|
|
98
|
+
case 'ab_winner': {
|
|
99
|
+
// AB test winner → adjust content parameters
|
|
100
|
+
const winner = item.data.winner;
|
|
101
|
+
const metric = item.data.metric;
|
|
102
|
+
if (winner && item.confidence >= 0.7) {
|
|
103
|
+
actions.push({
|
|
104
|
+
type: 'adjust_parameter',
|
|
105
|
+
payload: {
|
|
106
|
+
parameter: metric ?? 'content_template',
|
|
107
|
+
value: winner,
|
|
108
|
+
reason: `AB test winner: variant ${winner}`,
|
|
109
|
+
testId: item.data.testId,
|
|
110
|
+
},
|
|
111
|
+
source: item.source,
|
|
112
|
+
confidence: item.confidence,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'competitor_insight': {
|
|
118
|
+
// Competitor insight → creative seed or frequency adjustment
|
|
119
|
+
const verdict = item.data.verdict;
|
|
120
|
+
if (verdict && item.confidence >= 0.5) {
|
|
121
|
+
actions.push({
|
|
122
|
+
type: 'adjust_parameter',
|
|
123
|
+
payload: {
|
|
124
|
+
parameter: 'posting_frequency',
|
|
125
|
+
reason: `Competitor analysis: ${verdict}`,
|
|
126
|
+
competitorId: item.data.competitorId,
|
|
127
|
+
},
|
|
128
|
+
source: item.source,
|
|
129
|
+
confidence: item.confidence,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (item.data.topicSeed) {
|
|
133
|
+
actions.push({
|
|
134
|
+
type: 'creative_seed',
|
|
135
|
+
payload: {
|
|
136
|
+
topic: item.data.topicSeed,
|
|
137
|
+
reason: 'Trending competitor topic',
|
|
138
|
+
},
|
|
139
|
+
source: item.source,
|
|
140
|
+
confidence: item.confidence,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case 'user_pattern': {
|
|
146
|
+
// User model pattern → schedule optimization
|
|
147
|
+
const activeHours = item.data.activeHours;
|
|
148
|
+
if (activeHours && activeHours.length > 0) {
|
|
149
|
+
actions.push({
|
|
150
|
+
type: 'adjust_parameter',
|
|
151
|
+
payload: {
|
|
152
|
+
parameter: 'optimal_posting_hours',
|
|
153
|
+
value: activeHours,
|
|
154
|
+
reason: 'User activity pattern',
|
|
155
|
+
},
|
|
156
|
+
source: item.source,
|
|
157
|
+
confidence: item.confidence,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case 'engagement_feedback': {
|
|
163
|
+
// Engagement data → content strategy adjustment
|
|
164
|
+
const avgEngagement = item.data.avgEngagement;
|
|
165
|
+
if (typeof avgEngagement === 'number') {
|
|
166
|
+
actions.push({
|
|
167
|
+
type: 'adjust_parameter',
|
|
168
|
+
payload: {
|
|
169
|
+
parameter: 'engagement_baseline',
|
|
170
|
+
value: avgEngagement,
|
|
171
|
+
reason: 'Engagement trend update',
|
|
172
|
+
},
|
|
173
|
+
source: item.source,
|
|
174
|
+
confidence: item.confidence,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
default:
|
|
180
|
+
// Custom items pass through if high confidence
|
|
181
|
+
if (item.confidence >= 0.8 && item.data.actionType) {
|
|
182
|
+
actions.push({
|
|
183
|
+
type: item.data.actionType,
|
|
184
|
+
payload: item.data,
|
|
185
|
+
source: item.source,
|
|
186
|
+
confidence: item.confidence,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return actions;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=feedback-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-router.js","sourceRoot":"","sources":["../../src/feedback/feedback-router.ts"],"names":[],"mappings":"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"}
|