@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.
Files changed (34) hide show
  1. package/command-center.html +172 -1
  2. package/dist/action/__tests__/action-bridge.test.js +30 -0
  3. package/dist/action/__tests__/action-bridge.test.js.map +1 -1
  4. package/dist/action/action-bridge.d.ts +5 -1
  5. package/dist/action/action-bridge.js +18 -0
  6. package/dist/action/action-bridge.js.map +1 -1
  7. package/dist/creative/__tests__/creative-engine.test.js +31 -0
  8. package/dist/creative/__tests__/creative-engine.test.js.map +1 -1
  9. package/dist/creative/creative-engine.d.ts +6 -0
  10. package/dist/creative/creative-engine.js +51 -18
  11. package/dist/creative/creative-engine.js.map +1 -1
  12. package/dist/dashboard/command-center-server.d.ts +4 -0
  13. package/dist/dashboard/command-center-server.js +43 -1
  14. package/dist/dashboard/command-center-server.js.map +1 -1
  15. package/dist/guardrails/guardrail-engine.js +12 -4
  16. package/dist/guardrails/guardrail-engine.js.map +1 -1
  17. package/dist/llm/llm-service.d.ts +6 -0
  18. package/dist/llm/llm-service.js +22 -9
  19. package/dist/llm/llm-service.js.map +1 -1
  20. package/dist/research/adapters/__tests__/playwright-adapter.test.d.ts +1 -0
  21. package/dist/research/adapters/__tests__/playwright-adapter.test.js +64 -0
  22. package/dist/research/adapters/__tests__/playwright-adapter.test.js.map +1 -0
  23. package/dist/research/adapters/playwright-adapter.js +28 -2
  24. package/dist/research/adapters/playwright-adapter.js.map +1 -1
  25. package/dist/research/research-orchestrator.d.ts +13 -0
  26. package/dist/research/research-orchestrator.js +119 -0
  27. package/dist/research/research-orchestrator.js.map +1 -1
  28. package/dist/teaching/__tests__/teaching.test.js +37 -0
  29. package/dist/teaching/__tests__/teaching.test.js.map +1 -1
  30. package/dist/teaching/teaching-protocol.js +11 -0
  31. package/dist/teaching/teaching-protocol.js.map +1 -1
  32. package/dist/watchdog/watchdog-service.js +11 -5
  33. package/dist/watchdog/watchdog-service.js.map +1 -1
  34. package/package.json +1 -1
@@ -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: options.timeout ?? 30_000,
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 { /* best effort */ }
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":"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"}
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,