@lincoln504/pi-research 1.0.6 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +250 -104
- package/dist/prompts/research-tool-usage.md +1 -0
- package/dist/thread-worker.mjs +9 -3
- package/docs/ARCHITECTURE.md +19 -4
- package/docs/CHANGELOG.md +129 -0
- package/docs/CONFIGURATION.md +2 -1
- package/docs/SDK.md +4 -1
- package/package.json +18 -16
- package/skills/pi-research/SKILL.md +1 -1
- package/src/cli.ts +62 -13
- package/src/config.ts +12 -4
- package/src/core/llm/model-registry-factory.ts +77 -25
- package/src/index.ts +11 -5
- package/src/infrastructure/browser/task-execution-service.ts +15 -3
- package/src/infrastructure/browser/thread-worker.mjs +9 -3
- package/src/orchestration/research-orchestration-service.ts +6 -6
- package/src/orchestration/research-synthesis-service.ts +31 -20
- package/src/orchestration/researcher-executor.ts +51 -13
- package/src/orchestration/researcher.ts +12 -1
- package/src/orchestration/session-state.ts +27 -4
- package/src/prompts/research-tool-usage.md +1 -0
- package/src/sdk.ts +58 -5
- package/src/utils/make-resource-loader.ts +13 -23
- package/src/web-research/browser-search.ts +17 -5
- package/src/youtube/index.ts +5 -4
|
@@ -85,6 +85,7 @@ The coordinator will plan as many researchers as needed (up to the max). You do
|
|
|
85
85
|
|
|
86
86
|
**Multiple Topics:**
|
|
87
87
|
- If you need to research multiple unrelated topics of **truly distinct scope**, you may emit multiple `research` calls **simultaneously in a single turn**. Each call gets its own isolated research run with its own coordinator and researchers.
|
|
88
|
+
- **Maximum 4 simultaneous calls** — Do NOT make more than 4 `research` calls in a single turn, even if you have more unrelated topics. Exceeding 4 simultaneous research runs can overload the system and degrade performance. If you have more than 4 truly distinct topics, they can likely be consolidated into fewer calls (group related subtopics, use broader queries, or batch across multiple turns).
|
|
88
89
|
- Example: `research("bananas")` and `research("quantum computing")` in the same turn — these have zero overlap.
|
|
89
90
|
- **Group related topics**: If topics share ANY scope or could inform each other, combine them into a single `research` call. The internal coordinator will decompose them into parallel researcher agents. Do NOT split related sub-topics into separate calls.
|
|
90
91
|
- Example: `research("Python async performance and Rust async performance comparison")` — NOT separate calls for Python and Rust, since the comparison IS the research goal.
|
package/dist/thread-worker.mjs
CHANGED
|
@@ -2219,6 +2219,8 @@ var ConfigSchema = Type.Object({
|
|
|
2219
2219
|
RESEARCHER_MAX_RETRIES: Type.Number({ minimum: 0, maximum: 5, default: 2 }),
|
|
2220
2220
|
/** Base delay between retries in milliseconds (default: 2000, range: 100-10000) */
|
|
2221
2221
|
RESEARCHER_MAX_RETRY_DELAY_MS: Type.Number({ minimum: 100, maximum: 1e4, default: 2e3 }),
|
|
2222
|
+
/** Unique researcher failures that abort the entire run (default: 2, range: 1-10) */
|
|
2223
|
+
MAX_FAILED_RESEARCHERS: Type.Number({ minimum: 1, maximum: 10, default: 2 }),
|
|
2222
2224
|
/** Target depth for recursive research (default: 1, range: 1-3) */
|
|
2223
2225
|
DEFAULT_RESEARCH_DEPTH: Type.Number({ minimum: 1, maximum: 3, default: 1 }),
|
|
2224
2226
|
/** Number of batches to allow for a single scrape tool call (default: 2, 0=unlimited) */
|
|
@@ -2276,9 +2278,13 @@ var ConfigSchema = Type.Object({
|
|
|
2276
2278
|
* never killed before it has used its full nav budget plus time waiting in the queue.
|
|
2277
2279
|
* (default: 10000) */
|
|
2278
2280
|
BROWSER_TASK_TIMEOUT_MS: Type.Number({ minimum: 2e3, maximum: 12e4, default: 1e4 }),
|
|
2279
|
-
/** Timeout for coordinator/evaluator/repair/knowledge LLM calls in ms (default: 300000 = 5 min, range: 60s-
|
|
2280
|
-
* Not exposed in TUI — controlled via PI_RESEARCH_LLM_TIMEOUT_MS env var.
|
|
2281
|
-
|
|
2281
|
+
/** Timeout for coordinator/evaluator/repair/knowledge LLM calls in ms (default: 300000 = 5 min, range: 60s-30min).
|
|
2282
|
+
* Not exposed in TUI — controlled via PI_RESEARCH_LLM_TIMEOUT_MS env var.
|
|
2283
|
+
* Ceiling raised to 30min (matching RESEARCHER_TIMEOUT_MS): a slow model on a
|
|
2284
|
+
* large-context synthesis/plan call can legitimately run >10min, and the old
|
|
2285
|
+
* 600000 ceiling silently clamped an explicit PI_RESEARCH_LLM_TIMEOUT_MS
|
|
2286
|
+
* (e.g. 900000), causing those calls to time out mid-generation. */
|
|
2287
|
+
LLM_TIMEOUT_MS: Type.Number({ minimum: 6e4, maximum: 18e5, default: 3e5 }),
|
|
2282
2288
|
/** Chain-of-thought "thinking" level for the engine's own LLM calls (coordinator,
|
|
2283
2289
|
* evaluator, synthesis, JSON-repair, knowledge extraction) AND the researcher
|
|
2284
2290
|
* sub-agents. Default 'off': these calls emit structured JSON / cited reports, not
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -84,10 +84,23 @@ Two conventions apply:
|
|
|
84
84
|
report. A mid-round evaluation that cannot be parsed continues the existing agenda
|
|
85
85
|
rather than finalizing early, so a parse failure never truncates a run.
|
|
86
86
|
|
|
87
|
-
###
|
|
87
|
+
### Tool inventory
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
This is the canonical list of every tool the system exposes, on both surfaces.
|
|
90
|
+
|
|
91
|
+
**Host-facing tools** — registered with the pi session (`src/index.ts`) for the calling
|
|
92
|
+
agent to invoke:
|
|
93
|
+
|
|
94
|
+
| Tool | Purpose |
|
|
95
|
+
|------|---------|
|
|
96
|
+
| `research` | Run a full multi-source research session and return the cited Markdown report |
|
|
97
|
+
| `research_knowledge_search` | Instant local search of the knowledge store — checked before live research; always registered, reports why when the store is disabled |
|
|
98
|
+
| `health` | Verify system status (browser pool, knowledge store, GPU lock); optional liveness probe |
|
|
99
|
+
|
|
100
|
+
**Researcher-agent tools** — the fixed set each researcher sub-agent works with
|
|
101
|
+
(`src/tools/index.ts`). `search`, `security_search`, `stackexchange`, and
|
|
102
|
+
`youtube_transcript` share a budget of 12 gathering calls per phase
|
|
103
|
+
(`MAX_GATHERING_CALLS`); `scrape` and local `grep` have their own budgets:
|
|
91
104
|
|
|
92
105
|
| Tool | Quick | Deep | Backend |
|
|
93
106
|
|------|-------|------|---------|
|
|
@@ -102,7 +115,9 @@ Each researcher has a fixed tool set and a shared budget of 12 gathering calls p
|
|
|
102
115
|
In deep research `search` is excluded — the coordinator runs the search burst and hands
|
|
103
116
|
out URLs directly. In quick research `grep` is excluded — the single session is not
|
|
104
117
|
expected to traverse a local codebase. Researchers cannot write files, run shell
|
|
105
|
-
commands, or reach the network outside these tools.
|
|
118
|
+
commands, or reach the network outside these tools. Individual researcher tools can be
|
|
119
|
+
disabled per run with `PI_RESEARCH_DISABLED_TOOLS`
|
|
120
|
+
(see [CONFIGURATION.md](CONFIGURATION.md)).
|
|
106
121
|
|
|
107
122
|
### Browser infrastructure
|
|
108
123
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.9] - 2026-07-28
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **pi ≥ 0.81.0 researcher crash** — every researcher session died with `pendingNativeProviderRegistrations is not iterable` under pi 0.81.0+ ("no source material was collected"). The hand-maintained `ExtensionRuntime` mock in the resource loader was frozen at a pre-0.81 contract; it is replaced with pi's own `createExtensionRuntime()`, which always satisfies the running host's contract and cannot drift again.
|
|
12
|
+
- **pi 0.80.8+ ModelRuntime migration** — `buildModelRegistry` migrated off the removed `AuthStorage`/`ModelRegistry.create()` APIs to `ModelRuntime.create()` + the synchronous `ModelRegistry` facade. The `@earendil-works/*` dependency floor and the in-host version self-check are both raised to 0.80.8 to match.
|
|
13
|
+
- **Explicit-API-key auth now reaches researcher LLM calls** — researcher sessions are created with the `modelRuntime` backing our own registry (pi 0.80.8 removed the old `modelRegistry` session option). Without this, standalone `PI_RESEARCH_API_KEY` users with no `~/.pi/agent/auth.json` passed pre-flight and then failed auth on every researcher call.
|
|
14
|
+
- **Explicit-API-key pre-flight hang** — seeding a runtime API key triggered a model-catalog refresh whose default options open live network connections with no timeout; in network-restricted environments the CLI pre-flight hung indefinitely. The refresh is now pinned offline (`allowNetwork: false`).
|
|
15
|
+
- **No filesystem side effects on read-only commands** — the migrated runtime's default file-backed credential store created `~/.pi/agent/auth.json` at construction, so every CLI invocation (including `status`) silently created a pi config dir on machines without pi and hard-failed where `$HOME` is read-only. An in-memory credential store is used when no `auth.json` exists; env-var provider keys are unaffected.
|
|
16
|
+
- **Search-timeout cascade under concurrent load** — the per-query hard cap in browser search started at enqueue with only the nav budget, so a query stuck in a saturated worker queue burned its whole budget waiting and aborted with zero work ("Query timed out … likely blocked or slow startup"). The cap now includes the queue-wait margin, matching the scheduler's own ceiling.
|
|
17
|
+
- **`PI_RESEARCH_LLM_TIMEOUT_MS` ceiling raised 10 → 30 min** — an explicit higher value was silently clamped to 600000, timing out large-context synthesis calls mid-generation.
|
|
18
|
+
- **CHANGELOG dates corrected** — release dates in this file wrongly said 2024; corrected to the actual git release dates (2026).
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
- **protobufjs override 7.6.4 → 7.6.5** — clears GHSA-j3f2-48v5-ccww (DoS) and its whole moderate advisory chain through `@google/genai`/pi-ai.
|
|
22
|
+
- **adm-zip forced to 0.6.0 via override** — clears GHSA-xcpc-8h2w-3j85 (crafted-ZIP 4 GB allocation) in the camoufox-js subtree; extraction APIs camoufox uses verified compatible.
|
|
23
|
+
- **Audit gate hardened with a reviewed exception list** — CI and the release publish gate now run `scripts/audit-gate.cjs`: any moderate+ advisory in the shipped tree fails the build unless explicitly allowlisted in `config/tooling/audit-exceptions.json` with an upstream-blocked justification, a clears-when condition, and a review date. Current exceptions: `brace-expansion` (frozen inside pi-coding-agent's npm-shrinkwrap; fix released upstream) and `sharp` (unused image pipeline of the embeddings-only transformers dependency).
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- **Dependencies**: pi ecosystem lockfile refreshed to 0.82.1 (matching the current pi host); `pdf-oxide-wasm` 0.3.77; `undici` ^8.9; devDependencies refreshed (eslint 10.8, typescript-eslint 8.65, vitest 4.1.10, dependency-cruiser 17.4.3). Deliberate pins unchanged (typebox 1.1.38, playwright-core 1.60.0, camoufox-js 0.10.x, lancedb 0.29, apache-arrow 21.1.0, transformers 4.2, impit 0.13).
|
|
27
|
+
- **Docs**: `PI_RESEARCH_LLM_TIMEOUT_MS` range updated in CONFIGURATION.md.
|
|
28
|
+
|
|
29
|
+
## [1.0.8] - 2026-07-17
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- **CHANGELOG** — Added comprehensive CHANGELOG.md documenting all versions from 0.1.13 through 1.0.7
|
|
33
|
+
- **System prompt constraint** — Added "Maximum 4 simultaneous calls" constraint to research-tool-usage prompt to prevent system overload
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- **Topic consolidation guidance** — Emphasized that topics can likely be consolidated to fit the 4-call maximum, with strategies for grouping related subtopics
|
|
37
|
+
- **Package structure** — CHANGELOG.md now included in published package (docs/CHANGELOG.md)
|
|
38
|
+
|
|
39
|
+
## [1.0.7] - 2026-07-14
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- **Browser-pool leader-election race** - Fixed a critical bug where a single client's transient socket error could force-clear shared leader state, causing a live, healthy leader to self-destruct via the leadership-miss watchdog. The fix reserves the "unreachable, force clear" path for cases where liveness probes actually confirm the registered leader PID or port is gone.
|
|
43
|
+
- **Researcher retry-exemption** - Fixed the "no initial search results or historical links" failure path that bypassed the retry loop entirely. This now routes through the same `RESEARCHER_MAX_RETRIES`/`RESEARCHER_MAX_RETRY_DELAY_MS` retry loop as other failures.
|
|
44
|
+
- **Exit-code misclassification** - Fixed false-positive classification of "Research stopped" errors as config errors (exit 78) via substring matching on the boilerplate "API key" advice text. Error classification now properly distinguishes infrastructure failures from credential problems.
|
|
45
|
+
- **Unsurfaced quality signals** - Added per-run error tracking and per-researcher grounding signals to the log output for better debugging of report quality variance.
|
|
46
|
+
- **SIGINT/SIGTERM cancellation wiring** - Improved cancellation handling for graceful shutdown on interrupt signals.
|
|
47
|
+
- **Multi-researcher citation-provenance folding** - Fixed citation provenance tracking when multiple researchers contribute to a report.
|
|
48
|
+
- **Untitled YouTube video citation line** - Fixed citation formatting for YouTube videos without titles.
|
|
49
|
+
|
|
50
|
+
### Security
|
|
51
|
+
- Several hardening improvements from the adversarial-review audit:
|
|
52
|
+
- Migration abort safety gates
|
|
53
|
+
- Hollow-run detection gates
|
|
54
|
+
- Streaming caps for large responses
|
|
55
|
+
- Socket drain on Content-Length pre-screen throws
|
|
56
|
+
- Observer isolation improvements
|
|
57
|
+
|
|
58
|
+
## [1.0.6] - 2026-07-04
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
- **Observer isolation** - Fixed `makeSafeObserver` proxying the observer directly, which could cause state leakage.
|
|
62
|
+
|
|
63
|
+
### Documentation
|
|
64
|
+
- Corrected depth-0 availability documentation
|
|
65
|
+
- Fixed migration.ts attribution in architecture docs
|
|
66
|
+
|
|
67
|
+
## [1.0.5] - 2026-07-04
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
- **Content-Length pre-screen throws** - Added proper body draining to prevent resource leaks
|
|
71
|
+
- **TTL refresh for transcribed links** - Fixed TTL handling for YouTube transcripts
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
- **YouTube transcript provenance** - Fixed transcript provenance tracking in final report citations
|
|
75
|
+
|
|
76
|
+
## [1.0.4] - 2026-07-04
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
- **Credential detection** - Detect pi keys by pi's own semantics, not file presence; registry no longer gated on auth.json
|
|
80
|
+
- **Configuration clarity** - Updated docs to reflect that pi has configuration files, not a named 'auth' feature
|
|
81
|
+
|
|
82
|
+
## [1.0.3] - 2026-07-04
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
- **Model resolution** - Require a configured model on the standalone CLI/skill; align SDK session model with RESEARCH_MODEL
|
|
86
|
+
- **Configuration requirement** - Added proper error messages when model is not configured
|
|
87
|
+
|
|
88
|
+
### Documentation
|
|
89
|
+
- Updated installation and configuration documentation
|
|
90
|
+
|
|
91
|
+
## [1.0.2] - 2026-07-04
|
|
92
|
+
|
|
93
|
+
### Fixed
|
|
94
|
+
- **Timeout robustness** - Improved timeout handling for long-running operations
|
|
95
|
+
- De-emphasized the markdown-conversion fallback in favor of native HTML-to-markdown conversion
|
|
96
|
+
|
|
97
|
+
## [1.0.1] - 2026-07-04
|
|
98
|
+
|
|
99
|
+
### Fixed
|
|
100
|
+
- Initial stable release after rebuild against current pi APIs
|
|
101
|
+
- Stealth-browser stack integration
|
|
102
|
+
|
|
103
|
+
## [0.1.13] - 2026-04-15
|
|
104
|
+
|
|
105
|
+
### Added
|
|
106
|
+
- Initial research functionality
|
|
107
|
+
- Knowledge store integration
|
|
108
|
+
- Multi-agent orchestration
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Deferred Findings (from v1.0.7 audit)
|
|
113
|
+
|
|
114
|
+
The following issues were identified during the v1.0.7 audit but intentionally deferred to future releases:
|
|
115
|
+
|
|
116
|
+
1. **KnowledgeStore.close() timeout behavior** - The close() method proceeds even when the pending-write wait times out. A correct fix requires threading cancellation tokens through write paths or documenting the 10s timeout as intended behavior.
|
|
117
|
+
|
|
118
|
+
2. **Cross-session teardown via shared singletons** - Session shutdown handlers run process-wide cleanup for reload/new/fork events. The correct behavior depends on pi's session model semantics and needs further investigation.
|
|
119
|
+
|
|
120
|
+
3. **Lower-severity hardening items** - Various low-confidence or low-impact findings noted for future investigation:
|
|
121
|
+
- SIGINT-adjacent cancellation race
|
|
122
|
+
- Knowledge-store migration vs. concurrent access
|
|
123
|
+
- Global activity gate reused for per-session decisions
|
|
124
|
+
- Zombie queued browser tasks after timeout
|
|
125
|
+
- Non-atomic `clearBrowserServer`
|
|
126
|
+
- Inconsistent `knowledge --json` payload shapes
|
|
127
|
+
- Backward-clock-step tolerance across multiple rate limiters
|
|
128
|
+
|
|
129
|
+
See `DEFERRED-FINDINGS-2026-07-14.md` for full details.
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -73,6 +73,7 @@ Research
|
|
|
73
73
|
| `PI_RESEARCH_MAX_CONCURRENT_SCRAPES` | `3` | 1–20 | Concurrent URLs fetched per scrape batch. |
|
|
74
74
|
| `PI_RESEARCH_MAX_RETRIES` | `2` | 0–5 | Retries per researcher request. |
|
|
75
75
|
| `PI_RESEARCH_RETRY_DELAY_MS` | `2000` | 100–10000 | Base delay between retries. |
|
|
76
|
+
| `PI_RESEARCH_MAX_FAILED_RESEARCHERS` | `2` | 1–10 | Unique researcher failures that abort the whole run. Raise to let slower, still-in-flight researchers finish before giving up. |
|
|
76
77
|
| `PI_RESEARCH_WORKER_THREADS` | `4` | 1–10 | Browser worker processes. Higher = more throughput, more CPU/RAM. |
|
|
77
78
|
| `PI_RESEARCH_WORKER_CONCURRENCY` | `2` | 1–10 | Tasks per worker process. |
|
|
78
79
|
| `PI_RESEARCH_MODEL` | _(pi: session model; CLI/skill: required)_ | — | The model research runs on. **Required for the standalone CLI / agent skill** — they use only this configured model (never the model selected inside the pi extension) and refuse to start without one (the CLI's per-run `--model` flag also satisfies this). On the SDK it selects the session model when no `model` option is given. In the pi extension it overrides researcher sub-agents and knowledge synthesis, while the coordinator and evaluator keep using the session model. Accepts `provider/id` or a bare model id. |
|
|
@@ -96,7 +97,7 @@ Timeouts
|
|
|
96
97
|
|
|
97
98
|
| Variable | Default | Range | Description |
|
|
98
99
|
|----------|---------|-------|-------------|
|
|
99
|
-
| `PI_RESEARCH_LLM_TIMEOUT_MS` | `300000` | 60000–
|
|
100
|
+
| `PI_RESEARCH_LLM_TIMEOUT_MS` | `300000` | 60000–1800000 | Coordinator / evaluator / repair / knowledge LLM call timeout. |
|
|
100
101
|
| `PI_RESEARCH_SCRAPE_TIMEOUT_MS` | `15000` | 5000–120000 | Per-page scrape (page-load) timeout. |
|
|
101
102
|
| `PI_RESEARCH_SEARCH_TIMEOUT_MS` | `45000` | 5000–120000 | Browser search page timeout. |
|
|
102
103
|
| `PI_RESEARCH_BROWSER_TASK_TIMEOUT_MS` | `10000` | 2000–120000 | Queue-wait / overhead margin added to each browser op's own timeout (a search task ceiling is `SEARCH_TIMEOUT_MS` + this; a scrape is `SCRAPE_TIMEOUT_MS` + this). |
|
package/docs/SDK.md
CHANGED
|
@@ -76,7 +76,10 @@ await shutdownResearchSDK();
|
|
|
76
76
|
`runResearchDetailed`, `searchKnowledge`, `scrapeUrl`, `getResearchHealth`,
|
|
77
77
|
`getLastRunStats`, and `getSessionMetrics`, plus `exportKnowledge` (write the
|
|
78
78
|
knowledge store to a web-consumable JSON file) and the post-run telemetry accessors
|
|
79
|
-
`getLastRunMetrics`, `getLastRunSummary`,
|
|
79
|
+
`getLastRunMetrics`, `getLastRunSummary`, `getLastErrorReport`, and
|
|
80
|
+
`getLastResearcherOutcome` (planned/launched/succeeded/failed researcher counts plus
|
|
81
|
+
per-researcher failure reasons for the most recent run — lets a caller tell a thin
|
|
82
|
+
report caused by a sparse topic apart from one where most researchers failed). Both
|
|
80
83
|
`@lincoln504/pi-research` and `@lincoln504/pi-research/sdk` export these symbols.
|
|
81
84
|
|
|
82
85
|
> Concurrency: a single initialized SDK instance runs one research call at a time.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lincoln504/pi-research",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "A set of web research tools (search, scrape, and others) and research agent system that uses them safely and efficiently.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"scripts/prepare.cjs",
|
|
38
38
|
"LICENSE",
|
|
39
39
|
"docs/THIRD-PARTY-NOTICES.md",
|
|
40
|
-
"README.md"
|
|
40
|
+
"README.md",
|
|
41
|
+
"docs/CHANGELOG.md"
|
|
41
42
|
],
|
|
42
43
|
"repository": {
|
|
43
44
|
"type": "git",
|
|
@@ -109,9 +110,9 @@
|
|
|
109
110
|
"image": "https://raw.githubusercontent.com/Lincoln504/pi-research/main/docs/media/hero.png"
|
|
110
111
|
},
|
|
111
112
|
"dependencies": {
|
|
112
|
-
"@earendil-works/pi-ai": ">=0.80.
|
|
113
|
-
"@earendil-works/pi-coding-agent": ">=0.80.
|
|
114
|
-
"@earendil-works/pi-tui": ">=0.80.
|
|
113
|
+
"@earendil-works/pi-ai": ">=0.80.8 <1",
|
|
114
|
+
"@earendil-works/pi-coding-agent": ">=0.80.8 <1",
|
|
115
|
+
"@earendil-works/pi-tui": ">=0.80.8 <1",
|
|
115
116
|
"@huggingface/transformers": "4.2.0",
|
|
116
117
|
"@kreuzberg/html-to-markdown-node": "^3.5.5",
|
|
117
118
|
"@lancedb/lancedb": "0.29.0",
|
|
@@ -122,11 +123,11 @@
|
|
|
122
123
|
"impit": "0.13.0",
|
|
123
124
|
"jsdom": "^29.1.1",
|
|
124
125
|
"node-html-markdown": "^2.0.0",
|
|
125
|
-
"pdf-oxide-wasm": "^0.3.
|
|
126
|
+
"pdf-oxide-wasm": "^0.3.75",
|
|
126
127
|
"playwright-core": "1.60.0",
|
|
127
128
|
"poolifier": "^5.3.2",
|
|
128
129
|
"typebox": "1.1.38",
|
|
129
|
-
"undici": "^8.
|
|
130
|
+
"undici": "^8.9.0",
|
|
130
131
|
"youtubei.js": "^17.2.0"
|
|
131
132
|
},
|
|
132
133
|
"optionalDependencies": {
|
|
@@ -139,20 +140,21 @@
|
|
|
139
140
|
"onnxruntime-web": "1.24.3",
|
|
140
141
|
"node-domexception": "npm:@nolyfill/domexception@^1.0.28",
|
|
141
142
|
"ua-parser-js": "^1.0.41",
|
|
142
|
-
"protobufjs": "7.6.
|
|
143
|
-
"vite": "^8.0.16"
|
|
143
|
+
"protobufjs": "7.6.5",
|
|
144
|
+
"vite": "^8.0.16",
|
|
145
|
+
"adm-zip": "^0.6.0"
|
|
144
146
|
},
|
|
145
147
|
"devDependencies": {
|
|
146
148
|
"@eslint/js": "^10.0.1",
|
|
147
149
|
"@types/jsdom": "^28.0.3",
|
|
148
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
149
|
-
"@typescript-eslint/parser": "^8.
|
|
150
|
-
"@vitest/coverage-v8": "^4.1.
|
|
151
|
-
"dependency-cruiser": "^17.4.
|
|
150
|
+
"@typescript-eslint/eslint-plugin": "^8.65.0",
|
|
151
|
+
"@typescript-eslint/parser": "^8.65.0",
|
|
152
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
153
|
+
"dependency-cruiser": "^17.4.3",
|
|
152
154
|
"depngn": "^0.6.0",
|
|
153
|
-
"eslint": "^10.
|
|
154
|
-
"npm-check-updates": "^22.2.
|
|
155
|
+
"eslint": "^10.8.0",
|
|
156
|
+
"npm-check-updates": "^22.2.9",
|
|
155
157
|
"typescript": ">=6.0.3 <6.1.0",
|
|
156
|
-
"vitest": "^4.1.
|
|
158
|
+
"vitest": "^4.1.10"
|
|
157
159
|
}
|
|
158
160
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: pi-research
|
|
3
3
|
description: "Multi-agent web research for any coding agent. Use for anything requiring current information from the internet — news, trends, product/service/company facts, statistics, people, places, events, APIs, library behavior, or 'what is X / how does X work' questions. Also use to search a local knowledge store of previously researched findings before doing live research. Powered by the pi-research engine (search, stealth scraping, security databases, Stack Exchange). Not for reading local files, running project commands, or analyzing this repository's own source."
|
|
4
4
|
license: MIT
|
|
5
|
-
metadata: { "author": "Lincoln Deen", "version": "1.0.
|
|
5
|
+
metadata: { "author": "Lincoln Deen", "version": "1.0.9", "package": "@lincoln504/pi-research", "engine": "pi-research-sdk", "openclaw": { "requires": { "bins": ["node"] } } }
|
|
6
6
|
allowed-tools: Bash(node:*)
|
|
7
7
|
---
|
|
8
8
|
|
package/src/cli.ts
CHANGED
|
@@ -31,9 +31,12 @@ import {
|
|
|
31
31
|
runDeepResearch,
|
|
32
32
|
searchKnowledge,
|
|
33
33
|
shutdownResearchSDK,
|
|
34
|
+
getLastErrorReport,
|
|
35
|
+
getLastResearcherOutcome,
|
|
34
36
|
type KnowledgeSearchResult,
|
|
35
37
|
} from './sdk.ts';
|
|
36
38
|
import type { HeadlessObserverOptions } from './orchestration/headless-observer.ts';
|
|
39
|
+
import { RESEARCH_STOPPED_ERROR_CODE } from './orchestration/session-state.ts';
|
|
37
40
|
import { validateAndSanitizeQuery } from './utils/input-validation.ts';
|
|
38
41
|
import { validateInitialLink, MAX_INITIAL_LINKS } from './utils/url-utils.ts';
|
|
39
42
|
import { exportResearchReport, appendExportMessage } from './utils/research-export.ts';
|
|
@@ -237,9 +240,9 @@ function fileExists(p: string): boolean {
|
|
|
237
240
|
* OPENAI_API_KEY. File presence alone is neither necessary (keys can live in
|
|
238
241
|
* models.json / env with no auth.json) nor sufficient (auth.json can be `{}`).
|
|
239
242
|
*/
|
|
240
|
-
function piKeysAvailable(piAuthPresent: boolean, provider?: string): boolean {
|
|
243
|
+
async function piKeysAvailable(piAuthPresent: boolean, provider?: string): Promise<boolean> {
|
|
241
244
|
try {
|
|
242
|
-
const available = safeGetAvailable(buildModelRegistry(undefined, undefined));
|
|
245
|
+
const available = safeGetAvailable(await buildModelRegistry(undefined, undefined));
|
|
243
246
|
if (!provider) return available.length > 0;
|
|
244
247
|
// Provider known (from the configured provider/model-id): keys must exist
|
|
245
248
|
// for THAT provider — a key for some other provider would pass pre-flight
|
|
@@ -279,7 +282,7 @@ export interface CredentialDetection {
|
|
|
279
282
|
* blocks a valid `--model` run). An explicit PI_RESEARCH_PROVIDER still wins:
|
|
280
283
|
* it names the provider the configured key belongs to.
|
|
281
284
|
*/
|
|
282
|
-
function detectCredentials(explicitModel?: string): CredentialDetection {
|
|
285
|
+
async function detectCredentials(explicitModel?: string): Promise<CredentialDetection> {
|
|
283
286
|
const paths = resolvedConfigPaths();
|
|
284
287
|
const piAuthPresent = fileExists(paths.piAuth);
|
|
285
288
|
const piModelsPresent = fileExists(paths.piModels);
|
|
@@ -296,7 +299,7 @@ function detectCredentials(explicitModel?: string): CredentialDetection {
|
|
|
296
299
|
providerEnv ?? (model && model.includes('/') ? model.slice(0, model.indexOf('/')) : undefined);
|
|
297
300
|
// Provider-aware when the configured model names one: a key for a DIFFERENT
|
|
298
301
|
// provider must not greenlight this run.
|
|
299
|
-
const piKeysUsable = piKeysAvailable(piAuthPresent, provider);
|
|
302
|
+
const piKeysUsable = await piKeysAvailable(piAuthPresent, provider);
|
|
300
303
|
|
|
301
304
|
// --- Explicit API-key path -------------------------------------------------
|
|
302
305
|
if (apiKey) {
|
|
@@ -437,7 +440,7 @@ interface ResearchArgs {
|
|
|
437
440
|
async function cmdResearch(args: ResearchArgs): Promise<number> {
|
|
438
441
|
// Pre-flight with the per-run --model flag folded in: it satisfies the
|
|
439
442
|
// model-required check and steers the provider-aware key detection.
|
|
440
|
-
const det = detectCredentials(args.model);
|
|
443
|
+
const det = await detectCredentials(args.model);
|
|
441
444
|
if (det.problem) {
|
|
442
445
|
if (args.json) toStdout(pretty({ ok: false, error: det.problem, exitCode: EXIT.CONFIG }));
|
|
443
446
|
else toStderr(`\nError: ${det.problem}\n\n${configBlock(det)}\n`);
|
|
@@ -468,6 +471,11 @@ async function cmdResearch(args: ResearchArgs): Promise<number> {
|
|
|
468
471
|
const observer: HeadlessObserverOptions = makeProgressObserver();
|
|
469
472
|
|
|
470
473
|
let exit: number = EXIT.OK;
|
|
474
|
+
// Let Ctrl-C/SIGTERM (main()'s onSignal) cancel THIS run's orchestrator instead of
|
|
475
|
+
// only tearing down shared SDK services out from under it — see
|
|
476
|
+
// activeResearchAbortController's declaration for why.
|
|
477
|
+
const abortController = new AbortController();
|
|
478
|
+
activeResearchAbortController = abortController;
|
|
471
479
|
try {
|
|
472
480
|
await initResearchSDK({
|
|
473
481
|
model: runModel,
|
|
@@ -485,7 +493,7 @@ async function cmdResearch(args: ResearchArgs): Promise<number> {
|
|
|
485
493
|
observer,
|
|
486
494
|
...(args.excludeTools ? { excludeTools: args.excludeTools } : {}),
|
|
487
495
|
...(args.initialLinks ? { initialLinks: args.initialLinks } : {}),
|
|
488
|
-
});
|
|
496
|
+
}, abortController.signal);
|
|
489
497
|
|
|
490
498
|
// Optionally persist the report to a file (opt-in via
|
|
491
499
|
// PI_RESEARCH_REPORT_EXPORT_ENABLED). The SDK is a pure library and does not
|
|
@@ -503,13 +511,29 @@ async function cmdResearch(args: ResearchArgs): Promise<number> {
|
|
|
503
511
|
}
|
|
504
512
|
|
|
505
513
|
if (args.json) {
|
|
506
|
-
|
|
514
|
+
// Surface the internal quality/health signals that were previously log-only
|
|
515
|
+
// (ISSUES-ANALYSIS-2026-07-09.md, Symptom 4): a caller reading only stdout/JSON
|
|
516
|
+
// had no way to distinguish "thin report, little material out there" from "thin
|
|
517
|
+
// report because most researchers died mid-run" without opening the log file.
|
|
518
|
+
const outcome = getLastResearcherOutcome();
|
|
519
|
+
const errorReport = getLastErrorReport();
|
|
520
|
+
toStdout(pretty({
|
|
521
|
+
ok: true,
|
|
522
|
+
depth,
|
|
523
|
+
report,
|
|
524
|
+
...(reportPath ? { reportPath } : {}),
|
|
525
|
+
...(outcome ? { researchers: { planned: outcome.planned, launched: outcome.launched, succeeded: outcome.succeeded, failed: outcome.failed } } : {}),
|
|
526
|
+
...(errorReport && errorReport.totalErrors > 0 ? {
|
|
527
|
+
trackedErrors: { total: errorReport.totalErrors, uniquePatterns: errorReport.uniquePatterns },
|
|
528
|
+
} : {}),
|
|
529
|
+
}));
|
|
507
530
|
} else {
|
|
508
531
|
toStdout(report.endsWith('\n') ? report : report + '\n');
|
|
509
532
|
}
|
|
510
533
|
} catch (err) {
|
|
511
|
-
exit = reportError(err, 'research', args.json);
|
|
534
|
+
exit = await reportError(err, 'research', args.json);
|
|
512
535
|
} finally {
|
|
536
|
+
if (activeResearchAbortController === abortController) activeResearchAbortController = null;
|
|
513
537
|
await safeShutdown();
|
|
514
538
|
}
|
|
515
539
|
return exit;
|
|
@@ -521,7 +545,7 @@ async function cmdResearch(args: ResearchArgs): Promise<number> {
|
|
|
521
545
|
* research.
|
|
522
546
|
*/
|
|
523
547
|
async function cmdKnowledge(queries: string[], json?: boolean): Promise<number> {
|
|
524
|
-
const det = detectCredentials();
|
|
548
|
+
const det = await detectCredentials();
|
|
525
549
|
if (det.problem) {
|
|
526
550
|
if (json) { toStdout(pretty({ ok: false, error: det.problem, exitCode: EXIT.CONFIG })); return EXIT.CONFIG; }
|
|
527
551
|
toStderr(`\nError: ${det.problem}\n\n${configBlock(det)}\n`);
|
|
@@ -571,7 +595,7 @@ async function cmdKnowledge(queries: string[], json?: boolean): Promise<number>
|
|
|
571
595
|
* run, without initializing the SDK. Machine-readable with --json.
|
|
572
596
|
*/
|
|
573
597
|
async function cmdStatus(json?: boolean): Promise<number> {
|
|
574
|
-
const det = detectCredentials();
|
|
598
|
+
const det = await detectCredentials();
|
|
575
599
|
const paths = resolvedConfigPaths();
|
|
576
600
|
const cfg = getConfig(process.cwd(), 'cli');
|
|
577
601
|
const summary = {
|
|
@@ -791,12 +815,23 @@ function describeUninstall(r: { status: string; path: string; message?: string }
|
|
|
791
815
|
// ---------------------------------------------------------------------------
|
|
792
816
|
|
|
793
817
|
/** Distinguish setup errors from runtime errors and print a clean, located message. */
|
|
794
|
-
function reportError(err: unknown, what: string, json?: boolean): number {
|
|
818
|
+
export async function reportError(err: unknown, what: string, json?: boolean): Promise<number> {
|
|
795
819
|
const msg = err instanceof Error ? err.message : String(err);
|
|
796
820
|
const lower = msg.toLowerCase();
|
|
797
821
|
|
|
822
|
+
// A tagged research-stop error (session-state.ts's createResearchStopError) is an
|
|
823
|
+
// orchestration/infrastructure failure (worker-pool contention, ungrounded
|
|
824
|
+
// researchers, etc.), never a credentials problem — classify it as SOFTWARE
|
|
825
|
+
// unconditionally and skip the substring heuristic below entirely. Without this,
|
|
826
|
+
// the boilerplate advice line every research-stop message carries ("Verify the
|
|
827
|
+
// model is available and the API key / context settings are valid.") contains the
|
|
828
|
+
// literal substring "api key," so the heuristic alone would misclassify EVERY
|
|
829
|
+
// research-stop — regardless of real cause — as a config error (exit 78).
|
|
830
|
+
const isTaggedResearchStop = (err as { code?: unknown } | undefined)?.code === RESEARCH_STOPPED_ERROR_CODE;
|
|
831
|
+
|
|
798
832
|
// Auth / model / config-shaped errors → CONFIG exit + config block.
|
|
799
833
|
const isConfigError =
|
|
834
|
+
!isTaggedResearchStop && (
|
|
800
835
|
lower.includes('no llm model available') ||
|
|
801
836
|
lower.includes('not found in pi') ||
|
|
802
837
|
lower.includes('no api key') ||
|
|
@@ -804,7 +839,7 @@ function reportError(err: unknown, what: string, json?: boolean): number {
|
|
|
804
839
|
lower.includes('invalid model string') ||
|
|
805
840
|
lower.includes('authentication') ||
|
|
806
841
|
lower.includes('unauthorized') ||
|
|
807
|
-
lower.includes('api key');
|
|
842
|
+
lower.includes('api key'));
|
|
808
843
|
|
|
809
844
|
// Rate limits win over config-shaped matches: a provider 429 often mentions
|
|
810
845
|
// "api key" in its body (e.g. "rate limit exceeded for this api key"), and
|
|
@@ -830,7 +865,7 @@ function reportError(err: unknown, what: string, json?: boolean): number {
|
|
|
830
865
|
return exitCode;
|
|
831
866
|
}
|
|
832
867
|
if (isConfigError) {
|
|
833
|
-
toStderr(`\nError: pi-research ${what} failed: ${msg}\n\n${configBlock(detectCredentials())}\n`);
|
|
868
|
+
toStderr(`\nError: pi-research ${what} failed: ${msg}\n\n${configBlock(await detectCredentials())}\n`);
|
|
834
869
|
return exitCode;
|
|
835
870
|
}
|
|
836
871
|
toStderr(`\nError: pi-research ${what} failed: ${msg}\n`);
|
|
@@ -1175,6 +1210,15 @@ const flushAndExit = (code: number): void => {
|
|
|
1175
1210
|
});
|
|
1176
1211
|
};
|
|
1177
1212
|
|
|
1213
|
+
// Signal to the in-flight `research` run's AbortController, if any is active.
|
|
1214
|
+
// Without this, Ctrl-C only tore down shared SDK services (browser pool, LLM
|
|
1215
|
+
// clients, LanceDB) via safeShutdown() while runDeepResearch() was still using
|
|
1216
|
+
// them — the orchestrator never saw a cancellation and had no chance to return
|
|
1217
|
+
// its already-wired partial/fallback synthesis for a clean stop. Set at the
|
|
1218
|
+
// start of cmdResearch, cleared in its finally so a stale reference never
|
|
1219
|
+
// outlives that one run.
|
|
1220
|
+
let activeResearchAbortController: AbortController | null = null;
|
|
1221
|
+
|
|
1178
1222
|
async function main(argv: string[]): Promise<number> {
|
|
1179
1223
|
// Register signal handlers so SIGINT/SIGTERM always trigger SDK shutdown
|
|
1180
1224
|
// before the process exits. safeShutdown() is idempotent — safe to call
|
|
@@ -1184,6 +1228,11 @@ async function main(argv: string[]): Promise<number> {
|
|
|
1184
1228
|
if (_signalCleanupDone) return;
|
|
1185
1229
|
_signalCleanupDone = true;
|
|
1186
1230
|
toStderr(`\n[pi-research] ${sig} — cleaning up…\n`);
|
|
1231
|
+
// Abort the in-flight research run FIRST (synchronously ahead of the shutdown
|
|
1232
|
+
// teardown below) so the orchestrator's existing signal-aware cancellation path
|
|
1233
|
+
// gets a chance to return a partial/fallback synthesis instead of racing a live
|
|
1234
|
+
// browser-pool/LLM teardown out from under it.
|
|
1235
|
+
activeResearchAbortController?.abort();
|
|
1187
1236
|
// Fire-and-forget: we can't await here, but safeShutdown swallows errors
|
|
1188
1237
|
// and the finally blocks in each command handler also call it. Drain via
|
|
1189
1238
|
// flushAndExit (not a bare process.exit) so buffered stdout isn't truncated.
|
package/src/config.ts
CHANGED
|
@@ -28,6 +28,8 @@ export const ConfigSchema = Type.Object({
|
|
|
28
28
|
RESEARCHER_MAX_RETRIES: Type.Number({ minimum: 0, maximum: 5, default: 2 }),
|
|
29
29
|
/** Base delay between retries in milliseconds (default: 2000, range: 100-10000) */
|
|
30
30
|
RESEARCHER_MAX_RETRY_DELAY_MS: Type.Number({ minimum: 100, maximum: 10000, default: 2000 }),
|
|
31
|
+
/** Unique researcher failures that abort the entire run (default: 2, range: 1-10) */
|
|
32
|
+
MAX_FAILED_RESEARCHERS: Type.Number({ minimum: 1, maximum: 10, default: 2 }),
|
|
31
33
|
/** Target depth for recursive research (default: 1, range: 1-3) */
|
|
32
34
|
DEFAULT_RESEARCH_DEPTH: Type.Number({ minimum: 1, maximum: 3, default: 1 }),
|
|
33
35
|
/** Number of batches to allow for a single scrape tool call (default: 2, 0=unlimited) */
|
|
@@ -85,9 +87,13 @@ export const ConfigSchema = Type.Object({
|
|
|
85
87
|
* never killed before it has used its full nav budget plus time waiting in the queue.
|
|
86
88
|
* (default: 10000) */
|
|
87
89
|
BROWSER_TASK_TIMEOUT_MS: Type.Number({ minimum: 2000, maximum: 120000, default: 10000 }),
|
|
88
|
-
/** Timeout for coordinator/evaluator/repair/knowledge LLM calls in ms (default: 300000 = 5 min, range: 60s-
|
|
89
|
-
* Not exposed in TUI — controlled via PI_RESEARCH_LLM_TIMEOUT_MS env var.
|
|
90
|
-
|
|
90
|
+
/** Timeout for coordinator/evaluator/repair/knowledge LLM calls in ms (default: 300000 = 5 min, range: 60s-30min).
|
|
91
|
+
* Not exposed in TUI — controlled via PI_RESEARCH_LLM_TIMEOUT_MS env var.
|
|
92
|
+
* Ceiling raised to 30min (matching RESEARCHER_TIMEOUT_MS): a slow model on a
|
|
93
|
+
* large-context synthesis/plan call can legitimately run >10min, and the old
|
|
94
|
+
* 600000 ceiling silently clamped an explicit PI_RESEARCH_LLM_TIMEOUT_MS
|
|
95
|
+
* (e.g. 900000), causing those calls to time out mid-generation. */
|
|
96
|
+
LLM_TIMEOUT_MS: Type.Number({ minimum: 60000, maximum: 1800000, default: 300000 }),
|
|
91
97
|
/** Chain-of-thought "thinking" level for the engine's own LLM calls (coordinator,
|
|
92
98
|
* evaluator, synthesis, JSON-repair, knowledge extraction) AND the researcher
|
|
93
99
|
* sub-agents. Default 'off': these calls emit structured JSON / cited reports, not
|
|
@@ -690,6 +696,7 @@ export function saveConfig(config: Config, scope: 'local' | 'user' = 'local', cw
|
|
|
690
696
|
PI_RESEARCH_MAX_RESEARCHERS: String(config.MAX_CONCURRENT_RESEARCHERS),
|
|
691
697
|
PI_RESEARCH_MAX_RETRIES: String(config.RESEARCHER_MAX_RETRIES),
|
|
692
698
|
PI_RESEARCH_RETRY_DELAY_MS: String(config.RESEARCHER_MAX_RETRY_DELAY_MS),
|
|
699
|
+
PI_RESEARCH_MAX_FAILED_RESEARCHERS: String(config.MAX_FAILED_RESEARCHERS ?? DEFAULTS.MAX_FAILED_RESEARCHERS),
|
|
693
700
|
PI_RESEARCH_HEALTH_CHECK_TIMEOUT_MS: String(config.HEALTH_CHECK_TIMEOUT_MS ?? DEFAULTS.HEALTH_CHECK_TIMEOUT_MS),
|
|
694
701
|
PI_RESEARCH_SEARCH_TIMEOUT_MS: String(config.SEARCH_TIMEOUT_MS),
|
|
695
702
|
PI_RESEARCH_TUI_REFRESH_DEBOUNCE_MS: String(config.TUI_REFRESH_DEBOUNCE_MS),
|
|
@@ -912,6 +919,7 @@ export function createConfig(env: Record<string, string | undefined>, processEnv
|
|
|
912
919
|
MAX_CONCURRENT_RESEARCHERS: parseEnvNumber(e, 'PI_RESEARCH_MAX_RESEARCHERS', DEFAULTS.MAX_CONCURRENT_RESEARCHERS, 1, 5, true),
|
|
913
920
|
RESEARCHER_MAX_RETRIES: parseEnvNumber(e, 'PI_RESEARCH_MAX_RETRIES', DEFAULTS.RESEARCHER_MAX_RETRIES, 0, 5, true),
|
|
914
921
|
RESEARCHER_MAX_RETRY_DELAY_MS: parseEnvNumber(e, 'PI_RESEARCH_RETRY_DELAY_MS', DEFAULTS.RESEARCHER_MAX_RETRY_DELAY_MS, 100, 10000),
|
|
922
|
+
MAX_FAILED_RESEARCHERS: parseEnvNumber(e, 'PI_RESEARCH_MAX_FAILED_RESEARCHERS', DEFAULTS.MAX_FAILED_RESEARCHERS, 1, 10, true),
|
|
915
923
|
DEFAULT_RESEARCH_DEPTH: parseEnvNumber(e, 'PI_RESEARCH_DEFAULT_RESEARCH_DEPTH', DEFAULTS.DEFAULT_RESEARCH_DEPTH, 1, 3, true),
|
|
916
924
|
MAX_SCRAPE_BATCHES: parseEnvNumber(e, 'PI_RESEARCH_MAX_SCRAPE_BATCHES', DEFAULTS.MAX_SCRAPE_BATCHES, 0, 99, true),
|
|
917
925
|
MAX_GATHERING_CALLS: parseEnvNumber(e, 'PI_RESEARCH_MAX_GATHERING_CALLS', DEFAULTS.MAX_GATHERING_CALLS, 1, 100, true),
|
|
@@ -941,7 +949,7 @@ export function createConfig(env: Record<string, string | undefined>, processEnv
|
|
|
941
949
|
SEARCH_TIMEOUT_MS: parseEnvNumber(e, 'PI_RESEARCH_SEARCH_TIMEOUT_MS', DEFAULTS.SEARCH_TIMEOUT_MS, 5000, 120000),
|
|
942
950
|
TUI_REFRESH_DEBOUNCE_MS: parseEnvNumber(e, 'PI_RESEARCH_TUI_REFRESH_DEBOUNCE_MS', DEFAULTS.TUI_REFRESH_DEBOUNCE_MS, 0, 1000),
|
|
943
951
|
BROWSER_TASK_TIMEOUT_MS: parseEnvNumber(e, 'PI_RESEARCH_BROWSER_TASK_TIMEOUT_MS', DEFAULTS.BROWSER_TASK_TIMEOUT_MS, 2000, 120000),
|
|
944
|
-
LLM_TIMEOUT_MS: parseEnvNumber(e, 'PI_RESEARCH_LLM_TIMEOUT_MS', DEFAULTS.LLM_TIMEOUT_MS, 60000,
|
|
952
|
+
LLM_TIMEOUT_MS: parseEnvNumber(e, 'PI_RESEARCH_LLM_TIMEOUT_MS', DEFAULTS.LLM_TIMEOUT_MS, 60000, 1800000),
|
|
945
953
|
LLM_THINKING_LEVEL: parseEnvEnum(e, 'PI_RESEARCH_LLM_THINKING_LEVEL', ['off', 'minimal', 'low', 'medium', 'high'] as const, DEFAULTS.LLM_THINKING_LEVEL),
|
|
946
954
|
PLANNING_MAX_TOKENS: parseEnvNumber(e, 'PI_RESEARCH_PLANNING_MAX_TOKENS', DEFAULTS.PLANNING_MAX_TOKENS, 1024, 131072),
|
|
947
955
|
SYNTHESIS_MAX_TOKENS: parseEnvNumber(e, 'PI_RESEARCH_SYNTHESIS_MAX_TOKENS', DEFAULTS.SYNTHESIS_MAX_TOKENS, 1024, 131072),
|