@lincoln504/pi-research 0.1.12 → 1.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 (248) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +64 -115
  3. package/dist/cli.mjs +23211 -0
  4. package/dist/prompts/research-knowledge-relevance-triage.md +35 -0
  5. package/dist/prompts/research-knowledge-search-extractor.md +41 -0
  6. package/dist/prompts/research-tool-usage.md +92 -0
  7. package/dist/prompts/researcher.md +89 -0
  8. package/dist/prompts/system-coordinator.md +75 -0
  9. package/dist/prompts/system-lead-evaluator.md +118 -0
  10. package/dist/thread-worker.mjs +2611 -0
  11. package/dist/webgpu-probe.mjs +80 -0
  12. package/docs/AGENT-SKILL.md +102 -0
  13. package/docs/ARCHITECTURE.md +306 -80
  14. package/docs/CONFIGURATION.md +249 -0
  15. package/docs/KNOWLEDGE-STORE.md +213 -0
  16. package/docs/PI-EXTENSION.md +91 -0
  17. package/docs/SDK.md +113 -0
  18. package/docs/THIRD-PARTY-NOTICES.md +64 -0
  19. package/package.json +102 -36
  20. package/scripts/build.cjs +140 -0
  21. package/scripts/cleanup.cjs +149 -0
  22. package/scripts/prepare.cjs +28 -0
  23. package/scripts/setup.cjs +134 -0
  24. package/skills/pi-research/README.md +90 -0
  25. package/skills/pi-research/SKILL.md +102 -0
  26. package/skills/pi-research/references/configuration.md +77 -0
  27. package/skills/pi-research/scripts/run.mjs +237 -0
  28. package/src/cleanup/research-cleanup.ts +140 -0
  29. package/src/cli.ts +1044 -0
  30. package/src/config.ts +1036 -147
  31. package/src/constants.ts +115 -84
  32. package/src/core/health-check-service.ts +265 -0
  33. package/src/core/interfaces/health-check-interfaces.ts +50 -0
  34. package/src/core/interfaces/knowledge-interfaces.ts +190 -0
  35. package/src/core/interfaces/observer-interfaces.ts +67 -0
  36. package/src/core/interfaces/orchestration-interfaces.ts +86 -0
  37. package/src/core/interfaces/planning-interfaces.ts +100 -0
  38. package/src/core/interfaces/process-interfaces.ts +37 -0
  39. package/src/core/interfaces/research-plan-types.ts +52 -0
  40. package/src/core/interfaces/scheduler-interfaces.ts +91 -0
  41. package/src/core/interfaces/service-names.ts +39 -0
  42. package/src/core/interfaces/state-manager-interfaces.ts +29 -0
  43. package/src/core/interfaces/steering-interfaces.ts +31 -0
  44. package/src/core/llm/agentic-repair.ts +169 -0
  45. package/src/core/llm/inject-date.ts +52 -0
  46. package/src/core/llm/llm-timeout.ts +64 -0
  47. package/src/core/llm/llm-utils.ts +86 -0
  48. package/src/core/llm/model-registry-factory.ts +287 -0
  49. package/src/core/llm/pi-ai-completion.ts +29 -0
  50. package/src/core/llm/prompts.ts +50 -0
  51. package/src/core/llm/research-model-resolver.ts +91 -0
  52. package/src/core/planning-constants.ts +13 -0
  53. package/src/core/planning-service.ts +649 -0
  54. package/src/core/planning-utils.ts +299 -0
  55. package/src/core/scheduler-factory.ts +48 -0
  56. package/src/core/scheduler-service.ts +123 -0
  57. package/src/core/service-initialization.ts +194 -0
  58. package/src/core/service-interfaces.ts +48 -0
  59. package/src/core/service-registry.ts +659 -0
  60. package/src/healthcheck/index.ts +271 -265
  61. package/src/healthcheck/registry.ts +130 -0
  62. package/src/index.ts +639 -50
  63. package/src/infrastructure/browser/browser-cleanup.ts +309 -0
  64. package/src/infrastructure/browser/browser-client.ts +199 -0
  65. package/src/infrastructure/browser/browser-error-utils.ts +160 -0
  66. package/src/infrastructure/browser/browser-lifecycle.ts +120 -0
  67. package/src/infrastructure/browser/browser-server.ts +174 -0
  68. package/src/infrastructure/browser/browser-task-scheduler.ts +461 -0
  69. package/src/infrastructure/browser/cleanup-utils.ts +155 -0
  70. package/src/infrastructure/browser/client-agent.ts +31 -0
  71. package/src/infrastructure/browser/config.ts +288 -0
  72. package/src/infrastructure/browser/ensure-browser.ts +205 -0
  73. package/src/infrastructure/browser/index.ts +37 -0
  74. package/src/infrastructure/browser/priority-task-queue.ts +226 -0
  75. package/src/infrastructure/browser/scheduler-factory.ts +354 -0
  76. package/src/infrastructure/browser/task-execution-service.ts +209 -0
  77. package/src/infrastructure/browser/thread-worker-browser.ts +282 -0
  78. package/src/infrastructure/browser/thread-worker-lifecycle.ts +323 -0
  79. package/src/infrastructure/browser/thread-worker-messaging.ts +662 -0
  80. package/src/infrastructure/browser/thread-worker-types.ts +25 -0
  81. package/src/infrastructure/browser/thread-worker.mjs +2611 -0
  82. package/src/infrastructure/browser/thread-worker.ts +168 -0
  83. package/src/infrastructure/browser/worker-pool-manager.ts +449 -0
  84. package/src/infrastructure/embedding/embedding-client.ts +205 -0
  85. package/src/infrastructure/embedding/embedding-factory.ts +324 -0
  86. package/src/infrastructure/embedding/embedding-server.ts +535 -0
  87. package/src/infrastructure/file-lock-service.ts +592 -0
  88. package/src/infrastructure/gpu-resource-service.ts +191 -0
  89. package/src/infrastructure/knowledge-store-service.ts +475 -0
  90. package/src/infrastructure/metrics-service.ts +129 -0
  91. package/src/infrastructure/process-lifecycle-service.ts +253 -0
  92. package/src/infrastructure/scheduler-factory-service.ts +58 -0
  93. package/src/infrastructure/service-initialization.ts +292 -0
  94. package/src/infrastructure/state/state-backup-manager.ts +290 -0
  95. package/src/infrastructure/state/state-browser-api.ts +62 -0
  96. package/src/infrastructure/state/state-browser-manager.ts +70 -0
  97. package/src/infrastructure/state/state-manager-service.ts +264 -0
  98. package/src/infrastructure/state/state-manager.ts +547 -0
  99. package/src/infrastructure/state/state-metrics.ts +92 -0
  100. package/src/infrastructure/state/state-migration.ts +103 -0
  101. package/src/infrastructure/state/state-path-configuration.ts +119 -0
  102. package/src/infrastructure/state/state-session-api.ts +94 -0
  103. package/src/infrastructure/state/state-session-manager.ts +127 -0
  104. package/src/infrastructure/state/state-validator.ts +93 -0
  105. package/src/infrastructure/types/state-types.ts +85 -0
  106. package/src/knowledge/chunker.ts +157 -0
  107. package/src/knowledge/embedder-init.ts +413 -0
  108. package/src/knowledge/embedder-utils.ts +296 -0
  109. package/src/knowledge/embedder.ts +592 -0
  110. package/src/knowledge/index.ts +126 -0
  111. package/src/knowledge/lancedb-loader.ts +35 -0
  112. package/src/knowledge/migration.ts +17 -0
  113. package/src/knowledge/model-config.ts +132 -0
  114. package/src/knowledge/onnx-env.ts +48 -0
  115. package/src/knowledge/store-operations.ts +291 -0
  116. package/src/knowledge/store-schema.ts +79 -0
  117. package/src/knowledge/store.ts +1251 -0
  118. package/src/knowledge/transformers-loader.ts +31 -0
  119. package/src/knowledge/webgpu-probe.mjs +80 -0
  120. package/src/knowledge/webgpu-viability.ts +255 -0
  121. package/src/knowledge/writer-queue.ts +252 -0
  122. package/src/logger.ts +316 -122
  123. package/src/observers/research-observer-impl.ts +459 -0
  124. package/src/orchestration/deep-research-orchestrator.ts +524 -773
  125. package/src/orchestration/deep-research-types.ts +47 -35
  126. package/src/orchestration/headless-observer.ts +114 -0
  127. package/src/orchestration/orchestration-types.ts +33 -0
  128. package/src/orchestration/quick-research-orchestrator.ts +463 -0
  129. package/src/orchestration/research-orchestration-service.ts +549 -0
  130. package/src/orchestration/research-session-service.ts +178 -0
  131. package/src/orchestration/research-synthesis-service.ts +348 -0
  132. package/src/orchestration/researcher-executor.ts +394 -0
  133. package/src/orchestration/researcher.ts +115 -42
  134. package/src/orchestration/service-initialization.ts +109 -0
  135. package/src/orchestration/session-context.ts +7 -3
  136. package/src/orchestration/session-state.ts +672 -0
  137. package/src/prompts/research-knowledge-relevance-triage.md +35 -0
  138. package/src/prompts/research-knowledge-search-extractor.md +41 -0
  139. package/src/prompts/research-tool-usage.md +63 -16
  140. package/src/prompts/researcher.md +59 -136
  141. package/src/prompts/system-coordinator.md +64 -13
  142. package/src/prompts/system-lead-evaluator.md +82 -189
  143. package/src/research-config.ts +872 -0
  144. package/src/sdk.ts +847 -0
  145. package/src/security/cisa-kev.ts +72 -9
  146. package/src/security/cvss.ts +92 -0
  147. package/src/security/ecosystem.ts +45 -0
  148. package/src/security/github-advisories.ts +168 -428
  149. package/src/security/github-advisory-types.ts +282 -0
  150. package/src/security/index.ts +63 -177
  151. package/src/security/nvd-client.ts +550 -0
  152. package/src/security/nvd-types.ts +179 -0
  153. package/src/security/osv-client.ts +246 -0
  154. package/src/security/osv-types.ts +285 -0
  155. package/src/security/types.ts +67 -53
  156. package/src/skill-install/skill-installer.ts +519 -0
  157. package/src/stackexchange/index.ts +81 -27
  158. package/src/stackexchange/output/compact.ts +7 -6
  159. package/src/stackexchange/output/table.ts +11 -7
  160. package/src/stackexchange/queries.ts +0 -10
  161. package/src/stackexchange/rest-client.ts +141 -66
  162. package/src/stackexchange/types.ts +0 -17
  163. package/src/tool.ts +15 -423
  164. package/src/tools/grep.ts +32 -310
  165. package/src/tools/health-tool-definition.ts +101 -0
  166. package/src/tools/index.ts +38 -9
  167. package/src/tools/research-knowledge-search.ts +974 -0
  168. package/src/tools/research-knowledge-types.ts +77 -0
  169. package/src/tools/research-tool-definition.ts +494 -0
  170. package/src/tools/scrape.ts +286 -388
  171. package/src/tools/search.ts +98 -153
  172. package/src/tools/security.ts +107 -80
  173. package/src/tools/stackexchange.ts +58 -35
  174. package/src/tools/youtube-transcript.ts +163 -0
  175. package/src/tui/knowledge-search-panel.ts +86 -0
  176. package/src/tui/pulse.ts +86 -0
  177. package/src/tui/research-health.ts +124 -0
  178. package/src/tui/research-panel-color-utils.ts +340 -0
  179. package/src/tui/research-panel-format.ts +54 -0
  180. package/src/tui/research-panel-state.ts +404 -0
  181. package/src/tui/research-panel-wave.ts +280 -0
  182. package/src/tui/research-panel.ts +339 -435
  183. package/src/tui/research-tui-manager.ts +233 -0
  184. package/src/tui/tui-controller.ts +137 -0
  185. package/src/types/extension-context.ts +18 -49
  186. package/src/types/index.ts +61 -0
  187. package/src/types/llm.ts +68 -29
  188. package/src/types/research-panel-types.ts +61 -0
  189. package/src/utils/circuit-breaker.ts +146 -0
  190. package/src/utils/citation-utils.ts +97 -0
  191. package/src/utils/disk-space-checker.ts +55 -0
  192. package/src/utils/error-tracker.ts +265 -0
  193. package/src/utils/host-config.ts +45 -0
  194. package/src/utils/input-validation.ts +8 -10
  195. package/src/utils/json-utils.ts +150 -16
  196. package/src/utils/llm-usage.ts +54 -0
  197. package/src/utils/log-rotation.ts +125 -0
  198. package/src/utils/log-utils.ts +159 -0
  199. package/src/utils/make-resource-loader.ts +3 -1
  200. package/src/utils/metrics-summary.ts +459 -0
  201. package/src/utils/metrics.ts +302 -0
  202. package/src/utils/research-export.ts +28 -12
  203. package/src/utils/safe-unref.ts +31 -0
  204. package/src/utils/shared-links.ts +195 -90
  205. package/src/utils/shutdown-manager.ts +65 -0
  206. package/src/utils/stdio-capture.ts +407 -0
  207. package/src/utils/text-utils.ts +264 -7
  208. package/src/utils/tool-usage-tracker.ts +83 -16
  209. package/src/utils/url-utils.ts +255 -0
  210. package/src/utils/user-agent.ts +28 -0
  211. package/src/web-research/browser-search.ts +179 -0
  212. package/src/web-research/retry-utils.ts +173 -77
  213. package/src/web-research/scraper-types.ts +72 -0
  214. package/src/web-research/scraper-utils.ts +434 -0
  215. package/src/web-research/search.ts +78 -218
  216. package/src/web-research/types.ts +9 -24
  217. package/src/web-research/utils.ts +2 -143
  218. package/src/web-research/web-scraper.ts +477 -0
  219. package/src/youtube/index.ts +154 -0
  220. package/src/youtube/potoken.ts +259 -0
  221. package/src/youtube/transcript-client.ts +390 -0
  222. package/src/youtube/video-id.ts +118 -0
  223. package/config/searxng/default-settings.yml +0 -189
  224. package/config/searxng/limiter.toml +0 -34
  225. package/config/tooling/eslint.config.js +0 -73
  226. package/config/tooling/vitest.config.integration.ts +0 -17
  227. package/config/tooling/vitest.config.ts +0 -40
  228. package/config/tooling/vitest.config.unit.ts +0 -12
  229. package/docs/assets/README-banner.jpg +0 -0
  230. package/scripts/setup.js +0 -174
  231. package/src/infrastructure/searxng-lifecycle.ts +0 -577
  232. package/src/infrastructure/searxng-manager.ts +0 -1183
  233. package/src/infrastructure/state-manager.ts +0 -918
  234. package/src/orchestration/deep-research-reducer.ts +0 -129
  235. package/src/orchestration/id-utils.ts +0 -63
  236. package/src/security/nvd.ts +0 -532
  237. package/src/security/osv.ts +0 -487
  238. package/src/stackexchange/cache.ts +0 -111
  239. package/src/stackexchange/output/index.ts +0 -7
  240. package/src/stackexchange/output/json.ts +0 -21
  241. package/src/tui/searxng-status.ts +0 -80
  242. package/src/utils/inject-date.ts +0 -29
  243. package/src/utils/research-error.ts +0 -205
  244. package/src/utils/searxng-config.ts +0 -94
  245. package/src/utils/session-state.ts +0 -295
  246. package/src/web-research/dependencies.ts +0 -20
  247. package/src/web-research/scrapers.ts +0 -935
  248. package/tsconfig.json +0 -40
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Out-of-process WebGPU viability probe (child process entry point).
3
+ *
4
+ * WHY THIS EXISTS: onnxruntime-node bundles Dawn as its WebGPU execution
5
+ * provider. On a host whose only Vulkan adapter is software/paravirtual
6
+ * (llvmpipe/lavapipe, virtio/venus, SwiftShader, "Microsoft Basic Render
7
+ * Driver", ...), Dawn happily *loads* the pipeline but then SIGSEGVs the moment
8
+ * it dispatches the first compute shader. A SIGSEGV is a process-level signal,
9
+ * not a JavaScript exception — no try/catch, no Promise rejection handler, and
10
+ * none of the embedder's WebGPU->CPU fallback logic can ever observe it. The
11
+ * V8 isolate simply dies.
12
+ *
13
+ * The only reliable way to learn "will WebGPU compute crash on this machine?"
14
+ * is to actually run one tiny embedding in a DISPOSABLE child process and read
15
+ * its exit code. A native crash here kills only this child; the parent reads a
16
+ * non-zero exit / fatal signal and chooses CPU. This converts an uncatchable
17
+ * native crash into an ordinary, catchable signal.
18
+ *
19
+ * Contract — the parent judges viability by the STDOUT SENTINEL, not the exit
20
+ * code. This is deliberate: onnxruntime-node's native teardown can itself abort
21
+ * (SIGABRT, "Attempt to use DefaultLogger but none has been registered") when the
22
+ * process exits with a live ORT session — even after a perfectly successful
23
+ * compute. So:
24
+ * argv[2] = model id (e.g. onnx-community/granite-embedding-small-english-r2-ONNX)
25
+ * argv[3] = HuggingFace cache directory (shared with the parent)
26
+ * prints "PROBE_OK <dims>" to STDOUT (written synchronously) => compute worked
27
+ * no "PROBE_OK" on stdout (crash during compute, load failure, ...) => not viable
28
+ * A SIGSEGV during the compute dispatch (the software/paravirtual-GPU case) kills
29
+ * the child BEFORE the sentinel is written, so its absence is the signal.
30
+ *
31
+ * This file is plain .mjs (no TypeScript, no bundling). It is shipped verbatim
32
+ * next to the bundled entry points (dist/) and resolves @huggingface/transformers
33
+ * from the installed package's node_modules, exactly like the other entry points.
34
+ */
35
+
36
+ import process from 'node:process';
37
+ import { writeSync } from 'node:fs';
38
+
39
+ async function main() {
40
+ const model = process.argv[2];
41
+ const cacheDir = process.argv[3];
42
+ if (!model || !cacheDir) {
43
+ process.stderr.write('webgpu-probe: missing model/cacheDir args\n');
44
+ process.exit(2);
45
+ }
46
+
47
+ const { pipeline, env } = await import('@huggingface/transformers');
48
+ env.cacheDir = cacheDir;
49
+ // Keep the probe quiet — we only care about the exit code, not Dawn chatter.
50
+ try {
51
+ if (env.backends && env.backends.onnx) env.backends.onnx.logLevel = 'error';
52
+ } catch {
53
+ /* best-effort */
54
+ }
55
+
56
+ // Load on the WebGPU EP exactly as the real embedder would, then run ONE embed.
57
+ // The crash (if any) happens on this first compute dispatch, after load.
58
+ const pipe = await pipeline('feature-extraction', model, {
59
+ device: 'webgpu',
60
+ session_options: { intraOpNumThreads: 2, interOpNumThreads: 1 },
61
+ });
62
+
63
+ const out = await pipe('webgpu viability probe', { pooling: 'mean', normalize: true });
64
+
65
+ // Touch the result so the compute cannot be optimized away.
66
+ if (out && out.data && out.data.length > 0) {
67
+ // Write the sentinel SYNCHRONOUSLY to fd 1 so it is guaranteed on the pipe
68
+ // before process teardown — which may itself abort the ORT native runtime.
69
+ // The parent keys off this sentinel, so a teardown abort here is harmless.
70
+ writeSync(1, 'PROBE_OK ' + out.data.length + '\n');
71
+ process.exit(0);
72
+ }
73
+ process.stderr.write('webgpu-probe: empty embedding output\n');
74
+ process.exit(3);
75
+ }
76
+
77
+ main().catch((err) => {
78
+ process.stderr.write('webgpu-probe error: ' + (err && err.message ? err.message : String(err)) + '\n');
79
+ process.exit(1);
80
+ });
@@ -0,0 +1,102 @@
1
+ ## Agent Skill
2
+
3
+ pi-research ships as a portable [Agent Skill](https://agentskills.io/specification)
4
+ so any skills-compatible coding agent (Claude, OpenAI Codex CLI, OpenClaw, and
5
+ others using the same `SKILL.md` directory model) can run web research through the
6
+ pi-research software.
7
+
8
+ ![Installing the research skill into external agents](media/05-agent-skill.gif)
9
+
10
+ ### How it works
11
+
12
+ ```
13
+ agent
14
+ │ shells out (Bash / exec)
15
+ ▼
16
+ run.mjs — zero-dep launcher (skills/pi-research/scripts/)
17
+ │ locates the installed engine, or fails fast with guidance
18
+ ▼
19
+ pi-research engine — the SDK (dist/cli.mjs)
20
+ │ init → run → shutdown
21
+ ▼
22
+ cited Markdown report → stdout → back to the agent
23
+ ```
24
+
25
+ The agent matches the `description` in `SKILL.md` and shells out to the launcher.
26
+ `run.mjs` carries no dependencies; it locates the installed engine
27
+ (`PI_RESEARCH_BIN` pointing at the engine's `dist/cli.mjs`, then
28
+ `PI_RESEARCH_PATH` pointing at its package dir, then PATH / `node_modules` /
29
+ `~/.pi/bin`) and exits with an actionable
30
+ message — including config-file locations — if the package, a model, or an API key
31
+ is missing. It exposes four subcommands: `research "<query>"` (live research),
32
+ `knowledge "<query>"` (search past findings), `knowledge-config [set <mode>]`
33
+ (show/set the per-directory knowledge-store mode), and `status` (inspect detection/config).
34
+
35
+ ### Installation flow
36
+
37
+ The skill source lives at `skills/pi-research/` inside the package. Installing means
38
+ linking that directory into each agent's skills folder.
39
+
40
+ One-click (recommended). From the `pi` extension, run `/research-config` →
41
+ Install in External Agents. The installer:
42
+
43
+ 1. Detects which target agents are present under `$HOME` — currently Claude
44
+ (`~/.claude/skills`), OpenAI Codex CLI (`~/.codex/skills`), and OpenClaw
45
+ (`~/.openclaw/skills`, OpenClaw's managed skill root, which accepts symlinked
46
+ skill folders).
47
+ 2. Symlinks `skills/pi-research/` into each present agent, never overwriting an
48
+ unrelated skill already in that slot.
49
+ 3. Records what it created in a manifest, so Remove from External Agents
50
+ removes only its own links. Stale links are also garbage-collected on startup and
51
+ on `npm uninstall`.
52
+
53
+ **OpenClaw without `pi`**
54
+
55
+ If you run OpenClaw but not the `pi` extension, register the skill with OpenClaw's
56
+ own CLI after installing the engine — OpenClaw copies the `SKILL.md` folder into its
57
+ managed skill root and reads it on the next session:
58
+
59
+ ```bash
60
+ npm install -g @lincoln504/pi-research
61
+ openclaw skills install "$(npm root -g)/@lincoln504/pi-research/skills/pi-research" --global
62
+ ```
63
+
64
+ OpenClaw drives the skill through its `exec` tool, gated on `node` being on `PATH`
65
+ (`metadata.openclaw.requires.bins`). A bare `openclaw skills install git:…` is *not*
66
+ used here: OpenClaw expects `SKILL.md` at a repo root, whereas this package nests it
67
+ under `skills/pi-research/` — install the engine from npm and point OpenClaw at that
68
+ folder, as above.
69
+
70
+ Cursor is intentionally excluded from the one-click flow — it has no global skills
71
+ directory and reads only project-level `.cursor/skills/`. Link it per project:
72
+
73
+ ```bash
74
+ ln -s "$(npm root -g)/@lincoln504/pi-research/skills/pi-research" .cursor/skills/pi-research
75
+ ```
76
+
77
+ Manual. Symlink the directory into any agent's skills folder yourself:
78
+
79
+ | Agent | Personal | Project |
80
+ |-------|----------|---------|
81
+ | Claude | `~/.claude/skills/pi-research/` | `<project>/.claude/skills/pi-research/` |
82
+ | OpenAI Codex CLI | `~/.codex/skills/pi-research/` | `<project>/.codex/skills/pi-research/` |
83
+ | OpenClaw | `~/.openclaw/skills/pi-research/` | `<workspace>/skills/pi-research/` |
84
+ | Cursor | — (no global dir) | `<project>/.cursor/skills/pi-research/` |
85
+
86
+ ### Prerequisites
87
+
88
+ - Node.js >= 22.19.0
89
+ - `pi-research` installed where the launcher can find it, plus a model + API key.
90
+ See [Configuration](CONFIGURATION.md).
91
+
92
+ ```bash
93
+ npm install -g @lincoln504/pi-research
94
+ node "<skill_dir>/scripts/run.mjs" status # verify the engine is detected
95
+ ```
96
+
97
+ ![One-command health and readiness check](media/06-health-check.gif)
98
+
99
+ Once installed, ask the agent to research something — its skill system activates
100
+ pi-research automatically. The in-package readme (`skills/pi-research/README.md`) and
101
+ `skills/pi-research/references/configuration.md` carry the same detail for anyone
102
+ browsing the skill directly.
@@ -1,109 +1,335 @@
1
- # Architecture
1
+ ## Architecture
2
2
 
3
- ## Orchestration Modes
3
+ pi-research is a pi TUI extension for multi-agent web research. It runs inside the pi
4
+ process, registers its tools and commands, and manages its own browser worker pool,
5
+ service registry, and local knowledge store. One engine backs every front-end: besides
6
+ the pi extension it is exposed as a standalone CLI, a portable agent skill — the same
7
+ skill any skills-aware host runs, including OpenClaw — and a programmatic SDK
8
+ (`src/sdk.ts`).
4
9
 
5
- Two primary modes: **Quick Mode** and **Deep Mode**.
10
+ ```
11
+ pi CLI
12
+ └── pi-research extension (src/index.ts)
13
+ ├── Registered Tools research, health, research_knowledge_search (always registered; reports why when the store is disabled)
14
+ ├── Commands /research, /research-config, /knowledge-store
15
+ ├── Events input (mid-run steering), session_shutdown (cleanup)
16
+ └── Layers
17
+ ├── Orchestration quick/deep research coordination
18
+ ├── Agent Tools search, scrape, youtube_transcript, security_search, stackexchange, grep, read
19
+ ├── Infrastructure browser pool, knowledge store, state manager
20
+ └── Core service registry, scheduler, health checks
21
+ ```
6
22
 
7
- ### Quick Mode
8
- Routes research tasks to a single **Agent Researcher** session. No coordination or evaluation overhead — suitable for focused queries.
23
+ 1. A query enters through `runResearch` — the single internal entry point — with a depth.
24
+ Callers phrase the request in natural language: when the `research` tool is invoked
25
+ in-session, the calling agent picks the depth (1–3) from the user's wording and the
26
+ task's complexity, guided by the tool's usage prompt (`src/prompts/research-tool-usage.md`).
27
+ CLI and SDK callers pass the depth explicitly.
28
+ 2. Depth 0 takes the quick path; depth 1–3 takes the deep path (below). The TUI and agent
29
+ skill are restricted to levels 1–3.
30
+ 3. On the deep path the coordinator plans the research tracks and runs one initial
31
+ search burst, then hands each researcher a set of result URLs to start from.
32
+ 4. Researchers scrape and read those pages through the scrape tooling and return cited
33
+ reports. They consider only what they scraped this session.
34
+ 5. The evaluator reviews the round and either runs another round or synthesizes the
35
+ final report.
36
+ 6. The result is returned as a single cited Markdown report; the cited URLs and their
37
+ summaries are queued into the knowledge store for future runs.
9
38
 
10
- ### Deep Mode
11
- Uses a state machine to manage a three-status research lifecycle:
39
+ ### Orchestration
12
40
 
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.
41
+ `runResearch` (`IResearchOrchestration`, implemented in
42
+ `src/orchestration/research-orchestration-service.ts`) is the single internal entry
43
+ point. It dispatches on depth.
22
44
 
23
- State transitions are managed by a pure reducer (`src/orchestration/deep-research-reducer.ts`), ensuring predictable behavior.
45
+ Depth 0 — quick (`QuickResearchOrchestrator`): a single researcher runs directly with
46
+ all tools; there is no coordinator, no planning phase, and no rounds. Depth 0 is
47
+ reachable only through the SDK (`runQuickResearch`) or the CLI (`--depth 0`, which the
48
+ agent skill can pass). The pi extension's `research` tool has a minimum depth of 1, so
49
+ an in-session agent can never request quick mode.
24
50
 
25
- ---
51
+ Depth 1–3 — deep (`DeepResearchOrchestrator`): the run proceeds in **rounds**. A round
52
+ is one coordinate → research → evaluate cycle — the round's agenda is planned (by the
53
+ coordinator in round 1, by the evaluator thereafter), a batch of **researchers** runs it
54
+ in parallel, and the evaluator then decides whether to run another round or synthesize.
55
+ Two limits apply independently: how many researchers run *within* a round, and how many
56
+ rounds the run may take.
26
57
 
27
- ## The Agent Researcher
58
+ | Depth | Label | Researchers per round (max) | Rounds (max) |
59
+ |-------|--------|-----------------------------|--------------|
60
+ | 1 | normal | 2 | 2 |
61
+ | 2 | deep | 3 | 3 |
62
+ | 3 | ultra | 5 | 3 |
28
63
 
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:
64
+ These are ceilings, not targets: the coordinator and evaluator use as many researchers
65
+ and rounds as the topic needs. A depth-2 run, for example, may spawn up to 3 researchers
66
+ in each of up to 3 rounds. Queued steering messages (Alt+Enter) can unlock a few extra
67
+ rounds past the cap (`MAX_EXTRA_ROUNDS_WITH_STEERING`).
30
68
 
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.
69
+ The coordinator also runs the initial search burst and distributes its result URLs to
70
+ round 1's researchers (`distributeSearchResults`), so in deep mode the researchers
71
+ themselves do not call `search`.
35
72
 
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.
73
+ LLM-call conventions. Coordinator, evaluator, synthesis, JSON-repair, and
74
+ knowledge-extraction calls go through `completeSimple` (`src/core/llm/pi-ai-completion.ts`)
75
+ with `buildSafeOptions` (`src/core/llm/llm-utils.ts`); researcher sub-agents go through
76
+ `createAgentSession`.
77
+ Two conventions apply:
41
78
 
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).
79
+ - Thinking is off by default. These calls emit structured JSON or cited reports, so a
80
+ chain-of-thought block only spends output-token budget (and can truncate the answer).
81
+ `PI_RESEARCH_LLM_THINKING_LEVEL` (default `off`) controls it, clamped per provider.
82
+ - Output budgets are sized per role and clamped to the model's ceiling:
83
+ `PLANNING_MAX_TOKENS` for the plan/decision, `SYNTHESIS_MAX_TOKENS` for the final
84
+ report. A mid-round evaluation that cannot be parsed continues the existing agenda
85
+ rather than finalizing early, so a parse failure never truncates a run.
46
86
 
