@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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lincoln504
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ ![pi-research banner](docs/assets/README-banner.jpg)
2
+
3
+ <a href="https://github.com/Lincoln504/pi-research/actions/workflows/ci.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/Lincoln504/pi-research/ci.yml?style=flat-square&branch=main" /></a> <a href="https://www.npmjs.com/package/@lincoln504/pi-research"><img alt="npm version" src="https://img.shields.io/npm/v/@lincoln504/pi-research.svg?style=flat-square" /></a>
4
+
5
+ Multi-agent web research extension for [pi](https://github.com/badlogic/pi-mono). Runs parallel AI researchers against a self-hosted SearXNG search container, scrapes pages, queries security databases, and tracks everything in a real-time terminal UI.
6
+
7
+ ---
8
+
9
+ ## Contents
10
+
11
+ - [Capabilities](#capabilities)
12
+ - [Requirements](#requirements)
13
+ - [Install](#install)
14
+ - [Platform Setup](#platform-setup)
15
+ - [Usage](#usage)
16
+ - [Research Depth](#research-depth)
17
+ - [Configuration](#configuration)
18
+ - [Development & Release](#development--release)
19
+ - [Architecture & Design](docs/ARCHITECTURE.md)
20
+
21
+ ---
22
+
23
+ ## Capabilities
24
+
25
+ - **Multi-agent deep research** — AI coordinator decomposes a query into parallel research tracks; an AI lead evaluator synthesizes or delegates further rounds
26
+ - **Quick mode** — single researcher for fast, focused queries with no coordinator overhead
27
+ - **Web search** — SearXNG running in Docker, aggregating Bing, Brave, Yahoo, Qwant, DuckDuckGo, Wikipedia, GitHub, arXiv, and Semantic Scholar (engines can be enabled or disabled via [config](docs/ARCHITECTURE.md#searxng-lifecycle-management))
28
+ - **URL scraping** — fetch-first with Playwright/Chromium fallback for JavaScript-heavy pages
29
+ - **Security databases** — NVD, CISA KEV, GitHub Advisories, OSV
30
+ - **Stack Exchange** — full network search and filtering
31
+ - **Real-time TUI** — per-researcher token and cost tracking
32
+
33
+ ```
34
+ ── Research: 70% ─────────────────────────────────────
35
+ ┌───────┬────┐ 1 ┌─────┬────┐ 2 ┌────┬────┐ 3 ┌────┐
36
+ │SearXNG│ 18k │ 36k │ 50k │
37
+ │:55732 │ $0.0056 │ $0.0055 │ $0.0071 │
38
+ └───────┴──────────────┴─────────────┴─────────────┘
39
+ ```
40
+
41
+
42
+ ---
43
+
44
+ ## Requirements
45
+
46
+ - Node.js `22.13.0` or any `24.x+` release
47
+ - The `pi` CLI installed and configured
48
+ - Docker daemon running (required for SearXNG)
49
+ - Internet access
50
+
51
+ ---
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ pi install npm:@lincoln504/pi-research
57
+ ```
58
+
59
+ **From a checkout**
60
+
61
+ ```bash
62
+ npm install
63
+ pi install .
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Platform Setup
69
+
70
+ **macOS/Linux** — Install and start Docker. Ensure your user is in the `docker` group on Linux.
71
+ **Windows** — Install Docker Desktop (Linux containers). Detecting named pipes is automatic.
72
+
73
+ ---
74
+
75
+ ## Usage
76
+
77
+ Ask pi for web research. The extension registers a `research` tool.
78
+
79
+ ```text
80
+ Research "What is a binary search tree?"
81
+ Do a deep dive on CVE-2024-3094 at depth 2
82
+ ```
83
+
84
+ | Depth | Mode | Researchers | Target rounds |
85
+ |-------|------|-------------|---------------|
86
+ | 0 (default) | Quick | 1 | 1 |
87
+ | 1 | Normal | 2 | 2 |
88
+ | 2 | Deep | 3 | 3 |
89
+ | 3 | Ultra | 5 | 5 |
90
+
91
+ ---
92
+
93
+ ## Configuration
94
+
95
+ | Variable | Default | Description |
96
+ |----------|---------|-------------|
97
+ | `BRAVE_SEARCH_API_KEY` | — | Enables `braveapi` engine (official REST API) |
98
+ | `PI_RESEARCH_VERBOSE` | — | Set to `1` to write diagnostic JSONL logs to a temp file (see below) |
99
+ | `PI_RESEARCH_SKIP_HEALTHCHECK` | — | Skip startup health check (set to `1` if engines are blocked) |
100
+ | `PROXY_URL` | — | Proxy for SearXNG outgoing requests (e.g. `socks5://127.0.0.1:9050`) |
101
+ | `STACKEXCHANGE_API_KEY` | — | Stack Exchange API key (increases limit from 300 to 10,000/day) |
102
+ | `PI_RESEARCH_SEARCH_LANGUAGE` | `en-US` | BCP 47 language tag for search result filtering |
103
+ | `PI_RESEARCH_RESEARCHER_TIMEOUT_MS` | `240000` | Per-researcher timeout (30s to 10m) |
104
+ | `PI_RESEARCH_MAX_CONCURRENT_RESEARCHERS` | `3` | Maximum researchers running simultaneously (1 to 10) |
105
+ | `PI_RESEARCH_CONSOLE_RESTORE_DELAY_MS` | `15000` | Delay before TUI clears after research ends |
106
+ | `SEARXNG_IMAGE_TAG` | `latest` | SearXNG Docker image tag — pin to a specific release for reproducibility (e.g. `2024.12.23`) |
107
+
108
+ **Verbose logging**
109
+
110
+ Set `PI_RESEARCH_VERBOSE=1` (or pass `--verbose`) to enable diagnostic output. Logs are written as JSONL to `{os.tmpdir()}/pi-research-debug-{hash}.log` — one file per session, never written to stdout. Each log line includes a session correlation ID so multiple concurrent sessions are distinguishable. The log path is printed to stderr on startup when verbose mode is active.
111
+
112
+ ---
113
+
114
+ ## Development & Release
115
+
116
+ **Commands**
117
+ - `npm run lint` / `npm run lint:fix`: Code quality
118
+ - `npm run type-check`: TypeScript verification
119
+ - `npm run test:unit`: Unit tests — no dependencies
120
+ - `npm run test:integration`: Integration tests — requires Docker; uses Testcontainers to spin up a real SearXNG instance (runs locally and in CI)
121
+
122
+ **Release**
123
+ 1. `npm version patch` (or minor/major)
124
+ 2. `git push origin main --tags`
125
+ The GitHub Action will automatically publish to npm and create a release.
126
+
127
+ ---
128
+
129
+ ## Architecture
130
+
131
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed design information.
@@ -0,0 +1,189 @@
1
+ # SearXNG Default Configuration for pi-search-scrape Extension
2
+ # IMPROVED: Better timeouts, removed problematic engines, added real user agent
3
+
4
+ general:
5
+ instance_name: "pi SearXNG"
6
+ debug: false
7
+ privacypolicy_url: false
8
+ contact_url: false
9
+ enable_metrics: true # ENABLED for debugging engine issues
10
+ open_metrics: ''
11
+
12
+ brand:
13
+ new_issue_url: https://github.com/searxng/searxng/issues/new
14
+ docs_url: https://docs.searxng.org/
15
+ public_instances: https://searx.space
16
+ wiki_url: https://github.com/searxng/searxng/wiki
17
+ issue_url: https://github.com/searxng/searxng/issues
18
+
19
+ server:
20
+ port: 8080
21
+ bind_address: "0.0.0.0"
22
+ secret_key: ""
23
+ base_url: false
24
+ limiter: false
25
+ public_instance: false
26
+ image_proxy: false
27
+ http_protocol_version: "1.1"
28
+ method: "GET"
29
+ default_http_headers:
30
+ # REAL BROWSER USER AGENT - prevents bot detection
31
+ User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
32
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
33
+ Accept-Language: "en-US,en;q=0.9"
34
+ Accept-Encoding: "gzip, deflate, br"
35
+ X-Content-Type-Options: nosniff
36
+ X-Download-Options: noopen
37
+ X-Robots-Tag: noindex, nofollow
38
+ Referrer-Policy: no-referrer
39
+
40
+ search:
41
+ safe_search: 1
42
+ autocomplete: ""
43
+ autocomplete_min: 4
44
+ favicon_resolver: ""
45
+ default_lang: "en-US"
46
+ ban_time_on_fail: 5
47
+ max_ban_time_on_fail: 120
48
+ formats:
49
+ - html
50
+ - json
51
+ - csv
52
+ - rss
53
+ suspended_times:
54
+ # Long backoff prevents hammering blocked engines — re-check after 24h
55
+ SearxEngineAccessDenied: 86400
56
+ SearxEngineCaptcha: 86400
57
+ SearxEngineTooManyRequests: 3600
58
+ cf_SearxEngineCaptcha: 1296000
59
+ cf_SearxEngineAccessDenied: 86400
60
+ recaptcha_SearxEngineCaptcha: 604800
61
+
62
+ ui:
63
+ static_path: ""
64
+ templates_path: ""
65
+ query_in_title: false
66
+ default_theme: simple
67
+ center_alignment: false
68
+ default_locale: ""
69
+ results_on_new_tab: false
70
+ theme_args:
71
+ simple_style: auto
72
+ search_on_category_select: true
73
+ hotkeys: default
74
+ url_formatting: pretty
75
+
76
+ outgoing:
77
+ request_timeout: 20.0 # INCREASED from 10.0
78
+ max_request_timeout: 60.0 # INCREASED from 30.0
79
+ useragent_suffix: ""
80
+ pool_connections: 200 # INCREASED from 150
81
+ pool_maxsize: 30 # INCREASED from 20
82
+ enable_http2: true # ENABLED (was false)
83
+ retries: 3 # INCREASED from 2
84
+ using_tor_proxy: false
85
+ extra_proxy_timeout: 15
86
+
87
+ doi_resolvers:
88
+ oadoi.org: 'https://oadoi.org/'
89
+ doi.org: 'https://doi.org/'
90
+ default_doi_resolver: 'oadoi.org'
91
+
92
+ # Search engines configuration
93
+ #
94
+ # Engine reliability for a private low-volume self-hosted instance (2026):
95
+ # Bing — most consistently available; scrapes bing.com directly; weak on ambiguous queries
96
+ # Brave — independent index; moderate bot detection; occasional suspension at burst rates
97
+ # Yahoo — different backend from Bing; low blocking rate; lower result quality
98
+ # Qwant — EU-based independent index; low-volume private use is generally fine
99
+ # DuckDuckGo — ddg-lite endpoint (no vqd); intermittent IP blocks (issue #4824, open)
100
+ # StartPage — DISABLED: broken in current SearXNG due to missing sgt param (issue #4549, open)
101
+ # Google — DISABLED: IP-blocks any nontrivial automated use
102
+ #
103
+ # If BRAVE_SEARCH_API_KEY is set, searxng-lifecycle.ts injects a braveapi engine at weight 1.2
104
+ # which uses the official REST API and is not subject to bot detection.
105
+
106
+ engines:
107
+ # Bing: most reliable free general-web engine for private instances at low volume
108
+ - name: bing
109
+ engine: bing
110
+ shortcut: bi
111
+ weight: 1.5
112
+
113
+ # Brave (scraping): independent index, different coverage from Bing
114
+ # Can enter suspension after bursts; max_page 10 respected by SearXNG
115
+ - name: brave
116
+ engine: brave
117
+ shortcut: bv
118
+ weight: 0.8
119
+
120
+ # Yahoo: distinct backend, low blocking rate, fills gaps when others are suspended
121
+ - name: yahoo
122
+ engine: yahoo
123
+ shortcut: yh
124
+ weight: 0.8
125
+
126
+ # Qwant: EU-based independent index, low-volume private use stable
127
+ - name: qwant
128
+ engine: qwant
129
+ shortcut: qw
130
+ weight: 0.7
131
+
132
+ # DuckDuckGo: ddg-lite endpoint (no vqd); intermittent IP blocks (issue #4824 open)
133
+ - name: duckduckgo
134
+ engine: duckduckgo
135
+ shortcut: ddg
136
+ weight: 0.6
137
+
138
+ # DISABLED: startpage — sgt parameter missing in SearXNG parser (issue #4549, open)
139
+ # DISABLED: google — IP-blocks automated access within minutes
140
+
141
+ # Wikipedia: Encyclopedic reference
142
+ # - Reliability: HIGH (uses REST API)
143
+ # - Note: Keep weight low to prioritize search engines
144
+ - name: wikipedia
145
+ engine: wikipedia
146
+ shortcut: wp
147
+ categories: [general]
148
+ weight: 0.3 # LOWER - reference only, not primary search
149
+
150
+ # Images (only when category=images is requested)
151
+ - name: bing images
152
+ engine: bing_images
153
+ categories: [images]
154
+ shortcut: bii
155
+
156
+ # News (only when category=news is requested)
157
+ - name: bing news
158
+ engine: bing_news
159
+ categories: [news]
160
+ shortcut: bin
161
+
162
+ # REMOVED: brave_news (engine not available in SearXNG container)
163
+
164
+ # Videos (only when category=videos is requested)
165
+ - name: bing videos
166
+ engine: bing_videos
167
+ categories: [videos]
168
+ shortcut: biv
169
+
170
+ # Technical/IT (only when category=it is requested)
171
+ # REMOVED: stackoverflow (engine not available in SearXNG container)
172
+ - name: github
173
+ engine: github
174
+ categories: [it]
175
+ shortcut: gh
176
+ weight: 1.0
177
+
178
+ # Science/Academic (only when category=science is requested)
179
+ - name: arxiv
180
+ engine: arxiv
181
+ categories: [science]
182
+ weight: 1.5 # INCREASED
183
+ shortcut: arx
184
+
185
+ - name: semantic scholar
186
+ engine: semantic_scholar
187
+ categories: [science]
188
+ weight: 1.5 # INCREASED
189
+ shortcut: sem
@@ -0,0 +1,34 @@
1
+ # SearXNG Limiter Configuration for pi-search-scrape
2
+ # This configuration disables all bot detection and rate limiting for local use
3
+
4
+ [botdetection]
5
+ # The prefix defines number of leading bits in an address that are compared
6
+ # to determine whether or not an address is part of a (client) network.
7
+ ipv4_prefix = 32
8
+ ipv6_prefix = 48
9
+
10
+ # Trust localhost requests completely (no bot detection for local connections)
11
+ trusted_proxies = [
12
+ '127.0.0.0/8',
13
+ '::1',
14
+ ]
15
+
16
+ [botdetection.ip_limit]
17
+ # Disable rate limiting for link-local addresses (localhost)
18
+ filter_link_local = false
19
+ # Disable link token method (no captcha required)
20
+ link_token = false
21
+
22
+ [botdetection.ip_lists]
23
+ # No IP blocking for local use
24
+ block_ip = []
25
+
26
+ # Allow all localhost requests without restrictions
27
+ pass_ip = [
28
+ '127.0.0.0/8', # IPv4 localhost
29
+ 'fe80::/10', # IPv6 link-local
30
+ '0.0.0.0/0', # Allow all IPv4 (for local container use)
31
+ ]
32
+
33
+ # Allow SearXNG org IPs for checks
34
+ pass_searxng_org = true
@@ -0,0 +1,73 @@
1
+ import js from '@eslint/js';
2
+ import tseslint from '@typescript-eslint/eslint-plugin';
3
+ import tsparser from '@typescript-eslint/parser';
4
+
5
+ export default [
6
+ // Global ignores
7
+ {
8
+ ignores: [
9
+ 'node_modules/**',
10
+ 'dist/**',
11
+ '*.d.ts',
12
+ 'examples/**',
13
+ ],
14
+ },
15
+ // JavaScript recommended rules
16
+ js.configs.recommended,
17
+ // TypeScript recommended rules
18
+ {
19
+ files: ['**/*.ts'],
20
+ languageOptions: {
21
+ parser: tsparser,
22
+ parserOptions: {
23
+ ecmaVersion: 2022,
24
+ sourceType: 'module',
25
+ project: './tsconfig.json',
26
+ },
27
+ globals: {
28
+ process: 'readonly',
29
+ console: 'readonly',
30
+ Buffer: 'readonly',
31
+ __dirname: 'readonly',
32
+ __filename: 'readonly',
33
+ require: 'readonly',
34
+ module: 'readonly',
35
+ exports: 'readonly',
36
+ NodeJS: 'readonly',
37
+ setTimeout: 'readonly',
38
+ setInterval: 'readonly',
39
+ clearTimeout: 'readonly',
40
+ clearInterval: 'readonly',
41
+ crypto: 'readonly',
42
+ AbortSignal: 'readonly',
43
+ AbortController: 'readonly',
44
+ Request: 'readonly',
45
+ RequestInit: 'readonly',
46
+ Response: 'readonly',
47
+ FormData: 'readonly',
48
+ fetch: 'readonly',
49
+ URL: 'readonly',
50
+ URLSearchParams: 'readonly',
51
+ ArrayBuffer: 'readonly',
52
+ ReadableStream: 'readonly',
53
+ Blob: 'readonly',
54
+ },
55
+ },
56
+ plugins: {
57
+ '@typescript-eslint': tseslint,
58
+ },
59
+ rules: {
60
+ // Override specific rules
61
+ 'no-console': 'off', // Allow console.log for debugging
62
+ 'no-unused-vars': 'off', // Disable in favor of TypeScript version
63
+ '@typescript-eslint/no-unused-vars': [
64
+ 'error',
65
+ {
66
+ argsIgnorePattern: '^_',
67
+ varsIgnorePattern: '^_',
68
+ caughtErrorsIgnorePattern: '^_',
69
+ },
70
+ ],
71
+ },
72
+ },
73
+ ];
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import baseConfig from './vitest.config';
3
+
4
+ export default defineConfig({
5
+ ...baseConfig,
6
+ test: {
7
+ ...baseConfig.test,
8
+ // Integration tests using testcontainers
9
+ include: ['test/integration/**/*.test.ts'],
10
+ setupFiles: [],
11
+ testTimeout: 180000, // 180 second timeout for integration tests
12
+ maxConcurrency: 1, // Only one integration test at a time due to containers
13
+ fileParallelism: false,
14
+ pool: 'forks',
15
+ hookTimeout: 60000,
16
+ },
17
+ });
@@ -0,0 +1,40 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import { fileURLToPath } from 'node:url';
3
+ import path from 'node:path';
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+
7
+ export default defineConfig({
8
+ test: {
9
+ globals: true,
10
+ environment: 'node',
11
+ include: ['**/*.test.ts', '**/*.spec.ts'],
12
+ exclude: ['node_modules', 'dist', '.tmp'],
13
+ coverage: {
14
+ provider: 'v8',
15
+ reporter: ['text', 'json', 'html', 'lcov'],
16
+ exclude: [
17
+ 'node_modules/',
18
+ 'dist/',
19
+ '**/*.test.ts',
20
+ '**/*.spec.ts',
21
+ '**/types.ts',
22
+ '**/index.ts',
23
+ 'test/',
24
+ ],
25
+ reportOnFailure: true,
26
+ },
27
+ testTimeout: 60000,
28
+ hookTimeout: 30000,
29
+ teardownTimeout: 10000,
30
+ pool: 'threads',
31
+ includeSource: ['src/**/*.ts'],
32
+ setupFiles: ['./test/setup/unit.ts'],
33
+ },
34
+ resolve: {
35
+ alias: {
36
+ '@': path.resolve(__dirname, './src'),
37
+ '@test': path.resolve(__dirname, './test'),
38
+ },
39
+ },
40
+ });
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import baseConfig from './vitest.config';
3
+
4
+ export default defineConfig({
5
+ ...baseConfig,
6
+ test: {
7
+ ...baseConfig.test,
8
+ include: ['test/unit/**/*.test.ts'],
9
+ setupFiles: ['./test/setup/unit.ts'],
10
+ testTimeout: 10000,
11
+ },
12
+ });
@@ -0,0 +1,109 @@
1
+ # Architecture
2
+
3
+ ## Orchestration Modes
4
+
5
+ Two primary modes: **Quick Mode** and **Deep Mode**.
6
+
7
+ ### Quick Mode
8
+ Routes research tasks to a single **Agent Researcher** session. No coordination or evaluation overhead — suitable for focused queries.
9
+
10
+ ### Deep Mode
11
+ Uses a state machine to manage a three-status research lifecycle:
12
+
13
+ 1. **Planning Status**: The AI coordinator analyzes the conversation context and generates a JSON-formatted research agenda—a list of high-level research tasks.
14
+ 2. **Researching Status**:
15
+ - **Parallel Execution**: **Agent Researchers** run in parallel (up to 3 concurrent). Each researcher handles one agenda task per round.
16
+ - **Report Injection**: Collective knowledge is managed by the orchestrator. When a researcher completes its task, its full report (up to 50k chars) is **injected** into the context of siblings starting later, or **steered** into already running ones. This ensures researchers don't just know *what* was scraped, but also *what was found*.
17
+ - **Lead Evaluation**: When a round is complete, a **Lead Evaluator** agent call is triggered. It reviews all accumulated findings against the original agenda.
18
+ - **Decision Outcome**: The evaluator makes a binary choice:
19
+ - **Synthesize**: Generate a final, exhaustive Markdown report (transition to **Completed**).
20
+ - **Delegate**: Formulate new, targeted queries for a subsequent round (remain in **Researching**).
21
+ 3. **Completed Status**: The final synthesis is returned as the tool result.
22
+
23
+ State transitions are managed by a pure reducer (`src/orchestration/deep-research-reducer.ts`), ensuring predictable behavior.
24
+
25
+ ---
26
+
27
+ ## The Agent Researcher
28
+
29
+ An **Agent Researcher** is the fundamental building block of `pi-research`. Regardless of the orchestration mode, every researcher follows a structured three-phase internal lifecycle:
30
+
31
+ ### Phase 1: Gathering
32
+ The researcher uses broad tools to discover relevant information and identify high-quality URLs for further investigation.
33
+ - **Budget**: 4 Gathering calls (shared across `search`, `security_search`, `stackexchange`, and `grep`).
34
+ - **Goal**: Breadth-first exploration and URL discovery.
35
+
36
+ ### Phase 2: Scraping
37
+ The researcher deep-dives into the URLs identified during the Gathering phase to extract detailed content.
38
+ - **Budget**: 4 Scrape calls (1 handshake + 3 execution batches).
39
+ - **Goal**: Depth-first extraction of specific data and evidence.
40
+ - **Deduplication**: Researchers check a shared URL pool to avoid re-scraping links already handled by siblings in the same operation.
41
+
42
+ ### Phase 3: Reporting
43
+ The researcher synthesizes its individual findings into a structured Markdown report.
44
+ - **Structure**: Includes an executive summary, key findings, and a list of cited links.
45
+ - **Signaling**: Once finished, the report is submitted to the orchestrator for injection into siblings (Deep Mode) or final return (Quick Mode).
46
+
47
+ ---
48
+
49
+ ## Component Details
50
+
51
+ ### Research Tools & Limits
52
+ Each researcher session is constrained by a `ToolUsageTracker` to prevent infinite loops and manage token consumption:
53
+ - **search**: Interfaces with the local SearXNG instance. Supports multiple queries per call.
54
+ - **scrape**: Multi-batch protocol for page extraction.
55
+ - **security_search**: Queries NVD, CISA KEV, GitHub Advisories, and OSV.
56
+ - **stackexchange**: Queries the Stack Exchange network (Stack Overflow, etc.).
57
+ - **grep**: Local codebase search using `ripgrep` (fallback to `grep`).
58
+
59
+ ### Context-Aware Scraping Protocol
60
+ To avoid exceeding LLM context windows, the `scrape` tool follows a four-step handshake. **Each batch step is gated by a 55% context check** — if the session token count already exceeds 55% of the model's context window, that batch is skipped entirely and the tool returns early.
61
+
62
+ 1. **Handshake**: The researcher identifies target URLs. The tool returns a list of URLs already scraped by siblings. No network activity; no context check.
63
+ 2. **Batch 1**: Up to 3 URLs — primary broad scraping.
64
+ 3. **Batch 2**: Up to 2 URLs — targeted follow-up, deduplicated against Batch 1.
65
+ 4. **Batch 3**: Up to 3 URLs — deep-dive scraping.
66
+
67
+ ### Shared Link Deduplication
68
+ Researchers share a URL pool per research operation, used by the `scrape` handshake to avoid redundant network requests. The pool tracks URLs only — findings are shared separately via Report Injection. It resets for each new query.
69
+
70
+ ### SearXNG Lifecycle Management
71
+ SearXNG runs as a Docker container (`searxng/searxng`, tag controlled by `SEARXNG_IMAGE_TAG`, default `latest`).
72
+ - **Lazy Initialization**: The container starts only upon the first research request.
73
+ - **Singleton Pattern**: A single container is shared across all pi processes on the machine. State is persisted to `~/.pi/state/searxng-singleton.json`; each pi process registers a session by PID and the container stays alive until all registered processes have exited.
74
+ - **Configuration**: Runtime settings (proxies, API keys) are generated dynamically and volume-mounted into the container.
75
+ - **Engine configuration**: Active search engines are defined in `config/searxng/default-settings.yml`. Engines can be enabled or disabled by editing that file before starting the container.
76
+ - **Cross-Session Persistence**: Container state and locking are managed via `~/.pi/state/searxng-singleton.json` to handle multiple concurrent pi processes.
77
+
78
+ ### Output File Location
79
+
80
+ When research completes, the report is written to a `.md` file named `pi-research-{sanitized-query}-{hash}.md` (e.g. `pi-research-rust-async-runtimes-a3.md`). The destination directory is resolved from the pi session's working directory (`cwd`) using a three-tier priority:
81
+
82
+ 1. **cwd is home or a system directory** → `os.tmpdir()`. Prevents cluttering `~`, drive roots, or system paths like `/usr`.
83
+ 2. **cwd contains a recognised subdirectory** → that subdirectory. Probed in priority order: `research`, `docs`, `doc`, `ref`, `references`, `notes`. Matching is case-insensitive so it works on macOS APFS and Windows NTFS.
84
+ 3. **Otherwise** → cwd itself.
85
+
86
+ The two-character hash suffix (`{letter}{digit}`, e.g. `b7`) is a collision guard — the file is opened with an exclusive-create flag and the hash is regenerated on `EEXIST`. The full saved path is appended as a footer in the report and returned to the pi UI.
87
+
88
+ ### TUI Progress Percentage
89
+
90
+ The header percentage (`Research: 70%`) tracks tool-call units completed against a budget established after planning. The budget is set from the number of agenda items the coordinator produces — which scales with query complexity (complexity 1 → 2 initial researchers, 2 → 3, 3 → 5), each researcher allocated 10 units. The budget expands dynamically when the evaluator delegates additional rounds. If research goes beyond the planned rounds the display switches to `exploring` instead of a percentage.
91
+
92
+ ### Evaluator Decision Framework
93
+ The Lead Evaluator follows a strict priority list:
94
+ 1. **Error Check**: If all researchers in a round failed, it reports a critical failure and stops.
95
+ 2. **Coverage Check**: It compares cumulative findings against the initial agenda items.
96
+ 3. **Round Budget**: It checks the round budget (`targetRounds + MAX_EXTRA_ROUNDS`). The hard limit is `targetRounds + 2` bonus rounds.
97
+ 4. **Action Selection**: Produces either a Markdown synthesis (final result) or a JSON delegation object (`{"action": "delegate", "queries": [...]}`). Any non-JSON response is treated as a final synthesis to prevent the research from getting stuck.
98
+
99
+ ---
100
+
101
+ ## Project Structure
102
+
103
+ - `src/orchestration/`: Core state machine, coordinator, and evaluator logic.
104
+ - `src/infrastructure/`: Docker container management and SearXNG lifecycle.
105
+ - `src/tools/`: Implementations for search, scraping, and specialized database queries.
106
+ - `src/tui/`: Real-time terminal UI components using `pi`'s display capabilities.
107
+ - `src/utils/`: Shared utilities for token tracking, JSON extraction, and link deduplication.
108
+ - `src/prompts/`: Markdown-based system instructions for the Coordinator, Researcher, and Evaluator.
109
+ - `config/`: Default SearXNG engine and rate-limiter configurations.
Binary file