@lincoln504/pi-research 0.0.0

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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/config/searxng/default-settings.yml +189 -0
  4. package/config/searxng/limiter.toml +34 -0
  5. package/config/tooling/eslint.config.js +73 -0
  6. package/config/tooling/vitest.config.integration.ts +17 -0
  7. package/config/tooling/vitest.config.ts +40 -0
  8. package/config/tooling/vitest.config.unit.ts +12 -0
  9. package/docs/ARCHITECTURE.md +109 -0
  10. package/docs/assets/README-banner.jpg +0 -0
  11. package/package.json +92 -0
  12. package/scripts/setup.js +174 -0
  13. package/src/config.ts +206 -0
  14. package/src/constants.ts +148 -0
  15. package/src/healthcheck/index.ts +309 -0
  16. package/src/index.ts +71 -0
  17. package/src/infrastructure/searxng-lifecycle.ts +577 -0
  18. package/src/infrastructure/searxng-manager.ts +1173 -0
  19. package/src/infrastructure/state-manager.ts +918 -0
  20. package/src/logger.ts +246 -0
  21. package/src/orchestration/deep-research-orchestrator.ts +848 -0
  22. package/src/orchestration/deep-research-reducer.ts +129 -0
  23. package/src/orchestration/deep-research-types.ts +54 -0
  24. package/src/orchestration/id-utils.ts +63 -0
  25. package/src/orchestration/researcher.ts +104 -0
  26. package/src/orchestration/session-context.ts +43 -0
  27. package/src/prompts/research-tool-usage.md +45 -0
  28. package/src/prompts/researcher.md +166 -0
  29. package/src/prompts/system-coordinator.md +24 -0
  30. package/src/prompts/system-lead-evaluator.md +225 -0
  31. package/src/security/cisa-kev.ts +242 -0
  32. package/src/security/github-advisories.ts +607 -0
  33. package/src/security/index.ts +406 -0
  34. package/src/security/nvd.ts +532 -0
  35. package/src/security/osv.ts +487 -0
  36. package/src/security/types.ts +138 -0
  37. package/src/stackexchange/cache.ts +111 -0
  38. package/src/stackexchange/index.ts +352 -0
  39. package/src/stackexchange/output/compact.ts +64 -0
  40. package/src/stackexchange/output/index.ts +7 -0
  41. package/src/stackexchange/output/json.ts +21 -0
  42. package/src/stackexchange/output/table.ts +123 -0
  43. package/src/stackexchange/queries.ts +168 -0
  44. package/src/stackexchange/rest-client.ts +127 -0
  45. package/src/stackexchange/types.ts +129 -0
  46. package/src/tool.ts +430 -0
  47. package/src/tools/grep.ts +335 -0
  48. package/src/tools/index.ts +77 -0
  49. package/src/tools/scrape.ts +451 -0
  50. package/src/tools/search.ts +188 -0
  51. package/src/tools/security.ts +292 -0
  52. package/src/tools/stackexchange.ts +101 -0
  53. package/src/tui/research-panel.ts +576 -0
  54. package/src/tui/searxng-status.ts +80 -0
  55. package/src/types/extension-context.ts +60 -0
  56. package/src/types/llm.ts +111 -0
  57. package/src/utils/inject-date.ts +29 -0
  58. package/src/utils/input-validation.ts +103 -0
  59. package/src/utils/json-utils.ts +262 -0
  60. package/src/utils/make-resource-loader.ts +43 -0
  61. package/src/utils/research-error.ts +205 -0
  62. package/src/utils/research-export.ts +199 -0
  63. package/src/utils/searxng-config.ts +94 -0
  64. package/src/utils/session-state.ts +295 -0
  65. package/src/utils/shared-links.ts +123 -0
  66. package/src/utils/shutdown-manager.ts +54 -0
  67. package/src/utils/text-utils.ts +83 -0
  68. package/src/utils/tool-usage-tracker.ts +135 -0
  69. package/src/web-research/dependencies.ts +20 -0
  70. package/src/web-research/retry-utils.ts +232 -0
  71. package/src/web-research/scrapers.ts +935 -0
  72. package/src/web-research/search.ts +235 -0
  73. package/src/web-research/types.ts +47 -0
  74. package/src/web-research/utils.ts +158 -0
  75. package/tsconfig.json +40 -0
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@lincoln504/pi-research",
3
+ "version": "0.0.0",
4
+ "description": "Multi-agent research Deep Mode orchestration for pi. State-driven parallel researchers with web search, scraping, security databases, and Stack Exchange integration.",
5
+ "type": "module",
6
+ "private": false,
7
+ "main": "src/index.ts",
8
+ "files": [
9
+ "src/",
10
+ "config/",
11
+ "docs/",
12
+ "scripts/",
13
+ "LICENSE",
14
+ "README.md",
15
+ "tsconfig.json"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/Lincoln504/pi-research.git"
20
+ },
21
+ "homepage": "https://github.com/Lincoln504/pi-research#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/Lincoln504/pi-research/issues"
24
+ },
25
+ "keywords": [
26
+ "pi",
27
+ "pi-package",
28
+ "research",
29
+ "agent",
30
+ "orchestration",
31
+ "web-search",
32
+ "scraping",
33
+ "security",
34
+ "stack-exchange"
35
+ ],
36
+ "author": "Lincolndeen",
37
+ "contributors": [
38
+ "MuratAitov"
39
+ ],
40
+ "license": "MIT",
41
+ "engines": {
42
+ "node": "^22.13.0 || >=24.0.0"
43
+ },
44
+ "scripts": {
45
+ "lint": "npx eslint --config config/tooling/eslint.config.js src/",
46
+ "lint:fix": "npx eslint --config config/tooling/eslint.config.js src/ --fix",
47
+ "type-check": "tsc --noEmit",
48
+ "test": "vitest run --config config/tooling/vitest.config.unit.ts",
49
+ "test:unit": "vitest run --config config/tooling/vitest.config.unit.ts",
50
+ "test:integration": "vitest run --config config/tooling/vitest.config.integration.ts",
51
+ "test:watch": "vitest --config config/tooling/vitest.config.unit.ts",
52
+ "test:coverage": "vitest run --config config/tooling/vitest.config.ts --coverage",
53
+ "postinstall": "node scripts/setup.js",
54
+ "install:browsers": "npx playwright install chromium",
55
+ "install:deps": "npx playwright install-deps",
56
+ "setup": "npm install && npm run install:browsers"
57
+ },
58
+ "pi": {
59
+ "extensions": [
60
+ "src/index.ts"
61
+ ]
62
+ },
63
+ "dependencies": {
64
+ "@kreuzberg/html-to-markdown-node": "^2.30.0",
65
+ "dockerode": "^4.0.10",
66
+ "js-yaml": "^4.1.1",
67
+ "node-html-markdown": "^2.0.0",
68
+ "playwright": "^1.52.0"
69
+ },
70
+ "optionalDependencies": {
71
+ "@kreuzberg/html-to-markdown-node-darwin-arm64": "^2.30.0",
72
+ "@kreuzberg/html-to-markdown-node-darwin-x64": "^2.30.0",
73
+ "@kreuzberg/html-to-markdown-node-win32-x64-msvc": "^2.30.0"
74
+ },
75
+ "peerDependencies": {
76
+ "@mariozechner/pi-coding-agent": ">=0.65.0",
77
+ "@sinclair/typebox": ">=0.34.0"
78
+ },
79
+ "devDependencies": {
80
+ "@eslint/js": "^10.0.1",
81
+ "@types/dockerode": "^4.0.1",
82
+ "@types/js-yaml": "^4.0.9",
83
+ "@typescript-eslint/eslint-plugin": "^8.58.0",
84
+ "@typescript-eslint/parser": "^8.58.0",
85
+ "@vitest/coverage-v8": "^4.1.2",
86
+ "eslint": "^10.1.0",
87
+ "eslint-config-prettier": "^10.1.0",
88
+ "testcontainers": "^11.13.0",
89
+ "typescript": "^6.0.2",
90
+ "vitest": "^4.1.2"
91
+ }
92
+ }
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * pi-research Postinstall Setup Script
5
+ *
6
+ * This script runs automatically after `npm install` and ensures:
7
+ * 1. Playwright browser binaries are installed (Chromium, Firefox, WebKit)
8
+ * 2. System dependencies are available (if run with --system-deps flag)
9
+ *
10
+ * Usage:
11
+ * npm run setup # Full setup: install deps + browsers
12
+ * npm run install:browsers # Install only browser binaries
13
+ * npm run install:deps # Install system dependencies only
14
+ *
15
+ * Environment Variables:
16
+ * PLAYWRIGHT_BROWSERS_PATH - Custom path for browser cache
17
+ * PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 - Skip browser download
18
+ */
19
+
20
+ import { execSync } from 'child_process';
21
+ import { existsSync, readdirSync, statSync } from 'fs';
22
+ import { join, dirname } from 'path';
23
+ import { fileURLToPath } from 'url';
24
+ import { homedir, platform } from 'os';
25
+
26
+ const __filename = fileURLToPath(import.meta.url);
27
+ const __dirname = dirname(__filename);
28
+
29
+ console.log('='.repeat(60));
30
+ console.log('🔧 pi-research Setup');
31
+ console.log('='.repeat(60));
32
+
33
+ // Check Node.js version
34
+ const nodeVersion = process.version.replace('v', '');
35
+ const [major] = nodeVersion.split('.').map(Number);
36
+
37
+ console.log(`\nℹ️ Node.js version: v${nodeVersion}`);
38
+
39
+ const requiredVersions = {
40
+ min: 22,
41
+ preferred: [22, 24]
42
+ };
43
+
44
+ if (major < requiredVersions.min) {
45
+ console.warn(
46
+ `⚠️ WARNING: Node.js ${nodeVersion} is below the minimum required version ${requiredVersions.min}.`
47
+ );
48
+ console.warn(' Please upgrade to Node.js 22.13.0+ or 24.x+');
49
+ }
50
+
51
+ console.log(` Required: >= ${requiredVersions.min}.0.0`);
52
+
53
+ // Check if playwright is installed
54
+ const playwrightPath = join(process.cwd(), 'node_modules', 'playwright');
55
+
56
+ if (!existsSync(playwrightPath)) {
57
+ console.error('\n❌ ERROR: playwright is not installed.');
58
+ console.error(' Run `npm install` first, then `npm run setup`.');
59
+ process.exit(1);
60
+ }
61
+
62
+ console.log('✅ Playwright package installed');
63
+
64
+ // Detect OS for browser installation
65
+ const isLinux = platform() === 'linux';
66
+ const isDarwin = platform() === 'darwin';
67
+ const isWindows = platform() === 'win32';
68
+
69
+ console.log(`\n📦 Detected platform: ${platform()}`);
70
+
71
+ // Install browsers
72
+ console.log('\n🌐 Installing Playwright browsers...');
73
+
74
+ try {
75
+ // Check if user wants to install system dependencies
76
+ const installDeps = process.argv.includes('--system-deps') ||
77
+ process.env.PLAYWRIGHT_INSTALL_DEPS === 'true';
78
+
79
+ if (installDeps && isLinux) {
80
+ console.log('⚙️ Installing Linux system dependencies for Playwright...');
81
+ try {
82
+ execSync('npx playwright install-deps', {
83
+ stdio: 'inherit',
84
+ env: { ...process.env, PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '0' }
85
+ });
86
+ console.log('✅ System dependencies installed');
87
+ } catch (error) {
88
+ console.warn('⚠️ Could not install system dependencies automatically.');
89
+ console.warn(' Try running: sudo apt-get install $(npx playwright install-deps --print-deps)');
90
+ }
91
+ }
92
+
93
+ // Install browser binaries
94
+ // Only Chromium is needed for scraping (as per scrapers.ts implementation)
95
+ console.log(' Installing Chromium browser...');
96
+ execSync('npx playwright install chromium', {
97
+ stdio: 'inherit',
98
+ env: {
99
+ ...process.env,
100
+ PLAYWRIGHT_BROWSERS_PATH: process.env.PLAYWRIGHT_BROWSERS_PATH || ''
101
+ }
102
+ });
103
+
104
+ console.log('✅ Browsers installed successfully');
105
+
106
+ // Verify installation
107
+ console.log('\n📋 Verifying browser installation...');
108
+ const browsers = ['chromium']; // Only Chromium is required for scraping
109
+ const cacheDir = process.env['PLAYWRIGHT_BROWSERS_PATH'] ||
110
+ (process.platform === 'win32'
111
+ ? join(process.env['LOCALAPPDATA'] || join(homedir(), 'AppData', 'Local'), 'ms-playwright')
112
+ : join(homedir(), '.cache', 'ms-playwright'));
113
+
114
+ browsers.forEach(browser => {
115
+ let browserDirs = [];
116
+ try {
117
+ browserDirs = readdirSync(cacheDir).filter(f =>
118
+ f.startsWith(browser) && statSync(join(cacheDir, f)).isDirectory()
119
+ );
120
+ } catch {
121
+ // cache dir may not exist yet or may be at a different path
122
+ }
123
+
124
+ if (browserDirs.length > 0) {
125
+ console.log(` ✅ ${browser}: ${browserDirs.sort().join(', ')}`);
126
+ } else {
127
+ console.warn(` ⚠️ ${browser}: Not found in cache`);
128
+ }
129
+ });
130
+
131
+ // Show browser versions
132
+ console.log('\n🔍 Browser versions:');
133
+ try {
134
+ const playwrightVersion = execSync('npx playwright --version', { encoding: 'utf8' }).trim();
135
+ console.log(` Playwright: ${playwrightVersion}`);
136
+ } catch (error) {
137
+ console.warn(' Could not determine Playwright version');
138
+ }
139
+
140
+ } catch (error) {
141
+ console.error('\n❌ ERROR: Failed to install browsers');
142
+ console.error(' Error:', error.message);
143
+ console.error('\n💡 Troubleshooting:');
144
+ console.error(' 1. Ensure you have internet connectivity');
145
+ console.error(' 2. Try running: npm run install:browsers');
146
+ console.error(' 3. Check Node.js version: node --version');
147
+ console.error(' 4. For Linux: sudo apt-get install -y $(npx playwright install-deps --print-deps)');
148
+ process.exit(1);
149
+ }
150
+
151
+ console.log('\n' + '='.repeat(60));
152
+ console.log('🎉 Setup complete! Chromium browser installed. pi-research is ready to use.');
153
+ console.log('='.repeat(60));
154
+
155
+ // Additional info
156
+ console.log('\n📚 Quick start:');
157
+ console.log(' • npm test - Run unit tests');
158
+ console.log(' • npm run type-check - Type check TypeScript');
159
+ console.log(' • npm run lint - Run ESLint');
160
+ console.log(' • npm run setup - Full setup with system deps');
161
+
162
+ // Platform-specific tips
163
+ if (isLinux) {
164
+ console.log('\n💡 Linux tip: If you encounter issues, install system deps:');
165
+ console.log(' sudo apt-get install -y libgbm1 libnss3 libatk1.0-0');
166
+ console.log(' libatk-bridge2.0-0 libcups2 libxkbcommon0 libxcomposite1');
167
+ } else if (isDarwin) {
168
+ console.log('\n💡 macOS: Ensure Xcode Command Line Tools are installed:');
169
+ console.log(' xcode-select --install');
170
+ } else if (isWindows) {
171
+ console.log('\n💡 Windows: Ensure you have the latest Visual C++ redistributables');
172
+ }
173
+
174
+ console.log('\n');
package/src/config.ts ADDED
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Configuration Module (Refactored for Testability)
3
+ *
4
+ * Uses factory pattern instead of module-level caching.
5
+ * Allows for different configurations in tests.
6
+ */
7
+
8
+ import { logger } from './logger.ts';
9
+
10
+ /**
11
+ * Configuration values
12
+ */
13
+ export interface Config {
14
+ /** Per-researcher timeout in milliseconds (default: 240000) */
15
+ RESEARCHER_TIMEOUT_MS: number;
16
+ /** Maximum researchers allowed to run simultaneously (default: 3) */
17
+ MAX_CONCURRENT_RESEARCHERS: number;
18
+ /** Proxy URL for SearXNG searches (optional) */
19
+ PROXY_URL?: string;
20
+ /** Brave Search API key — enables the braveapi engine in SearXNG when set (optional) */
21
+ BRAVE_SEARCH_API_KEY?: string;
22
+ /** Health check timeout in milliseconds (default: 15000) */
23
+ HEALTH_CHECK_TIMEOUT_MS?: number;
24
+ /** Global TUI refresh debounce in milliseconds (default: 10) */
25
+ TUI_REFRESH_DEBOUNCE_MS: number;
26
+ /** Console restore delay after research in milliseconds (default: 15000) */
27
+ CONSOLE_RESTORE_DELAY_MS: number;
28
+ /** BCP 47 language tag sent to SearXNG to filter search results (default: 'en-US') */
29
+ SEARCH_LANGUAGE: string;
30
+ }
31
+
32
+ /**
33
+ * Configuration defaults
34
+ */
35
+ const DEFAULTS: Config = {
36
+ RESEARCHER_TIMEOUT_MS: 240000,
37
+ MAX_CONCURRENT_RESEARCHERS: 3,
38
+ PROXY_URL: undefined,
39
+ BRAVE_SEARCH_API_KEY: undefined,
40
+ HEALTH_CHECK_TIMEOUT_MS: 15000,
41
+ TUI_REFRESH_DEBOUNCE_MS: 10,
42
+ CONSOLE_RESTORE_DELAY_MS: 15000,
43
+ SEARCH_LANGUAGE: 'en-US',
44
+ };
45
+
46
+ /**
47
+ * Parse environment variable to number with default
48
+ */
49
+ function parseEnvNumber(
50
+ env: Record<string, string | undefined>,
51
+ key: string,
52
+ defaultValue: number,
53
+ base: number = 10
54
+ ): number {
55
+ const value = env[key];
56
+ if (value === undefined) {
57
+ return defaultValue;
58
+ }
59
+ const parsed = parseInt(value, base);
60
+ if (isNaN(parsed)) {
61
+ logger.warn(`[config] Invalid value for ${key}, using default: ${defaultValue}`);
62
+ return defaultValue;
63
+ }
64
+ return parsed;
65
+ }
66
+
67
+ /**
68
+ * Get optional string environment variable
69
+ */
70
+ function parseEnvString(
71
+ env: Record<string, string | undefined>,
72
+ key: string
73
+ ): string | undefined {
74
+ const value = env[key];
75
+ return value === undefined ? undefined : value;
76
+ }
77
+
78
+ /**
79
+ * Create configuration from environment variables
80
+ * @param env Optional environment object (for testing)
81
+ * @returns Configuration object
82
+ */
83
+ export function createConfig(env: Record<string, string | undefined> = process.env): Config {
84
+ return {
85
+ RESEARCHER_TIMEOUT_MS: parseEnvNumber(
86
+ env,
87
+ 'PI_RESEARCH_RESEARCHER_TIMEOUT_MS',
88
+ DEFAULTS.RESEARCHER_TIMEOUT_MS
89
+ ),
90
+ MAX_CONCURRENT_RESEARCHERS: parseEnvNumber(
91
+ env,
92
+ 'PI_RESEARCH_MAX_CONCURRENT_RESEARCHERS',
93
+ DEFAULTS.MAX_CONCURRENT_RESEARCHERS
94
+ ),
95
+ PROXY_URL: parseEnvString(env, 'PROXY_URL'),
96
+ BRAVE_SEARCH_API_KEY: parseEnvString(env, 'BRAVE_SEARCH_API_KEY'),
97
+ HEALTH_CHECK_TIMEOUT_MS: parseEnvNumber(
98
+ env,
99
+ 'PI_RESEARCH_HEALTH_CHECK_TIMEOUT_MS',
100
+ DEFAULTS.HEALTH_CHECK_TIMEOUT_MS as number,
101
+ ),
102
+ TUI_REFRESH_DEBOUNCE_MS: parseEnvNumber(
103
+ env,
104
+ 'PI_RESEARCH_TUI_REFRESH_DEBOUNCE_MS',
105
+ DEFAULTS.TUI_REFRESH_DEBOUNCE_MS
106
+ ),
107
+ CONSOLE_RESTORE_DELAY_MS: parseEnvNumber(
108
+ env,
109
+ 'PI_RESEARCH_CONSOLE_RESTORE_DELAY_MS',
110
+ DEFAULTS.CONSOLE_RESTORE_DELAY_MS
111
+ ),
112
+ SEARCH_LANGUAGE: parseEnvString(env, 'PI_RESEARCH_SEARCH_LANGUAGE') ?? DEFAULTS.SEARCH_LANGUAGE,
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Global configuration instance (for backward compatibility)
118
+ */
119
+ let globalConfig: Config | null = null;
120
+
121
+ /**
122
+ * Get global configuration
123
+ * Creates from environment if not yet initialized
124
+ */
125
+ export function getConfig(): Config {
126
+ if (!globalConfig) {
127
+ globalConfig = createConfig();
128
+ }
129
+ return globalConfig;
130
+ }
131
+
132
+ /**
133
+ * Set global configuration (for testing or manual override)
134
+ */
135
+ export function setConfig(config: Config): void {
136
+ globalConfig = config;
137
+ }
138
+
139
+ /**
140
+ * Reset global configuration to environment defaults
141
+ */
142
+ export function resetConfig(): void {
143
+ globalConfig = null;
144
+ }
145
+
146
+ /**
147
+ * Validate configuration values
148
+ * @param config Configuration to validate
149
+ * @throws {Error} if configuration is invalid
150
+ */
151
+ export function validateConfig(config: Config = getConfig()): void {
152
+ if (config.RESEARCHER_TIMEOUT_MS < 30000 || config.RESEARCHER_TIMEOUT_MS > 600000) {
153
+ throw new Error(
154
+ `PI_RESEARCH_RESEARCHER_TIMEOUT_MS must be between 30000ms (30s) and 600000ms (10m), got ${config.RESEARCHER_TIMEOUT_MS}ms`
155
+ );
156
+ }
157
+
158
+ if (config.MAX_CONCURRENT_RESEARCHERS < 1 || config.MAX_CONCURRENT_RESEARCHERS > 10) {
159
+ throw new Error(
160
+ `PI_RESEARCH_MAX_CONCURRENT_RESEARCHERS must be between 1 and 10, got ${config.MAX_CONCURRENT_RESEARCHERS}`
161
+ );
162
+ }
163
+
164
+ // Warn if proxy is configured
165
+ if (config.PROXY_URL) {
166
+ logger.warn('[config] Proxy configured - searches will be routed through:', config.PROXY_URL);
167
+ }
168
+
169
+ logger.debug('[config] Configuration validated:', {
170
+ RESEARCHER_TIMEOUT_MS: config.RESEARCHER_TIMEOUT_MS,
171
+ PROXY_URL: config.PROXY_URL,
172
+ HEALTH_CHECK_TIMEOUT_MS: config.HEALTH_CHECK_TIMEOUT_MS,
173
+ });
174
+ }
175
+
176
+ // ============================================================================
177
+ // Backward Compatibility Exports
178
+ // ============================================================================
179
+
180
+ /**
181
+ * @deprecated Use getConfig().RESEARCHER_TIMEOUT_MS instead
182
+ * Returns current config value - reflects environment changes if config is reset
183
+ */
184
+ export function getResearcherTimeoutMs(): number {
185
+ return getConfig().RESEARCHER_TIMEOUT_MS;
186
+ }
187
+
188
+ /**
189
+ * @deprecated Use getConfig().PROXY_URL instead
190
+ * Returns current config value - reflects environment changes if config is reset
191
+ */
192
+ export function getProxyUrl(): string | undefined {
193
+ return getConfig().PROXY_URL;
194
+ }
195
+
196
+ /**
197
+ * @deprecated Use getConfig().BRAVE_SEARCH_API_KEY instead
198
+ */
199
+ export function getBraveSearchApiKey(): string | undefined {
200
+ return getConfig().BRAVE_SEARCH_API_KEY;
201
+ }
202
+
203
+ // Legacy variable exports (for backward compatibility, but reflect module load time values)
204
+ export const RESEARCHER_TIMEOUT_MS = getResearcherTimeoutMs();
205
+ export const PROXY_URL = getProxyUrl();
206
+ export const BRAVE_SEARCH_API_KEY = getBraveSearchApiKey();
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Project Constants
3
+ *
4
+ * Centralized constants for magic numbers and configuration values.
5
+ */
6
+
7
+ // ==================== Time Constants ====================
8
+
9
+ /** Flash duration for UI slice animations in milliseconds */
10
+ export const FLASH_DURATION_MS = 500;
11
+
12
+ /** Delay between API requests in milliseconds (e.g., NVD rate limit) */
13
+ export const REQUEST_DELAY_MS = 6500;
14
+
15
+ // ==================== Research Constraints ====================
16
+
17
+ /** Maximum gathering (tool) calls per researcher */
18
+ export const MAX_GATHERING_CALLS = 4;
19
+
20
+ /** Maximum scrape tool calls per researcher (1 handshake + up to 3 execution batches) */
21
+ export const MAX_SCRAPE_CALLS = 4;
22
+
23
+ /** Maximum URLs to scrape per batch */
24
+ export const MAX_SCRAPE_URLS = 3;
25
+
26
+ /** Maximum siblings (researchers) allowed to run simultaneously across all rounds */
27
+ export const MAX_CONCURRENT_RESEARCHERS = 3;
28
+
29
+
30
+ // ==================== Complexity Levels ====================
31
+
32
+ /**
33
+ * Complexity 0: Quick mode (no orchestrator, 1 researcher, 1 round)
34
+ * Complexity 1: Normal (2 researchers, 2 rounds)
35
+ * Complexity 2: Deep (3 researchers, 3 rounds)
36
+ * Complexity 3: Ultra (5 researchers, 5 rounds)
37
+ */
38
+
39
+ /** Initial researcher count for complexity 1 (Normal) */
40
+ export const INITIAL_RESEARCHERS_LEVEL_1 = 2;
41
+
42
+ /** Initial researcher count for complexity 2 (Deep) */
43
+ export const INITIAL_RESEARCHERS_LEVEL_2 = 3;
44
+
45
+ /** Initial researcher count for complexity 3 (Ultra) */
46
+ export const INITIAL_RESEARCHERS_LEVEL_3 = 5;
47
+
48
+
49
+ /** Maximum rounds for complexity 1 (Normal) */
50
+ export const MAX_ROUNDS_LEVEL_1 = 2;
51
+
52
+ /** Maximum rounds for complexity 2 (Deep) */
53
+ export const MAX_ROUNDS_LEVEL_2 = 3;
54
+
55
+ /** Maximum rounds for complexity 3 (Ultra) */
56
+ export const MAX_ROUNDS_LEVEL_3 = 5;
57
+
58
+ /**
59
+ * Extra rounds the lead evaluator may earn beyond targetRounds when it judges
60
+ * critical gaps still exist. Rounds targetRounds+1 through targetRounds+MAX_EXTRA_ROUNDS
61
+ * are "bonus" territory; round targetRounds+MAX_EXTRA_ROUNDS+1 is unreachable (hard limit).
62
+ */
63
+ export const MAX_EXTRA_ROUNDS = 2;
64
+
65
+
66
+ // ==================== Timeout Constants ====================
67
+
68
+ /** Default timeout for SearXNG requests in milliseconds */
69
+ export const SEARXNG_TIMEOUT_MS = 30000;
70
+
71
+ /** Default timeout for OSV API requests in milliseconds */
72
+ export const OSV_TIMEOUT_MS = 30000;
73
+
74
+ /** Default timeout for NVD API requests in milliseconds */
75
+ export const NVD_TIMEOUT_MS = 30000;
76
+
77
+ // ==================== Export Constants ====================
78
+
79
+ /** Maximum query length for filename sanitization */
80
+ export const MAX_QUERY_LENGTH = 50;
81
+
82
+ /** Maximum retry attempts for export file collision */
83
+ export const MAX_EXPORT_RETRIES = 3;
84
+
85
+ // ==================== Retry Constants ====================
86
+
87
+ /** Default maximum retry attempts for transient errors */
88
+ export const DEFAULT_MAX_RETRIES = 3;
89
+
90
+ /** Default initial delay for exponential backoff in milliseconds */
91
+ export const DEFAULT_INITIAL_DELAY_MS = 1000;
92
+
93
+ /** Default maximum delay for exponential backoff in milliseconds */
94
+ export const DEFAULT_MAX_DELAY_MS = 10000;
95
+
96
+ // ==================== Context-Aware Scraping Constants ====================
97
+
98
+ /**
99
+ * Fraction of context window consumed beyond which all scraping is blocked.
100
+ * At or above this threshold, researchers skip batches and move to synthesis.
101
+ */
102
+ export const MAX_CONTEXT_FRACTION_FOR_SCRAPING = 0.55;
103
+
104
+ /**
105
+ * Estimated tokens consumed per scrape call (handshake + content).
106
+ * Used to project whether another batch is feasible given remaining context.
107
+ */
108
+ export const AVG_TOKENS_PER_SCRAPE = 15000;
109
+
110
+ /**
111
+ * Fallback context window size (tokens) when the model does not advertise one.
112
+ * Corresponds to typical large-context models (Sonnet, Gemini 1.5 Pro, etc.).
113
+ */
114
+ export const DEFAULT_MODEL_CONTEXT_WINDOW = 200000;
115
+
116
+ /**
117
+ * Maximum URLs per second-scrape batch (more targeted than batch 1).
118
+ * Kept lower to preserve context for synthesis after focused follow-up.
119
+ */
120
+ export const BATCH_2_MAX_SCRAPE_URLS = 2;
121
+
122
+ /**
123
+ * Default maxConcurrency for the second scrape batch.
124
+ * Higher than batch 1 because batch 2 URLs are already prioritized.
125
+ */
126
+ export const BATCH_2_DEFAULT_CONCURRENCY = 15;
127
+
128
+ // ==================== UI Constants ====================
129
+
130
+ /** Maximum characters for final report before truncation */
131
+ export const MAX_REPORT_LENGTH = 200000;
132
+
133
+ /** Visual width of the ====------ progress bar in the TUI header */
134
+ export const PROGRESS_BAR_WIDTH = 18;
135
+
136
+ // ==================== Orchestrator Constants ====================
137
+
138
+ /** Maximum characters per researcher report when sent to lead evaluator (truncated with link preservation) */
139
+ export const MAX_EVALUATOR_REPORT_LENGTH = 50000;
140
+
141
+ /** Delay in milliseconds between launching concurrent researchers to stagger startup */
142
+ export const RESEARCHER_LAUNCH_DELAY_MS = 1500;
143
+
144
+ /** Delay in milliseconds after state update to ensure propagation before next action */
145
+ export const STATE_PROPAGATION_DELAY_MS = 50;
146
+
147
+ /** Minimum streaming text length before updating UI with estimated tokens */
148
+ export const STREAMING_UPDATE_THRESHOLD_CHARS = 200;