47
- ---
87
+ ### Research tools
48
88
 
49
- ## Component Details
89
+ Each researcher has a fixed tool set and a shared budget of 12 gathering calls per phase
90
+ (`MAX_GATHERING_CALLS`).
50
91
 
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`).
92
+ | Tool | Quick | Deep | Backend |
93
+ |------|-------|------|---------|
94
+ | `search` | ✓ | — | DuckDuckGo Lite via the stealth browser |
95
+ | `scrape` | ✓ | ✓ | Batch page fetch → Markdown via the stealth browser (up to 6 URLs per call) |
96
+ | `youtube_transcript` | ✓ | ✓ | YouTube captions via youtubei.js + BotGuard PoToken (≤3 videos, one call per researcher) |
97
+ | `security_search` | ✓ | ✓ | NVD, CISA KEV, GitHub Advisories, OSV |
98
+ | `stackexchange` | ✓ | ✓ | Stack Exchange network |
99
+ | `grep` | — | ✓ | Local ripgrep (from pi-coding-agent) |
100
+ | `read` | ✓ | ✓ | Local file reads (from pi-coding-agent) |
58
101
 
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.
102
+ In deep research `search` is excluded — the coordinator runs the search burst and hands
103
+ out URLs directly. In quick research `grep` is excluded — the single session is not
104
+ expected to traverse a local codebase. Researchers cannot write files, run shell
105
+ commands, or reach the network outside these tools.
61
106
 
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.
107
+ ### Browser infrastructure
66
108
 
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.
109
+ All browser work (search, scrape, health checks) goes through a poolifier
110
+ `FixedClusterPool` of worker processes — each a Node.js child process running its own
111
+ camoufox (stealth Firefox) instance. Isolating the browser in workers means a crash in
112
+ one worker cannot take down the orchestrator or other sessions.
69
113
 
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.
114
+ ```
115
+ BrowserTaskScheduler
116
+ └── FixedClusterPool (poolifier)
117
+ ├── Worker 1 → camoufox instance
118
+ ├── Worker 2 → camoufox instance
119
+ └── Worker N → camoufox instance
120
+ ```
77
121
 
78
- ### Output File Location
122
+ Key files:
123
+ - `src/infrastructure/browser/browser-task-scheduler.ts` — dispatches tasks to the pool
124
+ - `src/infrastructure/browser/thread-worker.ts` — worker entry point (bundled separately by esbuild)
125
+ - `src/infrastructure/browser/thread-worker-messaging.ts` — IPC protocol
126
+ - `src/infrastructure/browser/config.ts` — pool configuration, binary path detection
79
127
 
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:
128
+ ### Knowledge store and data handling
81
129
 
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.
130
+ The knowledge store is a local LanceDB vector table of past findings. It is optional
131
+ (research works without it) and is driven entirely by the orchestrator — researchers
132
+ never call it directly:
85
133
 
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.
134
+ - Before each researcher starts, the orchestrator searches the store for that
135
+ researcher's goal and injects any matching historical URLs (with summaries) into its
136
+ prompt as starting points.
137
+ - After a run, the cited URLs and their descriptions are enqueued into the async writer
138
+ queue and stored in the background — writes never block a run.
87
139
 
88
- ### TUI Progress Percentage
140
+ On ingest, each source's summary and full scraped Markdown are split into chunks and
141
+ embedded into vectors. A SHA-256 content hash of the page dedupes re-ingested URLs:
142
+ an unchanged page is skipped, a changed page replaces its old rows. Each row carries the
143
+ vector, the normalized URL, the text and full content, a timestamp, and scope flags
144
+ (project vs. global) that are filtered at query time.
89
145
 
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.
146
+ ```
147
+ WriterQueue (async, non-blocking)
148
+ └── KnowledgeStore
149
+ ├── Embedder (onnx-community/granite-embedding-small-english-r2-ONNX via @huggingface/transformers)
150
+ │ └── backend: auto (out-of-process WebGPU probe → webgpu or cpu) / webgpu / cpu
151
+ └── LanceDB (knowledge_db/ directory, Arrow-backed vector table)
152
+ ```
91
153
 
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.
154
+ Key files: `src/knowledge/store.ts` (LanceDB operations), `embedder.ts` (model load +
155
+ batched inference), `writer-queue.ts` (async writes + content-hash dedup), `chunker.ts`
156
+ (chunking), `webgpu-viability.ts` (out-of-process GPU probe + cached verdict),
157
+ `migration.ts` (model-change migration: drop / backup / re-embed).
98
158
 
99
- ---
159
+ The store needs native ONNX-runtime and LanceDB bindings. On platforms with no prebuilt
160
+ binary — notably Intel macOS (`darwin-x64`) — it is absent: the health check reports it
161
+ disabled-but-healthy and research runs without the cache. See
162
+ [KNOWLEDGE-STORE.md](KNOWLEDGE-STORE.md) for the full subsystem and platform matrix.
100
163
 
101
- ## Project Structure
164
+ ### Services and lifecycle
102
165
 
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.
166
+ Services are registered with async factory functions and resolved through a registry
167
+ (`getService()`), initialized lazily or eagerly with dependencies wired at init time.
168
+
169
+ ```typescript
170
+ registerService(ServiceNames.FOO, async () => {
171
+ const dep = await getService<IBar>(ServiceNames.BAR);
172
+ return new FooService(dep);
173
+ }, { lazyInitialization: true });
174
+
175
+ const foo = await getService<IFoo>(ServiceNames.FOO);
176
+ ```
177
+
178
+ Services that hold resources implement `dispose()`; the registry disposes them in
179
+ reverse dependency order. Resolving through the registry (rather than direct imports)
180
+ enforces lifecycle discipline (init → use → dispose) and lets tests swap in mocks.
181
+
182
+ - Core (`src/core/`): `PlanningService`, `SchedulerService`, `HealthCheckService`
183
+ - Infrastructure (`src/infrastructure/`): `StateManagerService`, `KnowledgeStoreService`, `MetricsService`, `WorkerPoolManager`, `FileLockService`, `GPUResourceService` (plus `WriterQueue`, defined in `src/knowledge/` and registered here)
184
+ - Orchestration (`src/orchestration/`): `ResearchOrchestrationService`, `ResearchSessionService`, `ResearchSynthesisService`
185
+
186
+ Cross-session, cross-process state (active sessions, browser status, metrics) lives in
187
+ `StateManagerService` (`src/infrastructure/state/`), which serializes concurrent writes
188
+ with file-based locking (`FileLockService`).
189
+
190
+ ### TUI
191
+
192
+ The live progress panel uses `@earendil-works/pi-tui`, which handles terminal state
193
+ (keyboard protocol, mouse tracking, bracketed paste). stdio capture (to keep stray
194
+ output from corrupting the panel and to guarantee a clean exit) lives in
195
+ `src/utils/stdio-capture.ts`.
196
+
197
+ ### Project structure
198
+
199
+ ```
200
+ src/
201
+ ├── index.ts extension entry point (tools, commands, events, lifecycle)
202
+ ├── cli.ts standalone CLI entry point
203
+ ├── sdk.ts programmatic SDK (non-extension use)
204
+ ├── config.ts env-var parsing, validation, singleton
205
+ ├── constants.ts team sizes, round caps, tool budgets, batch limits
206
+ ├── logger.ts structured logger (JSONL, TUI-safe)
207
+ ├── tool.ts re-export barrel for the research + health tool definitions
208
+ ├── research-config.ts /research-config TUI
209
+ ├── core/
210
+ │ ├── llm/ prompts, model resolution, agentic JSON repair, inject-date
211
+ │ ├── interfaces/ abstraction contracts (observer, planning, orchestration)
212
+ │ ├── planning-service.ts, scheduler-service.ts
213
+ │ ├── service-registry.ts, service-interfaces.ts, service-initialization.ts
214
+ │ └── planning-utils.ts
215
+ ├── infrastructure/
216
+ │ ├── browser/ worker pool, task scheduler, IPC, camoufox config
217
+ │ ├── state/ state manager, session tracking, metrics collector
218
+ │ ├── embedding/ local embedding server management
219
+ │ ├── knowledge-store-service.ts, metrics-service.ts, file-lock-service.ts
220
+ │ └── process-lifecycle-service.ts
221
+ ├── orchestration/
222
+ │ ├── deep-research-orchestrator.ts, quick-research-orchestrator.ts
223
+ │ ├── research-orchestration-service.ts, research-synthesis-service.ts
224
+ │ ├── research-session-service.ts, session-state.ts, session-context.ts
225
+ │ ├── researcher-executor.ts, researcher.ts, headless-observer.ts
226
+ ├── prompts/ Markdown prompt templates for all agents
227
+ ├── tools/ search, scrape, youtube_transcript, security, stackexchange, grep, read, knowledge-search
228
+ ├── knowledge/ embedder, store, writer queue, chunker, migration, webgpu probe
229
+ ├── web-research/ DuckDuckGo search, scraper, retry logic
230
+ ├── security/ NVD, CISA KEV, OSV, GitHub Advisory clients
231
+ ├── stackexchange/ Stack Exchange API client
232
+ ├── youtube/ YouTube transcript client (InnerTube + BotGuard PoToken)
233
+ ├── skill-install/ research-skill installer for coding-agent harnesses
234
+ ├── tui/ panels, layout, controller, wave animation, terminal utils
235
+ ├── healthcheck/ health-check registry and checks
236
+ ├── cleanup/ research-result cleanup
237
+ ├── observers/ research-observer implementation
238
+ ├── types/ shared index and TUI types
239
+ └── utils/ circuit breaker, text utils, shared-links, metrics, error tracking
240
+ ```
241
+
242
+ ### Key design decisions
243
+
244
+ Sandboxed researchers — researcher agents are limited to the tool set above. They cannot
245
+ write files, spawn processes, or make arbitrary network calls.
246
+
247
+ Worker pool over direct browser — browser processes are isolated in workers so a crash
248
+ in one cannot affect the orchestrator or other sessions.
249
+
250
+ Pinned browser stack — `playwright-core` and `impit` are pinned to exact versions and
251
+ `camoufox-js` to its `0.10.x` line (`^0.10.2`, which excludes the `0.11.x` alpha); the
252
+ three are coupled and upgraded together, because each floating range broke fresh consumer
253
+ installs that our lockfile masked. camoufox-js `0.10.2` fetches Firefox
254
+ 135/beta.24, the newest camoufox with binaries for every supported OS (later builds
255
+ dropped Windows). playwright-core `1.60.0` is the newest the FF135 Juggler protocol
256
+ accepts (1.61 rejects it and fails every launch). impit `0.13.0` avoids the
257
+ `only-allow pnpm` preinstall guard that impit 0.13.1/0.14.0 shipped, which fails
258
+ `npm install -g`; a direct-dependency pin is required because npm `overrides` do not
259
+ propagate to consumers. Will upgrade all three together when camoufox ships stable
260
+ cross-platform binaries. Rationale in full:
261
+ `src/infrastructure/browser/thread-worker-browser.ts`.
262
+
263
+ Pinned data stack — `apache-arrow` is a direct dependency at `21.1.0`, and `overrides`
264
+ forces the whole tree to that single version so LanceDB and Arrow share one Arrow instance
265
+ (mismatched Arrow copies do not interoperate — arrays built by one are rejected by the
266
+ other). This sits above `@lancedb/lancedb` 0.29's declared Arrow peer ceiling
267
+ (`>=15.0.0 <=18.1.0`); it is verified working, but the override should be re-validated
268
+ whenever `@lancedb/lancedb` is upgraded.
269
+
270
+ Pinned validation library — `typebox` is pinned to the exact version the pi host packages
271
+ depend on (`@earendil-works/pi-ai` / `@earendil-works/pi-coding-agent` pin `1.1.38`). Every
272
+ tool's parameter schema is built with TypeBox here and handed across the boundary to pi's
273
+ tool system, so the two must agree on `Value.Check`/`Convert` semantics. A floating `^1.1.38`
274
+ range let a fresh consumer install resolve pi-research to a newer TypeBox than pi's, shipping
275
+ an untested cross-version pairing; the exact pin keeps pi-research on the same version pi
276
+ validates with. Bump it in lockstep with the pi host, not independently. (`undici`, by
277
+ contrast, tracks the host's major — the host is on undici 8, and pi-research only uses the
278
+ stable `Agent` connector API, so it follows `^8`.)
279
+
280
+ Transient-failure resilience — every LLM call is a potential single point of failure on a
281
+ streaming endpoint that can drop mid-response (undici surfaces this as `terminated`). The
282
+ coordinator and evaluator calls retry fast transient transport failures (socket aborts, 5xx,
283
+ 429, provider overload) with bounded exponential backoff — mirroring the per-researcher retry
284
+ (`PI_RESEARCH_MAX_RETRIES`) — and, if still failing, degrade to a deterministic fallback plan
285
+ rather than aborting the run. An app-level LLM timeout is not retried (it already spent the
286
+ full budget); it degrades directly. Retry counts are internal constants, not configuration.
287
+
288
+ Registry over direct imports — services are registered and resolved through the registry
289
+ to support testing (mock replacement) and enforce init → use → dispose lifecycle.
290
+
291
+ Pure ESM — the codebase is ES Modules (`"type": "module"`). Worker bundles are built with
292
+ esbuild (`npm run build:worker`) before integration tests or publishing.
293
+
294
+ Enforced boundaries — `docs/deps.svg` is regenerated on every push (madge), and
295
+ architectural rules are enforced by dependency-cruiser
296
+ (`config/tooling/dependency-cruiser.cjs`).
297
+
298
+ ### Built with
299
+
300
+ Browser & scraping
301
+
302
+ - [Camoufox](https://camoufox.com) — stealth Firefox (driven via [Playwright](https://playwright.dev)) for undetected search and scraping
303
+ - [poolifier](https://github.com/poolifier/poolifier) — the worker-process pool behind the browser workers
304
+ - [html-to-markdown](https://github.com/Goldziher/html-to-markdown) & [node-html-markdown](https://github.com/crosstype/node-html-markdown) — convert scraped HTML to Markdown
305
+ - `pdf-oxide-wasm` — PDF text extraction (Rust/WASM)
306
+
307
+ Knowledge store & embeddings
308
+
309
+ - [Transformers.js](https://github.com/huggingface/transformers.js) — local embedding inference (model execution via ONNX Runtime)
310
+ - Google [Dawn](https://dawn.googlesource.com/dawn) — the WebGPU backend, accessed through the `webgpu` Node binding
311
+ - [LanceDB](https://lancedb.com) — on-disk vector database
312
+ - [Apache Arrow](https://arrow.apache.org) — the columnar schema the vector table is built on
313
+
314
+ YouTube transcripts
315
+
316
+ - [youtubei.js](https://github.com/LuanRT/YouTube.js) — YouTube internal-API client
317
+ - [BgUtils](https://github.com/LuanRT/BgUtils) — BotGuard PoToken generation
318
+ - [jsdom](https://github.com/jsdom/jsdom) — DOM environment for minting the PoToken
319
+
320
+ Host & runtime
321
+
322
+ - [pi](https://github.com/earendil-works/pi) — the host runtime, agent SDK, and TUI toolkit
323
+ - [TypeBox](https://github.com/sinclairzx81/typebox) — runtime config schema and validation
324
+
325
+ ### Development
326
+
327
+ ```bash
328
+ npm run test:unit # unit tests, no browser required
329
+ npm run test:integration # requires camoufox (Xvfb only for the opt-in virtual-display tests)
330
+ npm run type-check # TypeScript strict mode (src)
331
+ npm run type-check:tests # TypeScript strict mode (tests)
332
+ npm run lint # ESLint
333
+ npm run deps:check # architectural rule enforcement
334
+ npm run build:worker # bundle the browser worker (required before integration tests / publish)
335
+ ```