@timmeck/brain-core 2.36.11 → 2.36.14
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/dist/cross-brain/__tests__/borg-sync-engine.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/borg-sync-engine.test.js +240 -0
- package/dist/cross-brain/__tests__/borg-sync-engine.test.js.map +1 -0
- package/dist/cross-brain/borg-sync-engine.d.ts +62 -0
- package/dist/cross-brain/borg-sync-engine.js +215 -0
- package/dist/cross-brain/borg-sync-engine.js.map +1 -0
- package/dist/cross-brain/borg-types.d.ts +37 -0
- package/dist/cross-brain/borg-types.js +9 -0
- package/dist/cross-brain/borg-types.js.map +1 -0
- package/dist/embeddings/engine.js +2 -1
- package/dist/embeddings/engine.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/__tests__/anthropic-provider.test.d.ts +1 -0
- package/dist/llm/__tests__/anthropic-provider.test.js +121 -0
- package/dist/llm/__tests__/anthropic-provider.test.js.map +1 -0
- package/dist/llm/__tests__/llm-service.test.js +181 -40
- package/dist/llm/__tests__/llm-service.test.js.map +1 -1
- package/dist/llm/__tests__/ollama-embedding.test.d.ts +1 -0
- package/dist/llm/__tests__/ollama-embedding.test.js +128 -0
- package/dist/llm/__tests__/ollama-embedding.test.js.map +1 -0
- package/dist/llm/__tests__/ollama-provider.test.d.ts +1 -0
- package/dist/llm/__tests__/ollama-provider.test.js +213 -0
- package/dist/llm/__tests__/ollama-provider.test.js.map +1 -0
- package/dist/llm/__tests__/provider.test.d.ts +1 -0
- package/dist/llm/__tests__/provider.test.js +126 -0
- package/dist/llm/__tests__/provider.test.js.map +1 -0
- package/dist/llm/anthropic-provider.d.ts +41 -0
- package/dist/llm/anthropic-provider.js +86 -0
- package/dist/llm/anthropic-provider.js.map +1 -0
- package/dist/llm/index.d.ts +9 -1
- package/dist/llm/index.js +4 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/llm-service.d.ts +55 -7
- package/dist/llm/llm-service.js +184 -82
- package/dist/llm/llm-service.js.map +1 -1
- package/dist/llm/ollama-embedding.d.ts +46 -0
- package/dist/llm/ollama-embedding.js +93 -0
- package/dist/llm/ollama-embedding.js.map +1 -0
- package/dist/llm/ollama-provider.d.ts +80 -0
- package/dist/llm/ollama-provider.js +178 -0
- package/dist/llm/ollama-provider.js.map +1 -0
- package/dist/llm/provider.d.ts +120 -0
- package/dist/llm/provider.js +104 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/missions/mission-engine.d.ts +4 -0
- package/dist/missions/mission-engine.js +30 -8
- package/dist/missions/mission-engine.js.map +1 -1
- package/dist/notifications/__tests__/notification-service.test.d.ts +1 -0
- package/dist/notifications/__tests__/notification-service.test.js +176 -0
- package/dist/notifications/__tests__/notification-service.test.js.map +1 -0
- package/dist/notifications/discord-provider.d.ts +30 -0
- package/dist/notifications/discord-provider.js +89 -0
- package/dist/notifications/discord-provider.js.map +1 -0
- package/dist/notifications/email-provider.d.ts +41 -0
- package/dist/notifications/email-provider.js +101 -0
- package/dist/notifications/email-provider.js.map +1 -0
- package/dist/notifications/index.d.ts +8 -0
- package/dist/notifications/index.js +5 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/notification-provider.d.ts +75 -0
- package/dist/notifications/notification-provider.js +47 -0
- package/dist/notifications/notification-provider.js.map +1 -0
- package/dist/notifications/notification-service.d.ts +85 -0
- package/dist/notifications/notification-service.js +184 -0
- package/dist/notifications/notification-service.js.map +1 -0
- package/dist/notifications/telegram-provider.d.ts +30 -0
- package/dist/notifications/telegram-provider.js +78 -0
- package/dist/notifications/telegram-provider.js.map +1 -0
- package/dist/plugin/__tests__/plugin-registry.test.d.ts +1 -0
- package/dist/plugin/__tests__/plugin-registry.test.js +166 -0
- package/dist/plugin/__tests__/plugin-registry.test.js.map +1 -0
- package/dist/plugin/plugin-registry.d.ts +38 -0
- package/dist/plugin/plugin-registry.js +185 -0
- package/dist/plugin/plugin-registry.js.map +1 -0
- package/dist/plugin/types.d.ts +59 -0
- package/dist/plugin/types.js +2 -0
- package/dist/plugin/types.js.map +1 -0
- package/dist/research/adapters/__tests__/web-adapters.test.d.ts +1 -0
- package/dist/research/adapters/__tests__/web-adapters.test.js +106 -0
- package/dist/research/adapters/__tests__/web-adapters.test.js.map +1 -0
- package/dist/research/adapters/firecrawl-adapter.d.ts +57 -0
- package/dist/research/adapters/firecrawl-adapter.js +137 -0
- package/dist/research/adapters/firecrawl-adapter.js.map +1 -0
- package/dist/research/adapters/index.d.ts +3 -0
- package/dist/research/adapters/index.js +2 -0
- package/dist/research/adapters/index.js.map +1 -1
- package/dist/research/adapters/playwright-adapter.d.ts +54 -0
- package/dist/research/adapters/playwright-adapter.js +130 -0
- package/dist/research/adapters/playwright-adapter.js.map +1 -0
- package/dist/research/research-orchestrator.d.ts +3 -0
- package/dist/research/research-orchestrator.js +19 -1
- package/dist/research/research-orchestrator.js.map +1 -1
- package/dist/self-modification/self-modification-engine.js +28 -4
- package/dist/self-modification/self-modification-engine.js.map +1 -1
- package/dist/techradar/__tests__/techradar-engine.test.d.ts +1 -0
- package/dist/techradar/__tests__/techradar-engine.test.js +246 -0
- package/dist/techradar/__tests__/techradar-engine.test.js.map +1 -0
- package/dist/techradar/daily-digest.d.ts +18 -0
- package/dist/techradar/daily-digest.js +100 -0
- package/dist/techradar/daily-digest.js.map +1 -0
- package/dist/techradar/index.d.ts +5 -0
- package/dist/techradar/index.js +5 -0
- package/dist/techradar/index.js.map +1 -0
- package/dist/techradar/relevance-scorer.d.ts +29 -0
- package/dist/techradar/relevance-scorer.js +139 -0
- package/dist/techradar/relevance-scorer.js.map +1 -0
- package/dist/techradar/repo-watcher.d.ts +24 -0
- package/dist/techradar/repo-watcher.js +87 -0
- package/dist/techradar/repo-watcher.js.map +1 -0
- package/dist/techradar/techradar-engine.d.ts +69 -0
- package/dist/techradar/techradar-engine.js +382 -0
- package/dist/techradar/techradar-engine.js.map +1 -0
- package/dist/techradar/types.d.ts +87 -0
- package/dist/techradar/types.js +5 -0
- package/dist/techradar/types.js.map +1 -0
- package/dist/watchdog/__tests__/watchdog-service.test.d.ts +1 -0
- package/dist/watchdog/__tests__/watchdog-service.test.js +113 -0
- package/dist/watchdog/__tests__/watchdog-service.test.js.map +1 -0
- package/dist/watchdog/watchdog-service.d.ts +60 -0
- package/dist/watchdog/watchdog-service.js +275 -0
- package/dist/watchdog/watchdog-service.js.map +1 -0
- package/dist/watchdog/windows-service.d.ts +39 -0
- package/dist/watchdog/windows-service.js +179 -0
- package/dist/watchdog/windows-service.js.map +1 -0
- package/package.json +20 -2
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, it, expect, vi } 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
|
+
import { FirecrawlAdapter } from '../firecrawl-adapter.js';
|
|
9
|
+
describe('PlaywrightAdapter', () => {
|
|
10
|
+
it('has correct name', () => {
|
|
11
|
+
const adapter = new PlaywrightAdapter();
|
|
12
|
+
expect(adapter.name).toBe('playwright');
|
|
13
|
+
});
|
|
14
|
+
it('isEnabled returns true (optimistic)', () => {
|
|
15
|
+
const adapter = new PlaywrightAdapter();
|
|
16
|
+
expect(adapter.isEnabled()).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it('scout returns empty array', async () => {
|
|
19
|
+
const adapter = new PlaywrightAdapter();
|
|
20
|
+
const discoveries = await adapter.scout();
|
|
21
|
+
expect(discoveries).toEqual([]);
|
|
22
|
+
});
|
|
23
|
+
it('checkAvailable returns false when playwright not installed', async () => {
|
|
24
|
+
const adapter = new PlaywrightAdapter();
|
|
25
|
+
// In test environment, playwright chromium is likely not installed
|
|
26
|
+
const available = await adapter.checkAvailable();
|
|
27
|
+
expect(typeof available).toBe('boolean');
|
|
28
|
+
});
|
|
29
|
+
it('extract returns null when playwright unavailable', async () => {
|
|
30
|
+
const adapter = new PlaywrightAdapter();
|
|
31
|
+
// Force unavailable
|
|
32
|
+
adapter.available = false;
|
|
33
|
+
const result = await adapter.extract('https://example.com');
|
|
34
|
+
expect(result).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
it('shutdown is safe when no browser', async () => {
|
|
37
|
+
const adapter = new PlaywrightAdapter();
|
|
38
|
+
await expect(adapter.shutdown()).resolves.not.toThrow();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('FirecrawlAdapter', () => {
|
|
42
|
+
it('has correct name', () => {
|
|
43
|
+
const adapter = new FirecrawlAdapter();
|
|
44
|
+
expect(adapter.name).toBe('firecrawl');
|
|
45
|
+
});
|
|
46
|
+
it('isEnabled returns false without API key', () => {
|
|
47
|
+
const adapter = new FirecrawlAdapter({ apiKey: undefined });
|
|
48
|
+
expect(adapter.isEnabled()).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
it('isEnabled returns true with API key', () => {
|
|
51
|
+
const adapter = new FirecrawlAdapter({ apiKey: 'fc-test' });
|
|
52
|
+
expect(adapter.isEnabled()).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it('scout returns empty array', async () => {
|
|
55
|
+
const adapter = new FirecrawlAdapter();
|
|
56
|
+
const discoveries = await adapter.scout();
|
|
57
|
+
expect(discoveries).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
it('scrape returns null when not enabled', async () => {
|
|
60
|
+
const adapter = new FirecrawlAdapter({ apiKey: undefined });
|
|
61
|
+
const result = await adapter.scrape('https://example.com');
|
|
62
|
+
expect(result).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
it('crawl returns empty when not enabled', async () => {
|
|
65
|
+
const adapter = new FirecrawlAdapter({ apiKey: undefined });
|
|
66
|
+
const result = await adapter.crawl('https://example.com');
|
|
67
|
+
expect(result).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
it('scrape calls API with correct URL', async () => {
|
|
70
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
71
|
+
ok: true,
|
|
72
|
+
json: async () => ({
|
|
73
|
+
success: true,
|
|
74
|
+
data: {
|
|
75
|
+
markdown: '# Test\n\nContent here',
|
|
76
|
+
metadata: { title: 'Test Page', description: 'A test page' },
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
81
|
+
const adapter = new FirecrawlAdapter({ apiKey: 'fc-test' });
|
|
82
|
+
const result = await adapter.scrape('https://example.com');
|
|
83
|
+
expect(mockFetch).toHaveBeenCalledWith('https://api.firecrawl.dev/v1/scrape', expect.objectContaining({
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: expect.objectContaining({
|
|
86
|
+
'Authorization': 'Bearer fc-test',
|
|
87
|
+
}),
|
|
88
|
+
}));
|
|
89
|
+
expect(result).not.toBeNull();
|
|
90
|
+
expect(result.title).toBe('Test Page');
|
|
91
|
+
expect(result.markdown).toContain('# Test');
|
|
92
|
+
vi.unstubAllGlobals();
|
|
93
|
+
});
|
|
94
|
+
it('scrape handles API errors gracefully', async () => {
|
|
95
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
96
|
+
ok: false,
|
|
97
|
+
status: 500,
|
|
98
|
+
});
|
|
99
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
100
|
+
const adapter = new FirecrawlAdapter({ apiKey: 'fc-test' });
|
|
101
|
+
const result = await adapter.scrape('https://example.com');
|
|
102
|
+
expect(result).toBeNull();
|
|
103
|
+
vi.unstubAllGlobals();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=web-adapters.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-adapters.test.js","sourceRoot":"","sources":["../../../../src/research/adapters/__tests__/web-adapters.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firecrawl Adapter — Cloud-based LLM-optimized web scraping
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════
|
|
5
|
+
* EINRICHTEN
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* 1. Account erstellen: https://firecrawl.dev
|
|
9
|
+
* 2. API Key holen
|
|
10
|
+
* 3. In .env:
|
|
11
|
+
* FIRECRAWL_API_KEY=fc-...
|
|
12
|
+
*
|
|
13
|
+
* Wann nutzen?
|
|
14
|
+
* → Bulk Crawling (ganze Websites)
|
|
15
|
+
* → Wenn LLM-ready Markdown gebraucht wird
|
|
16
|
+
* → Wenn lokaler Playwright zu langsam ist
|
|
17
|
+
*
|
|
18
|
+
* Fallback-Strategie:
|
|
19
|
+
* 1. JinaReader (schnell, kostenlos, kein JS)
|
|
20
|
+
* 2. PlaywrightAdapter (lokal, JS-Rendering)
|
|
21
|
+
* 3. FirecrawlAdapter (Cloud, bezahlt, beste Qualität)
|
|
22
|
+
* ═══════════════════════════════════════════════════════════════
|
|
23
|
+
*/
|
|
24
|
+
import type { ScoutAdapter, ScoutDiscovery } from '../data-scout.js';
|
|
25
|
+
export interface FirecrawlConfig {
|
|
26
|
+
apiKey?: string;
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class FirecrawlAdapter implements ScoutAdapter {
|
|
30
|
+
readonly name = "firecrawl";
|
|
31
|
+
private readonly apiKey;
|
|
32
|
+
private readonly baseUrl;
|
|
33
|
+
constructor(config?: FirecrawlConfig);
|
|
34
|
+
isEnabled(): boolean;
|
|
35
|
+
scout(): Promise<ScoutDiscovery[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Scrape a single URL and return LLM-ready markdown content.
|
|
38
|
+
*/
|
|
39
|
+
scrape(url: string): Promise<{
|
|
40
|
+
title: string;
|
|
41
|
+
content: string;
|
|
42
|
+
description: string;
|
|
43
|
+
markdown: string;
|
|
44
|
+
} | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Crawl a website starting from a URL.
|
|
47
|
+
* Returns markdown for multiple pages.
|
|
48
|
+
*/
|
|
49
|
+
crawl(url: string, options?: {
|
|
50
|
+
limit?: number;
|
|
51
|
+
maxDepth?: number;
|
|
52
|
+
}): Promise<Array<{
|
|
53
|
+
url: string;
|
|
54
|
+
title: string;
|
|
55
|
+
markdown: string;
|
|
56
|
+
}>>;
|
|
57
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firecrawl Adapter — Cloud-based LLM-optimized web scraping
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════
|
|
5
|
+
* EINRICHTEN
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* 1. Account erstellen: https://firecrawl.dev
|
|
9
|
+
* 2. API Key holen
|
|
10
|
+
* 3. In .env:
|
|
11
|
+
* FIRECRAWL_API_KEY=fc-...
|
|
12
|
+
*
|
|
13
|
+
* Wann nutzen?
|
|
14
|
+
* → Bulk Crawling (ganze Websites)
|
|
15
|
+
* → Wenn LLM-ready Markdown gebraucht wird
|
|
16
|
+
* → Wenn lokaler Playwright zu langsam ist
|
|
17
|
+
*
|
|
18
|
+
* Fallback-Strategie:
|
|
19
|
+
* 1. JinaReader (schnell, kostenlos, kein JS)
|
|
20
|
+
* 2. PlaywrightAdapter (lokal, JS-Rendering)
|
|
21
|
+
* 3. FirecrawlAdapter (Cloud, bezahlt, beste Qualität)
|
|
22
|
+
* ═══════════════════════════════════════════════════════════════
|
|
23
|
+
*/
|
|
24
|
+
import { getLogger } from '../../utils/logger.js';
|
|
25
|
+
const log = getLogger();
|
|
26
|
+
export class FirecrawlAdapter {
|
|
27
|
+
name = 'firecrawl';
|
|
28
|
+
apiKey;
|
|
29
|
+
baseUrl;
|
|
30
|
+
constructor(config = {}) {
|
|
31
|
+
this.apiKey = config.apiKey ?? process.env.FIRECRAWL_API_KEY ?? null;
|
|
32
|
+
this.baseUrl = config.baseUrl ?? 'https://api.firecrawl.dev/v1';
|
|
33
|
+
}
|
|
34
|
+
isEnabled() {
|
|
35
|
+
return this.apiKey !== null && this.apiKey.length > 0;
|
|
36
|
+
}
|
|
37
|
+
async scout() {
|
|
38
|
+
// Firecrawl doesn't search — it scrapes URLs.
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Scrape a single URL and return LLM-ready markdown content.
|
|
43
|
+
*/
|
|
44
|
+
async scrape(url) {
|
|
45
|
+
if (!this.isEnabled())
|
|
46
|
+
return null;
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch(`${this.baseUrl}/scrape`, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify({
|
|
55
|
+
url,
|
|
56
|
+
formats: ['markdown'],
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
log.warn(`[firecrawl] API error (${response.status})`);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const data = await response.json();
|
|
64
|
+
if (!data.success || !data.data?.markdown)
|
|
65
|
+
return null;
|
|
66
|
+
const markdown = data.data.markdown;
|
|
67
|
+
const title = data.data.metadata?.title ?? url;
|
|
68
|
+
const description = data.data.metadata?.description ?? markdown.substring(0, 300);
|
|
69
|
+
return { title, content: markdown, description, markdown };
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
log.warn(`[firecrawl] Error scraping ${url}: ${err.message}`);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Crawl a website starting from a URL.
|
|
78
|
+
* Returns markdown for multiple pages.
|
|
79
|
+
*/
|
|
80
|
+
async crawl(url, options = {}) {
|
|
81
|
+
if (!this.isEnabled())
|
|
82
|
+
return [];
|
|
83
|
+
try {
|
|
84
|
+
// Start crawl job
|
|
85
|
+
const startResponse = await fetch(`${this.baseUrl}/crawl`, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: {
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
url,
|
|
93
|
+
limit: options.limit ?? 10,
|
|
94
|
+
maxDepth: options.maxDepth ?? 2,
|
|
95
|
+
formats: ['markdown'],
|
|
96
|
+
}),
|
|
97
|
+
});
|
|
98
|
+
if (!startResponse.ok) {
|
|
99
|
+
log.warn(`[firecrawl] Crawl start error (${startResponse.status})`);
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const startData = await startResponse.json();
|
|
103
|
+
if (!startData.success || !startData.id)
|
|
104
|
+
return [];
|
|
105
|
+
// Poll for results (max 60s)
|
|
106
|
+
for (let i = 0; i < 30; i++) {
|
|
107
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
108
|
+
const statusResponse = await fetch(`${this.baseUrl}/crawl/${startData.id}`, {
|
|
109
|
+
headers: { 'Authorization': `Bearer ${this.apiKey}` },
|
|
110
|
+
});
|
|
111
|
+
if (!statusResponse.ok)
|
|
112
|
+
continue;
|
|
113
|
+
const statusData = await statusResponse.json();
|
|
114
|
+
if (statusData.status === 'completed' && statusData.data) {
|
|
115
|
+
return statusData.data
|
|
116
|
+
.filter(d => d.markdown)
|
|
117
|
+
.map(d => ({
|
|
118
|
+
url: d.metadata?.sourceURL ?? url,
|
|
119
|
+
title: d.metadata?.title ?? 'Untitled',
|
|
120
|
+
markdown: d.markdown,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
if (statusData.status === 'failed') {
|
|
124
|
+
log.warn('[firecrawl] Crawl job failed');
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
log.warn('[firecrawl] Crawl job timed out');
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
log.warn(`[firecrawl] Error crawling ${url}: ${err.message}`);
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=firecrawl-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firecrawl-adapter.js","sourceRoot":"","sources":["../../../src/research/adapters/firecrawl-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;AAOxB,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAG,WAAW,CAAC;IACX,MAAM,CAAgB;IACtB,OAAO,CAAS;IAEjC,YAAY,SAA0B,EAAE;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC;QACrE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,8BAA8B,CAAC;IAClE,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,KAAK;QACT,8CAA8C;QAC9C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QAMtB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO,IAAI,CAAC;QAEnC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,SAAS,EAAE;gBACrD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;iBACzC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,GAAG;oBACH,OAAO,EAAE,CAAC,UAAU,CAAC;iBACtB,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,GAAG,CAAC,IAAI,CAAC,0BAA0B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;gBACvD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAS/B,CAAC;YAEF,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAEvD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;YACpC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,GAAG,CAAC;YAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAElF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;QAC7D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,8BAA8B,GAAG,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACzE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,UAGrB,EAAE;QAKJ,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO,EAAE,CAAC;QAEjC,IAAI,CAAC;YACH,kBAAkB;YAClB,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,QAAQ,EAAE;gBACzD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;iBACzC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,GAAG;oBACH,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;oBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,CAAC;oBAC/B,OAAO,EAAE,CAAC,UAAU,CAAC;iBACtB,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC;gBACtB,GAAG,CAAC,IAAI,CAAC,kCAAkC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;gBACpE,OAAO,EAAE,CAAC;YACZ,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,IAAI,EAAuC,CAAC;YAClF,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE;gBAAE,OAAO,EAAE,CAAC;YAEnD,6BAA6B;YAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC5B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBAE5C,MAAM,cAAc,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,EAAE,EAAE;oBAC1E,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE,EAAE;iBACtD,CAAC,CAAC;gBAEH,IAAI,CAAC,cAAc,CAAC,EAAE;oBAAE,SAAS;gBAEjC,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,IAAI,EAM3C,CAAC;gBAEF,IAAI,UAAU,CAAC,MAAM,KAAK,WAAW,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;oBACzD,OAAO,UAAU,CAAC,IAAI;yBACnB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;yBACvB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBACT,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,IAAI,GAAG;wBACjC,KAAK,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,IAAI,UAAU;wBACtC,QAAQ,EAAE,CAAC,CAAC,QAAS;qBACtB,CAAC,CAAC,CAAC;gBACR,CAAC;gBAED,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;oBACnC,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;oBACzC,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;YAC5C,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,8BAA8B,GAAG,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACzE,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -3,3 +3,6 @@ export { TradingDataMinerAdapter } from './trading-adapter.js';
|
|
|
3
3
|
export { MarketingDataMinerAdapter } from './marketing-adapter.js';
|
|
4
4
|
export { ScannerDataMinerAdapter } from './scanner-adapter.js';
|
|
5
5
|
export { BraveSearchAdapter, JinaReaderAdapter } from './web-research-adapter.js';
|
|
6
|
+
export { PlaywrightAdapter } from './playwright-adapter.js';
|
|
7
|
+
export { FirecrawlAdapter } from './firecrawl-adapter.js';
|
|
8
|
+
export type { FirecrawlConfig } from './firecrawl-adapter.js';
|
|
@@ -3,4 +3,6 @@ export { TradingDataMinerAdapter } from './trading-adapter.js';
|
|
|
3
3
|
export { MarketingDataMinerAdapter } from './marketing-adapter.js';
|
|
4
4
|
export { ScannerDataMinerAdapter } from './scanner-adapter.js';
|
|
5
5
|
export { BraveSearchAdapter, JinaReaderAdapter } from './web-research-adapter.js';
|
|
6
|
+
export { PlaywrightAdapter } from './playwright-adapter.js';
|
|
7
|
+
export { FirecrawlAdapter } from './firecrawl-adapter.js';
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/research/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/research/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Adapter — Headless Browser for JS-rendered pages
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════
|
|
5
|
+
* EINRICHTEN
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* 1. Playwright ist als optionale peerDependency installiert.
|
|
9
|
+
* Falls nötig: npm install playwright
|
|
10
|
+
* 2. Browser installieren:
|
|
11
|
+
* npx playwright install chromium
|
|
12
|
+
* 3. Keine Config nötig — funktioniert automatisch.
|
|
13
|
+
*
|
|
14
|
+
* Wann nutzen?
|
|
15
|
+
* → SPAs (React, Vue, Angular) — Inhalt wird per JS gerendert
|
|
16
|
+
* → Seiten die ohne JS leer oder unvollständig sind
|
|
17
|
+
* → Wenn Jina Reader kein brauchbares Ergebnis liefert
|
|
18
|
+
*
|
|
19
|
+
* Fallback-Strategie:
|
|
20
|
+
* 1. JinaReader (schnell, kostenlos, kein JS)
|
|
21
|
+
* 2. PlaywrightAdapter (langsamer, lokal, JS-Rendering)
|
|
22
|
+
* 3. FirecrawlAdapter (Cloud, optional)
|
|
23
|
+
* ═══════════════════════════════════════════════════════════════
|
|
24
|
+
*/
|
|
25
|
+
import type { ScoutAdapter, ScoutDiscovery } from '../data-scout.js';
|
|
26
|
+
export declare class PlaywrightAdapter implements ScoutAdapter {
|
|
27
|
+
readonly name = "playwright";
|
|
28
|
+
private browser;
|
|
29
|
+
private available;
|
|
30
|
+
isEnabled(): boolean;
|
|
31
|
+
scout(): Promise<ScoutDiscovery[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if Playwright + Chromium are available.
|
|
34
|
+
* Caches the result.
|
|
35
|
+
*/
|
|
36
|
+
checkAvailable(): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Extract content from a URL using a headless browser.
|
|
39
|
+
* Renders JavaScript, waits for content, then extracts text.
|
|
40
|
+
*/
|
|
41
|
+
extract(url: string, options?: {
|
|
42
|
+
waitForSelector?: string;
|
|
43
|
+
timeout?: number;
|
|
44
|
+
screenshot?: boolean;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
title: string;
|
|
47
|
+
content: string;
|
|
48
|
+
description: string;
|
|
49
|
+
screenshot?: Buffer;
|
|
50
|
+
} | null>;
|
|
51
|
+
private getBrowser;
|
|
52
|
+
/** Graceful shutdown — close the browser */
|
|
53
|
+
shutdown(): Promise<void>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Adapter — Headless Browser for JS-rendered pages
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════
|
|
5
|
+
* EINRICHTEN
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* 1. Playwright ist als optionale peerDependency installiert.
|
|
9
|
+
* Falls nötig: npm install playwright
|
|
10
|
+
* 2. Browser installieren:
|
|
11
|
+
* npx playwright install chromium
|
|
12
|
+
* 3. Keine Config nötig — funktioniert automatisch.
|
|
13
|
+
*
|
|
14
|
+
* Wann nutzen?
|
|
15
|
+
* → SPAs (React, Vue, Angular) — Inhalt wird per JS gerendert
|
|
16
|
+
* → Seiten die ohne JS leer oder unvollständig sind
|
|
17
|
+
* → Wenn Jina Reader kein brauchbares Ergebnis liefert
|
|
18
|
+
*
|
|
19
|
+
* Fallback-Strategie:
|
|
20
|
+
* 1. JinaReader (schnell, kostenlos, kein JS)
|
|
21
|
+
* 2. PlaywrightAdapter (langsamer, lokal, JS-Rendering)
|
|
22
|
+
* 3. FirecrawlAdapter (Cloud, optional)
|
|
23
|
+
* ═══════════════════════════════════════════════════════════════
|
|
24
|
+
*/
|
|
25
|
+
import { getLogger } from '../../utils/logger.js';
|
|
26
|
+
const log = getLogger();
|
|
27
|
+
export class PlaywrightAdapter {
|
|
28
|
+
name = 'playwright';
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
browser = null;
|
|
31
|
+
available = null;
|
|
32
|
+
isEnabled() {
|
|
33
|
+
// Optimistic — actual check happens on first use
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
async scout() {
|
|
37
|
+
// Playwright doesn't search — it extracts content from URLs.
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if Playwright + Chromium are available.
|
|
42
|
+
* Caches the result.
|
|
43
|
+
*/
|
|
44
|
+
async checkAvailable() {
|
|
45
|
+
if (this.available !== null)
|
|
46
|
+
return this.available;
|
|
47
|
+
try {
|
|
48
|
+
const pwPath = 'playwright';
|
|
49
|
+
const { chromium } = await import(/* webpackIgnore: true */ pwPath);
|
|
50
|
+
const browser = await chromium.launch({ headless: true });
|
|
51
|
+
await browser.close();
|
|
52
|
+
this.available = true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
this.available = false;
|
|
56
|
+
log.debug('[playwright] Not available (install with: npx playwright install chromium)');
|
|
57
|
+
}
|
|
58
|
+
return this.available;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extract content from a URL using a headless browser.
|
|
62
|
+
* Renders JavaScript, waits for content, then extracts text.
|
|
63
|
+
*/
|
|
64
|
+
async extract(url, options = {}) {
|
|
65
|
+
try {
|
|
66
|
+
if (!(await this.checkAvailable()))
|
|
67
|
+
return null;
|
|
68
|
+
const browser = await this.getBrowser();
|
|
69
|
+
const context = await browser.newContext({
|
|
70
|
+
userAgent: 'BrainEcosystem/1.0 (Research Bot)',
|
|
71
|
+
});
|
|
72
|
+
const page = await context.newPage();
|
|
73
|
+
try {
|
|
74
|
+
await page.goto(url, {
|
|
75
|
+
waitUntil: 'networkidle',
|
|
76
|
+
timeout: options.timeout ?? 30_000,
|
|
77
|
+
});
|
|
78
|
+
if (options.waitForSelector) {
|
|
79
|
+
await page.waitForSelector(options.waitForSelector, { timeout: 10_000 }).catch(() => { });
|
|
80
|
+
}
|
|
81
|
+
// Auto-scroll for lazy loading
|
|
82
|
+
await page.evaluate(() => {
|
|
83
|
+
window.scrollTo(0, document.body.scrollHeight);
|
|
84
|
+
});
|
|
85
|
+
await page.waitForTimeout(1000);
|
|
86
|
+
const title = await page.title();
|
|
87
|
+
const content = await page.evaluate(() => {
|
|
88
|
+
// Remove scripts, styles, navigation
|
|
89
|
+
const clone = document.body.cloneNode(true);
|
|
90
|
+
clone.querySelectorAll('script, style, nav, footer, header, aside, [role="navigation"]')
|
|
91
|
+
.forEach(el => el.remove());
|
|
92
|
+
return clone.innerText || clone.textContent || '';
|
|
93
|
+
});
|
|
94
|
+
const description = content.trim().substring(0, 300);
|
|
95
|
+
let screenshot;
|
|
96
|
+
if (options.screenshot) {
|
|
97
|
+
screenshot = await page.screenshot({ fullPage: false });
|
|
98
|
+
}
|
|
99
|
+
return { title, content: content.trim(), description, screenshot };
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await context.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
log.warn(`[playwright] Error extracting ${url}: ${err.message}`);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
+
async getBrowser() {
|
|
112
|
+
if (this.browser)
|
|
113
|
+
return this.browser;
|
|
114
|
+
const pwPath = 'playwright';
|
|
115
|
+
const { chromium } = await import(/* webpackIgnore: true */ pwPath);
|
|
116
|
+
this.browser = await chromium.launch({ headless: true });
|
|
117
|
+
return this.browser;
|
|
118
|
+
}
|
|
119
|
+
/** Graceful shutdown — close the browser */
|
|
120
|
+
async shutdown() {
|
|
121
|
+
if (this.browser) {
|
|
122
|
+
try {
|
|
123
|
+
await this.browser.close();
|
|
124
|
+
}
|
|
125
|
+
catch { /* best effort */ }
|
|
126
|
+
this.browser = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=playwright-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-adapter.js","sourceRoot":"","sources":["../../../src/research/adapters/playwright-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -96,6 +96,7 @@ export declare class ResearchOrchestrator {
|
|
|
96
96
|
private bootstrapService;
|
|
97
97
|
private conceptAbstraction;
|
|
98
98
|
private llmService;
|
|
99
|
+
private onSuggestionCallback;
|
|
99
100
|
private db;
|
|
100
101
|
private brainName;
|
|
101
102
|
private feedbackTimer;
|
|
@@ -113,6 +114,8 @@ export declare class ResearchOrchestrator {
|
|
|
113
114
|
/** Cycle number of last written suggestions — write at most once per cycle. */
|
|
114
115
|
private lastSuggestionsCycle;
|
|
115
116
|
constructor(db: Database.Database, config: ResearchOrchestratorConfig, causalGraph?: CausalGraph);
|
|
117
|
+
/** Set callback for self-improvement suggestions (e.g. to create notifications). */
|
|
118
|
+
setOnSuggestion(callback: (suggestions: string[]) => void): void;
|
|
116
119
|
/** Set the DataMiner instance for DB-driven engine feeding. */
|
|
117
120
|
setDataMiner(miner: DataMiner): void;
|
|
118
121
|
/** Set the DreamEngine — wires journal + knowledgeDistiller into it. */
|
|
@@ -57,6 +57,7 @@ export class ResearchOrchestrator {
|
|
|
57
57
|
bootstrapService = null;
|
|
58
58
|
conceptAbstraction = null;
|
|
59
59
|
llmService = null;
|
|
60
|
+
onSuggestionCallback = null;
|
|
60
61
|
db;
|
|
61
62
|
brainName;
|
|
62
63
|
feedbackTimer = null;
|
|
@@ -92,7 +93,11 @@ export class ResearchOrchestrator {
|
|
|
92
93
|
this.autoResponder = new AutoResponder(db, { brainName: config.brainName });
|
|
93
94
|
this.autoResponder.setAdaptiveStrategy(this.adaptiveStrategy);
|
|
94
95
|
this.autoResponder.setJournal(this.journal);
|
|
95
|
-
this.hypothesisEngine = new HypothesisEngine(db, { minEvidence: 3, confirmThreshold: 0.
|
|
96
|
+
this.hypothesisEngine = new HypothesisEngine(db, { minEvidence: 3, confirmThreshold: 0.10, rejectThreshold: 0.5 });
|
|
97
|
+
}
|
|
98
|
+
/** Set callback for self-improvement suggestions (e.g. to create notifications). */
|
|
99
|
+
setOnSuggestion(callback) {
|
|
100
|
+
this.onSuggestionCallback = callback;
|
|
96
101
|
}
|
|
97
102
|
/** Set the DataMiner instance for DB-driven engine feeding. */
|
|
98
103
|
setDataMiner(miner) {
|
|
@@ -366,6 +371,10 @@ export class ResearchOrchestrator {
|
|
|
366
371
|
const now = Date.now();
|
|
367
372
|
this.hypothesisEngine.observe({ source: this.brainName, type: 'anomaly_count', value: anomalies.length, timestamp: now });
|
|
368
373
|
this.hypothesisEngine.observe({ source: this.brainName, type: 'insight_count', value: insights.length, timestamp: now });
|
|
374
|
+
// Feed journal significance for richer hypothesis generation
|
|
375
|
+
for (const insight of insights) {
|
|
376
|
+
this.hypothesisEngine.observe({ source: this.brainName, type: 'journal', value: insight.confidence, timestamp: now, metadata: { title: insight.title } });
|
|
377
|
+
}
|
|
369
378
|
if (anomalies.length > 0) {
|
|
370
379
|
for (const a of anomalies) {
|
|
371
380
|
this.hypothesisEngine.observe({ source: this.brainName, type: `anomaly:${a.metric}`, value: a.deviation, timestamp: now, metadata: { severity: a.severity } });
|
|
@@ -497,6 +506,8 @@ export class ResearchOrchestrator {
|
|
|
497
506
|
const sig = result.conclusion.significant;
|
|
498
507
|
ts?.emit('experiment', 'discovering', `Experiment "${exp.name}": ${sig ? result.conclusion.direction : 'inconclusive'} (p=${result.conclusion.p_value.toFixed(4)})`, sig ? 'notable' : 'routine');
|
|
499
508
|
this.journal.recordExperiment(exp.name, sig ? (result.conclusion.direction === 'positive' ? 'confirmed' : 'rejected') : 'inconclusive', { conclusion: result.conclusion, hypothesis: exp.hypothesis }, sig);
|
|
509
|
+
// Feed experiment effect size into HypothesisEngine
|
|
510
|
+
this.hypothesisEngine.observe({ source: this.brainName, type: 'experiment', value: result.conclusion.effect_size, timestamp: now, metadata: { name: exp.name, direction: result.conclusion.direction, significant: sig } });
|
|
500
511
|
this.log.info(`[orchestrator] Experiment "${exp.name}": ${sig ? result.conclusion.direction : 'inconclusive'} (p=${result.conclusion.p_value.toFixed(4)}, d=${result.conclusion.effect_size.toFixed(2)})`);
|
|
501
512
|
}
|
|
502
513
|
}
|
|
@@ -2359,6 +2370,13 @@ export class ResearchOrchestrator {
|
|
|
2359
2370
|
else {
|
|
2360
2371
|
fs.appendFileSync(filePath, `---\n${header}${body}`, 'utf-8');
|
|
2361
2372
|
}
|
|
2373
|
+
// Notify via callback (e.g. to create NotificationService entries)
|
|
2374
|
+
if (this.onSuggestionCallback) {
|
|
2375
|
+
try {
|
|
2376
|
+
this.onSuggestionCallback(suggestions);
|
|
2377
|
+
}
|
|
2378
|
+
catch { /* best effort */ }
|
|
2379
|
+
}
|
|
2362
2380
|
}
|
|
2363
2381
|
catch {
|
|
2364
2382
|
// Don't let file writing break the feedback cycle
|