@timmeck/brain-core 2.36.60 → 2.36.62
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/command-center.html +172 -1
- package/dist/action/__tests__/action-bridge.test.js +30 -0
- package/dist/action/__tests__/action-bridge.test.js.map +1 -1
- package/dist/action/action-bridge.d.ts +5 -1
- package/dist/action/action-bridge.js +18 -0
- package/dist/action/action-bridge.js.map +1 -1
- package/dist/creative/__tests__/creative-engine.test.js +31 -0
- package/dist/creative/__tests__/creative-engine.test.js.map +1 -1
- package/dist/creative/creative-engine.d.ts +6 -0
- package/dist/creative/creative-engine.js +51 -18
- package/dist/creative/creative-engine.js.map +1 -1
- package/dist/dashboard/command-center-server.d.ts +4 -0
- package/dist/dashboard/command-center-server.js +43 -1
- package/dist/dashboard/command-center-server.js.map +1 -1
- package/dist/guardrails/guardrail-engine.js +12 -4
- package/dist/guardrails/guardrail-engine.js.map +1 -1
- package/dist/llm/llm-service.d.ts +6 -0
- package/dist/llm/llm-service.js +22 -9
- package/dist/llm/llm-service.js.map +1 -1
- package/dist/research/adapters/__tests__/playwright-adapter.test.d.ts +1 -0
- package/dist/research/adapters/__tests__/playwright-adapter.test.js +64 -0
- package/dist/research/adapters/__tests__/playwright-adapter.test.js.map +1 -0
- package/dist/research/adapters/playwright-adapter.js +28 -2
- package/dist/research/adapters/playwright-adapter.js.map +1 -1
- package/dist/research/research-orchestrator.d.ts +13 -0
- package/dist/research/research-orchestrator.js +119 -0
- package/dist/research/research-orchestrator.js.map +1 -1
- package/dist/teaching/__tests__/teaching.test.js +37 -0
- package/dist/teaching/__tests__/teaching.test.js.map +1 -1
- package/dist/teaching/teaching-protocol.js +11 -0
- package/dist/teaching/teaching-protocol.js.map +1 -1
- package/dist/watchdog/watchdog-service.js +11 -5
- package/dist/watchdog/watchdog-service.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
vi.mock('../../../utils/logger.js', () => ({
|
|
3
|
+
getLogger: () => ({
|
|
4
|
+
info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(),
|
|
5
|
+
}),
|
|
6
|
+
}));
|
|
7
|
+
import { PlaywrightAdapter } from '../playwright-adapter.js';
|
|
8
|
+
describe('PlaywrightAdapter', () => {
|
|
9
|
+
let adapter;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
adapter = new PlaywrightAdapter();
|
|
12
|
+
});
|
|
13
|
+
describe('getBrowser health check', () => {
|
|
14
|
+
it('returns fresh instance after simulated disconnect', async () => {
|
|
15
|
+
// Simulate a cached browser that reports disconnected
|
|
16
|
+
const mockDisconnectedBrowser = {
|
|
17
|
+
isConnected: () => false,
|
|
18
|
+
close: vi.fn(),
|
|
19
|
+
};
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
adapter.browser = mockDisconnectedBrowser;
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
adapter.available = true;
|
|
24
|
+
// Access private getBrowser
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
const getBrowser = adapter.getBrowser.bind(adapter);
|
|
27
|
+
// After health check, browser should be null (disconnected detected)
|
|
28
|
+
// The actual re-launch would fail without playwright installed,
|
|
29
|
+
// but we verify the disconnect detection works
|
|
30
|
+
try {
|
|
31
|
+
await getBrowser();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Expected — playwright not installed in test env
|
|
35
|
+
}
|
|
36
|
+
// The disconnected browser should have been cleared
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
expect(adapter.browser).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
it('reuses connected browser', async () => {
|
|
41
|
+
const mockConnectedBrowser = {
|
|
42
|
+
isConnected: () => true,
|
|
43
|
+
newContext: vi.fn(),
|
|
44
|
+
};
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
+
adapter.browser = mockConnectedBrowser;
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
|
+
const result = await adapter.getBrowser();
|
|
49
|
+
expect(result).toBe(mockConnectedBrowser);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('shutdown', () => {
|
|
53
|
+
it('closes browser and nulls reference', async () => {
|
|
54
|
+
const mockBrowser = { close: vi.fn().mockResolvedValue(undefined) };
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
adapter.browser = mockBrowser;
|
|
57
|
+
await adapter.shutdown();
|
|
58
|
+
expect(mockBrowser.close).toHaveBeenCalled();
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
expect(adapter.browser).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=playwright-adapter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-adapter.test.js","sourceRoot":"","sources":["../../../../src/research/adapters/__tests__/playwright-adapter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE9D,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAChB,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;KAC7D,CAAC;CACH,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,OAA0B,CAAC;IAE/B,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,IAAI,iBAAiB,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,sDAAsD;YACtD,MAAM,uBAAuB,GAAG;gBAC9B,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK;gBACxB,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;aACf,CAAC;YACF,8DAA8D;YAC7D,OAAe,CAAC,OAAO,GAAG,uBAAuB,CAAC;YACnD,8DAA8D;YAC7D,OAAe,CAAC,SAAS,GAAG,IAAI,CAAC;YAElC,4BAA4B;YAC5B,8DAA8D;YAC9D,MAAM,UAAU,GAAI,OAAe,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE7D,qEAAqE;YACrE,gEAAgE;YAChE,+CAA+C;YAC/C,IAAI,CAAC;gBACH,MAAM,UAAU,EAAE,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,kDAAkD;YACpD,CAAC;YAED,oDAAoD;YACpD,8DAA8D;YAC9D,MAAM,CAAE,OAAe,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,MAAM,oBAAoB,GAAG;gBAC3B,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;gBACvB,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;aACpB,CAAC;YACF,8DAA8D;YAC7D,OAAe,CAAC,OAAO,GAAG,oBAAoB,CAAC;YAEhD,8DAA8D;YAC9D,MAAM,MAAM,GAAG,MAAO,OAAe,CAAC,UAAU,EAAE,CAAC;YACnD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,WAAW,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC;YACpE,8DAA8D;YAC7D,OAAe,CAAC,OAAO,GAAG,WAAW,CAAC;YAEvC,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;YAEzB,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,gBAAgB,EAAE,CAAC;YAC7C,8DAA8D;YAC9D,MAAM,CAAE,OAAe,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -62,9 +62,17 @@ export class PlaywrightAdapter {
|
|
|
62
62
|
* Renders JavaScript, waits for content, then extracts text.
|
|
63
63
|
*/
|
|
64
64
|
async extract(url, options = {}) {
|
|
65
|
+
// Global timeout guard: abort entire extraction after 30s
|
|
66
|
+
const extractTimeout = options.timeout ?? 30_000;
|
|
67
|
+
const abortController = new AbortController();
|
|
68
|
+
const timeoutId = setTimeout(() => abortController.abort(), extractTimeout + 5_000);
|
|
65
69
|
try {
|
|
66
70
|
if (!(await this.checkAvailable()))
|
|
67
71
|
return null;
|
|
72
|
+
if (abortController.signal.aborted) {
|
|
73
|
+
log.warn(`[playwright] Extraction aborted before start: ${url}`);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
68
76
|
const browser = await this.getBrowser();
|
|
69
77
|
const context = await browser.newContext({
|
|
70
78
|
userAgent: 'BrainEcosystem/1.0 (Research Bot)',
|
|
@@ -73,7 +81,7 @@ export class PlaywrightAdapter {
|
|
|
73
81
|
try {
|
|
74
82
|
await page.goto(url, {
|
|
75
83
|
waitUntil: 'networkidle',
|
|
76
|
-
timeout:
|
|
84
|
+
timeout: extractTimeout,
|
|
77
85
|
});
|
|
78
86
|
if (options.waitForSelector) {
|
|
79
87
|
await page.waitForSelector(options.waitForSelector, { timeout: 10_000 }).catch(() => { });
|
|
@@ -106,9 +114,25 @@ export class PlaywrightAdapter {
|
|
|
106
114
|
log.warn(`[playwright] Error extracting ${url}: ${err.message}`);
|
|
107
115
|
return null;
|
|
108
116
|
}
|
|
117
|
+
finally {
|
|
118
|
+
clearTimeout(timeoutId);
|
|
119
|
+
}
|
|
109
120
|
}
|
|
110
121
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
122
|
async getBrowser() {
|
|
123
|
+
// Health check: verify cached browser is still connected
|
|
124
|
+
if (this.browser) {
|
|
125
|
+
try {
|
|
126
|
+
if (typeof this.browser.isConnected === 'function' && !this.browser.isConnected()) {
|
|
127
|
+
log.warn('[playwright] Browser disconnected — relaunching');
|
|
128
|
+
this.browser = null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
log.warn('[playwright] Browser health check failed — relaunching');
|
|
133
|
+
this.browser = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
112
136
|
if (this.browser)
|
|
113
137
|
return this.browser;
|
|
114
138
|
const pwPath = 'playwright';
|
|
@@ -122,7 +146,9 @@ export class PlaywrightAdapter {
|
|
|
122
146
|
try {
|
|
123
147
|
await this.browser.close();
|
|
124
148
|
}
|
|
125
|
-
catch {
|
|
149
|
+
catch (err) {
|
|
150
|
+
log.debug(`[playwright] Browser close error: ${err.message}`);
|
|
151
|
+
}
|
|
126
152
|
this.browser = null;
|
|
127
153
|
}
|
|
128
154
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-adapter.js","sourceRoot":"","sources":["../../../src/research/adapters/playwright-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;AAExB,MAAM,OAAO,iBAAiB;IACnB,IAAI,GAAG,YAAY,CAAC;IAC7B,8DAA8D;IACtD,OAAO,GAAQ,IAAI,CAAC;IACpB,SAAS,GAAmB,IAAI,CAAC;IAEzC,SAAS;QACP,iDAAiD;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,KAAK;QACT,6DAA6D;QAC7D,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc;QAClB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,YAAY,CAAC;YAC5B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC;YACpE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,GAAG,CAAC,KAAK,CAAC,4EAA4E,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,UAIvB,EAAE;QAMJ,IAAI,CAAC;YACH,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"playwright-adapter.js","sourceRoot":"","sources":["../../../src/research/adapters/playwright-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -62,6 +62,8 @@ export interface ResearchOrchestratorConfig {
|
|
|
62
62
|
agendaEvery?: number;
|
|
63
63
|
/** Journal reflection every N cycles. Default: 10 */
|
|
64
64
|
reflectEvery?: number;
|
|
65
|
+
/** Minimum cycle duration in ms. Prevents tight-looping. Default: 5000 */
|
|
66
|
+
minCycleDurationMs?: number;
|
|
65
67
|
}
|
|
66
68
|
export declare class ResearchOrchestrator {
|
|
67
69
|
readonly selfObserver: SelfObserver;
|
|
@@ -142,9 +144,11 @@ export declare class ResearchOrchestrator {
|
|
|
142
144
|
private brainName;
|
|
143
145
|
private feedbackTimer;
|
|
144
146
|
private cycleCount;
|
|
147
|
+
private lastDesireActuationCycle;
|
|
145
148
|
private distillEvery;
|
|
146
149
|
private agendaEvery;
|
|
147
150
|
private reflectEvery;
|
|
151
|
+
private minCycleDurationMs;
|
|
148
152
|
private log;
|
|
149
153
|
/** Tracks how many times each suggestion key has been emitted without being resolved. */
|
|
150
154
|
private suggestionHistory;
|
|
@@ -255,6 +259,15 @@ export declare class ResearchOrchestrator {
|
|
|
255
259
|
setCreativeEngine(engine: import('../creative/creative-engine.js').CreativeEngine): void;
|
|
256
260
|
/** Set the ActionBridgeEngine — risk-assessed action execution. */
|
|
257
261
|
setActionBridge(bridge: import('../action/action-bridge.js').ActionBridgeEngine): void;
|
|
262
|
+
/** Register outcome handler — called when actions complete/fail. */
|
|
263
|
+
setActionOutcomeHandler(handler: (action: {
|
|
264
|
+
source: string;
|
|
265
|
+
type: string;
|
|
266
|
+
title: string;
|
|
267
|
+
}, outcome: {
|
|
268
|
+
success: boolean;
|
|
269
|
+
result: unknown;
|
|
270
|
+
}) => void): void;
|
|
258
271
|
/** Set the CrossBrainSignalRouter — bidirectional signal routing. */
|
|
259
272
|
setSignalRouter(router: import('../cross-brain/signal-router.js').CrossBrainSignalRouter): void;
|
|
260
273
|
/** Set the ContentForge — autonomous content pipeline. */
|
|
@@ -93,9 +93,11 @@ export class ResearchOrchestrator {
|
|
|
93
93
|
brainName;
|
|
94
94
|
feedbackTimer = null;
|
|
95
95
|
cycleCount = 0;
|
|
96
|
+
lastDesireActuationCycle = 0;
|
|
96
97
|
distillEvery;
|
|
97
98
|
agendaEvery;
|
|
98
99
|
reflectEvery;
|
|
100
|
+
minCycleDurationMs;
|
|
99
101
|
log = getLogger();
|
|
100
102
|
/** Tracks how many times each suggestion key has been emitted without being resolved. */
|
|
101
103
|
suggestionHistory = new Map();
|
|
@@ -113,6 +115,7 @@ export class ResearchOrchestrator {
|
|
|
113
115
|
this.distillEvery = config.distillEvery ?? 5;
|
|
114
116
|
this.agendaEvery = config.agendaEvery ?? 3;
|
|
115
117
|
this.reflectEvery = config.reflectEvery ?? 10;
|
|
118
|
+
this.minCycleDurationMs = config.minCycleDurationMs ?? 5000;
|
|
116
119
|
this.causalGraph = causalGraph ?? null;
|
|
117
120
|
this.selfObserver = new SelfObserver(db, { brainName: config.brainName });
|
|
118
121
|
this.adaptiveStrategy = new AdaptiveStrategyEngine(db, { brainName: config.brainName });
|
|
@@ -277,6 +280,12 @@ export class ResearchOrchestrator {
|
|
|
277
280
|
setCreativeEngine(engine) { this.creativeEngine = engine; }
|
|
278
281
|
/** Set the ActionBridgeEngine — risk-assessed action execution. */
|
|
279
282
|
setActionBridge(bridge) { this.actionBridge = bridge; }
|
|
283
|
+
/** Register outcome handler — called when actions complete/fail. */
|
|
284
|
+
setActionOutcomeHandler(handler) {
|
|
285
|
+
if (this.actionBridge) {
|
|
286
|
+
this.actionBridge.onOutcome((action, outcome) => handler(action, outcome));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
280
289
|
/** Set the CrossBrainSignalRouter — bidirectional signal routing. */
|
|
281
290
|
setSignalRouter(router) { this.signalRouter = router; }
|
|
282
291
|
/** Set the ContentForge — autonomous content pipeline. */
|
|
@@ -380,6 +389,16 @@ export class ResearchOrchestrator {
|
|
|
380
389
|
this.cycleCount++;
|
|
381
390
|
const start = Date.now();
|
|
382
391
|
const ts = this.thoughtStream;
|
|
392
|
+
const stepTimings = [];
|
|
393
|
+
const profileStep = (name, fn) => {
|
|
394
|
+
const t0 = Date.now();
|
|
395
|
+
fn();
|
|
396
|
+
const elapsed = Date.now() - t0;
|
|
397
|
+
if (elapsed > 1000) {
|
|
398
|
+
stepTimings.push({ step: name, ms: elapsed });
|
|
399
|
+
this.log.warn(`[orchestrator] Slow step: ${name} took ${elapsed}ms`);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
383
402
|
this.log.info(`[orchestrator] ─── Feedback Cycle #${this.cycleCount} ───`);
|
|
384
403
|
// Fix 6: Auto-instrument research cycles with TraceCollector
|
|
385
404
|
let traceId;
|
|
@@ -2426,6 +2445,8 @@ export class ResearchOrchestrator {
|
|
|
2426
2445
|
// Step 58: CreativeEngine — cross-pollination (every 20 cycles)
|
|
2427
2446
|
if (this.creativeEngine && this.cycleCount % 20 === 0) {
|
|
2428
2447
|
try {
|
|
2448
|
+
const debugInfo = this.creativeEngine.getDebugInfo();
|
|
2449
|
+
this.log.debug(`[orchestrator] Step 58: ${debugInfo.principlesCount} principles, domains: ${JSON.stringify(debugInfo.domains)}`);
|
|
2429
2450
|
ts?.emit('creative', 'discovering', 'Step 58: Cross-pollinating ideas...', 'routine');
|
|
2430
2451
|
const insights = this.creativeEngine.crossPollinate();
|
|
2431
2452
|
if (insights.length > 0) {
|
|
@@ -2441,6 +2462,9 @@ export class ResearchOrchestrator {
|
|
|
2441
2462
|
data: { insightCount: insights.length, converted },
|
|
2442
2463
|
});
|
|
2443
2464
|
}
|
|
2465
|
+
else {
|
|
2466
|
+
ts?.emit('creative', 'reflecting', `Step 58: 0 insights (${debugInfo.principlesCount} principles, ${Object.keys(debugInfo.domains).length} domains)`, 'notable');
|
|
2467
|
+
}
|
|
2444
2468
|
}
|
|
2445
2469
|
catch (err) {
|
|
2446
2470
|
this.log.warn(`[orchestrator] Step 58 (creative) error: ${err.message}`);
|
|
@@ -2591,6 +2615,92 @@ export class ResearchOrchestrator {
|
|
|
2591
2615
|
this.log.warn(`[orchestrator] Step 63 (signal emission) error: ${err.message}`);
|
|
2592
2616
|
}
|
|
2593
2617
|
}
|
|
2618
|
+
// Step 64: DesireActuator — convert top desire to action (every 15 cycles)
|
|
2619
|
+
if (this.actionBridge && this.cycleCount - this.lastDesireActuationCycle >= 15) {
|
|
2620
|
+
try {
|
|
2621
|
+
const desires = this.getDesires();
|
|
2622
|
+
const topDesire = desires.find(d => d.priority >= 5);
|
|
2623
|
+
if (topDesire) {
|
|
2624
|
+
ts?.emit('desire', 'analyzing', `Step 64: Actuating desire "${topDesire.key}" (P${topDesire.priority})...`, 'notable');
|
|
2625
|
+
// Map desire key → action type
|
|
2626
|
+
let actionType = 'create_goal';
|
|
2627
|
+
if (topDesire.key.startsWith('contradiction_')) {
|
|
2628
|
+
actionType = 'start_mission';
|
|
2629
|
+
}
|
|
2630
|
+
else if (topDesire.key.startsWith('no_predictions') || topDesire.key.startsWith('low_accuracy')) {
|
|
2631
|
+
actionType = 'adjust_parameter';
|
|
2632
|
+
}
|
|
2633
|
+
const actionId = this.actionBridge.propose({
|
|
2634
|
+
source: 'desire',
|
|
2635
|
+
type: actionType,
|
|
2636
|
+
title: `Desire: ${topDesire.suggestion.substring(0, 80)}`,
|
|
2637
|
+
description: topDesire.suggestion,
|
|
2638
|
+
confidence: Math.min(topDesire.priority / 10, 0.9),
|
|
2639
|
+
payload: { desireKey: topDesire.key, priority: topDesire.priority, alternatives: topDesire.alternatives },
|
|
2640
|
+
});
|
|
2641
|
+
if (actionId > 0) {
|
|
2642
|
+
this.log.info(`[orchestrator] Step 64: Desire "${topDesire.key}" → Action #${actionId} (${actionType})`);
|
|
2643
|
+
this.journal.write({
|
|
2644
|
+
title: `Desire Actuation: ${topDesire.key}`,
|
|
2645
|
+
content: `Converted desire (P${topDesire.priority}) to ${actionType} action #${actionId}: ${topDesire.suggestion}`,
|
|
2646
|
+
type: 'insight',
|
|
2647
|
+
significance: 'notable',
|
|
2648
|
+
tags: [this.brainName, 'desire', 'actuation'],
|
|
2649
|
+
references: [],
|
|
2650
|
+
data: { desireKey: topDesire.key, actionId, actionType },
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
this.lastDesireActuationCycle = this.cycleCount;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
catch (err) {
|
|
2657
|
+
this.log.warn(`[orchestrator] Step 64 (desire actuation) error: ${err.message}`);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
// Step 65: Action-Outcome Review (every 20 cycles)
|
|
2661
|
+
if (this.actionBridge && this.cycleCount % 20 === 0) {
|
|
2662
|
+
try {
|
|
2663
|
+
const history = this.actionBridge.getHistory(10);
|
|
2664
|
+
const recentCompleted = history.filter(a => a.status === 'completed' && a.outcome?.success);
|
|
2665
|
+
const recentFailed = history.filter(a => a.status === 'failed' || (a.outcome && !a.outcome.success));
|
|
2666
|
+
if (recentCompleted.length > 0 || recentFailed.length > 0) {
|
|
2667
|
+
ts?.emit('orchestrator', 'reflecting', `Step 65: Reviewing ${recentCompleted.length} successes, ${recentFailed.length} failures`, 'routine');
|
|
2668
|
+
// Success → journal lesson learned
|
|
2669
|
+
for (const action of recentCompleted.slice(0, 3)) {
|
|
2670
|
+
const lesson = action.outcome?.learnedLesson ?? `Action "${action.title}" succeeded`;
|
|
2671
|
+
this.journal.write({
|
|
2672
|
+
title: `Action Outcome: ${action.title}`,
|
|
2673
|
+
content: `${action.type} from ${action.source} succeeded. Lesson: ${lesson}`,
|
|
2674
|
+
type: 'insight',
|
|
2675
|
+
significance: 'routine',
|
|
2676
|
+
tags: [this.brainName, 'action-outcome', 'success'],
|
|
2677
|
+
references: [],
|
|
2678
|
+
data: { actionId: action.id, type: action.type, source: action.source },
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
// Failure → journal + hypothesis rejection
|
|
2682
|
+
for (const action of recentFailed.slice(0, 3)) {
|
|
2683
|
+
this.journal.write({
|
|
2684
|
+
title: `Action Failed: ${action.title}`,
|
|
2685
|
+
content: `${action.type} from ${action.source} failed: ${action.outcome?.result ?? 'unknown'}`,
|
|
2686
|
+
type: 'anomaly',
|
|
2687
|
+
significance: 'notable',
|
|
2688
|
+
tags: [this.brainName, 'action-outcome', 'failure'],
|
|
2689
|
+
references: [],
|
|
2690
|
+
data: { actionId: action.id, type: action.type, source: action.source },
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
if (this.metaCognitionLayer) {
|
|
2694
|
+
this.metaCognitionLayer.recordStep('action_outcome_review', this.cycleCount, {
|
|
2695
|
+
insights: recentCompleted.length + recentFailed.length,
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
catch (err) {
|
|
2701
|
+
this.log.warn(`[orchestrator] Step 65 (outcome review) error: ${err.message}`);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2594
2704
|
const duration = Date.now() - start;
|
|
2595
2705
|
ts?.emit('orchestrator', 'reflecting', `Feedback Cycle #${this.cycleCount} complete (${duration}ms)`);
|
|
2596
2706
|
this.log.info(`[orchestrator] ─── Feedback Cycle #${this.cycleCount} complete (${duration}ms) ───`);
|
|
@@ -2616,6 +2726,15 @@ export class ResearchOrchestrator {
|
|
|
2616
2726
|
}
|
|
2617
2727
|
catch { /* checkpoint save should never break the cycle */ }
|
|
2618
2728
|
}
|
|
2729
|
+
// Step-profiling summary: log slow steps if any
|
|
2730
|
+
if (stepTimings.length > 0) {
|
|
2731
|
+
this.log.warn(`[orchestrator] Cycle #${this.cycleCount} slow steps: ${stepTimings.map(s => `${s.step}(${s.ms}ms)`).join(', ')}`);
|
|
2732
|
+
}
|
|
2733
|
+
// Cycle-pacing: ensure minimum cycle duration to prevent tight-looping
|
|
2734
|
+
const remaining = this.minCycleDurationMs - duration;
|
|
2735
|
+
if (remaining > 0) {
|
|
2736
|
+
await new Promise(resolve => setTimeout(resolve, remaining));
|
|
2737
|
+
}
|
|
2619
2738
|
}
|
|
2620
2739
|
/** Analyze Brain's own state and generate concrete improvement suggestions.
|
|
2621
2740
|
* Tracks suggestion history — if a suggestion repeats 3+ times without resolution,
|