@lincoln504/pi-research 1.7.8 → 1.7.10

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 (37) hide show
  1. package/README.md +5 -1
  2. package/agent-skill/pi-research/README.md +3 -3
  3. package/agent-skill/pi-research/SKILL.md +1 -1
  4. package/dist/cli-bin.mjs +3 -0
  5. package/dist/cli.mjs +673 -383
  6. package/dist/pdf-extract-worker.mjs +1 -1
  7. package/dist/thread-worker.mjs +6 -6
  8. package/docs/AGENT-SKILL.md +6 -4
  9. package/docs/ARCHITECTURE.md +64 -50
  10. package/docs/CHANGELOG.md +41 -0
  11. package/docs/CONFIGURATION.md +2 -2
  12. package/docs/PI-EXTENSION.md +4 -0
  13. package/docs/SDK.md +10 -7
  14. package/docs/THIRD-PARTY-NOTICES.md +11 -7
  15. package/package.json +10 -8
  16. package/scripts/camoufox-layout.cjs +455 -0
  17. package/scripts/cleanup.cjs +72 -29
  18. package/scripts/setup.cjs +73 -111
  19. package/src/cli-bin.mjs +3 -0
  20. package/src/cli.ts +12 -13
  21. package/src/core/pi-version.ts +9 -1
  22. package/src/healthcheck/index.ts +1 -1
  23. package/src/infrastructure/browser/browser-cleanup.ts +1 -1
  24. package/src/infrastructure/browser/browser-error-utils.ts +5 -4
  25. package/src/infrastructure/browser/camoufox-layout.ts +450 -0
  26. package/src/infrastructure/browser/config.ts +46 -103
  27. package/src/infrastructure/browser/ensure-browser.ts +90 -52
  28. package/src/infrastructure/browser/thread-worker-browser.ts +38 -42
  29. package/src/infrastructure/browser/thread-worker.mjs +6 -6
  30. package/src/infrastructure/knowledge-store-service.ts +6 -6
  31. package/src/orchestration/researcher-executor.ts +47 -1
  32. package/src/tui/research-health.ts +1 -1
  33. package/src/utils/log-utils.ts +1 -1
  34. package/src/web-research/browser-search.ts +8 -10
  35. package/src/web-research/pdf-extract-worker.mjs +1 -1
  36. package/src/web-research/scraper-utils.ts +11 -2
  37. package/src/web-research/web-scraper.ts +1 -1
@@ -221,7 +221,7 @@ var AUTH_SCHEME_PATTERN = new RegExp(
221
221
  "gi"
222
222
  );
223
223
  var KNOWN_TOKEN_PATTERN = new RegExp(
224
- `${B_LEFT}(sk-[A-Za-z0-9_-]{16,}|gh[posru]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|AKIA[0-9A-Z]{16}|xox[baprs]-[A-Za-z0-9-]{10,})${B_RIGHT}`,
224
+ `${B_LEFT}(sk-[A-Za-z0-9_-]{16,}|gh[posru]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|npm_[A-Za-z0-9]{36}|hf_[A-Za-z0-9]{30,}|xox[baprs]-[A-Za-z0-9-]{10,})${B_RIGHT}`,
225
225
  "g"
226
226
  );
227
227
  var JWT_PATTERN = new RegExp(
@@ -107,7 +107,7 @@ var init_log_utils = __esm({
107
107
  "gi"
108
108
  );
109
109
  KNOWN_TOKEN_PATTERN = new RegExp(
110
- `${B_LEFT}(sk-[A-Za-z0-9_-]{16,}|gh[posru]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|AKIA[0-9A-Z]{16}|xox[baprs]-[A-Za-z0-9-]{10,})${B_RIGHT}`,
110
+ `${B_LEFT}(sk-[A-Za-z0-9_-]{16,}|gh[posru]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|npm_[A-Za-z0-9]{36}|hf_[A-Za-z0-9]{30,}|xox[baprs]-[A-Za-z0-9-]{10,})${B_RIGHT}`,
111
111
  "g"
112
112
  );
113
113
  JWT_PATTERN = new RegExp(
@@ -1761,9 +1761,9 @@ function formatErrorWithCause(err) {
1761
1761
  function isPrivateIp(ip) {
1762
1762
  const parts = ip.split(".").map(Number);
1763
1763
  if (parts.length !== 4 || parts.some((p) => isNaN(p))) return false;
1764
- const [a, b] = parts;
1764
+ const [a, b, c] = parts;
1765
1765
  if (a === void 0 || b === void 0) return false;
1766
- return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a >= 224;
1766
+ return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a === 192 && b === 0 && (c === 0 || c === 2) || a === 198 && b === 51 && c === 100 || a === 203 && b === 0 && c === 113 || a >= 224;
1767
1767
  }
1768
1768
  function isPrivateIpv6(ip) {
1769
1769
  const normalized = ip.toLowerCase().replace(/^\[|\]$/g, "");
@@ -3038,9 +3038,9 @@ async function initBrowser() {
3038
3038
  logToDebugFile3("INFO", `[Worker-${workerId3}] Initializing browser instance...`);
3039
3039
  let CamoufoxModule;
3040
3040
  try {
3041
- CamoufoxModule = await import("camoufox-js");
3041
+ CamoufoxModule = await import("@camoufox/camoufox");
3042
3042
  } catch (e) {
3043
- throw new Error(`[Worker] camoufox-js not found in node_modules. Please run 'npm install'. Original error: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
3043
+ throw new Error(`[Worker] @camoufox/camoufox not found in node_modules. Please run 'npm install'. Original error: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
3044
3044
  }
3045
3045
  const { Camoufox } = CamoufoxModule;
3046
3046
  const launchTimeoutMs = BROWSER_LAUNCH_TIMEOUT_MS;
@@ -3146,7 +3146,7 @@ async function initBrowser() {
3146
3146
  browser = null;
3147
3147
  const msg = e instanceof Error ? e.message : String(e);
3148
3148
  if (msg.includes("Camoufox is not installed") || msg.includes("Version information not found")) {
3149
- throw new Error(`[Worker] Browser binaries not found. Please run 'npx camoufox-js fetch' to install them.`, { cause: e });
3149
+ throw new Error(`[Worker] Browser binaries not found. Please run 'npx camoufox fetch' to install them.`, { cause: e });
3150
3150
  }
3151
3151
  const errName = e instanceof Error ? e.constructor?.name ?? "" : "";
3152
3152
  if (errName === "CannotFindXvfb" || errName === "CannotExecuteXvfb" || msg.includes("Xvfb") || msg.includes("virtual display")) {
@@ -21,10 +21,12 @@ npm install -g @lincoln504/pi-research # the engine (puts `pi-research` on PAT
21
21
  pi-research skill install # link the skill into every detected agent
22
22
  ```
23
23
 
24
- On npm ≥11.19 (and npm 12) dependency install scripts are skipped by default — and
25
- none are needed: better-sqlite3 13 ships prebuilt bindings in its own tarball, and the
26
- stealth browser self-provisions on its first use (the first scrape takes a few
27
- minutes). No approval is required (see the [README](../README.md#install)).
24
+ npm 12, the current npm, blocks dependency install scripts by default — and
25
+ none are needed: the only native piece on the launch path is `impit`, which ships
26
+ prebuilt binaries, and the stealth browser self-provisions on its first use (the first
27
+ scrape takes a few minutes). On npm 10 through 11.19 the `postinstall` fetch runs
28
+ during install instead; that is the npm bundled with Node 24/25 (11.19.0) and with
29
+ the Node 22.22.2 floor (10.9.7). No approval is required (see the [README](../README.md#install)).
28
30
 
29
31
  The global install also brings the `@earendil-works/pi-*` host packages in as peer
30
32
  dependencies (npm ≥7 resolves them). If your install skips peers
@@ -308,43 +308,53 @@ directory".
308
308
  Worker pool over direct browser — browser processes are isolated in workers so a crash
309
309
  in one cannot affect the orchestrator or other sessions.
310
310
 
311
- Pinned browser stack — `playwright-core` and `impit` are pinned to exact versions and
312
- `camoufox-js` is pinned to its `0.12.0` line; the three are coupled and upgraded together,
311
+ Pinned browser stack — the three browser packages are coupled and upgraded together,
313
312
  because each floating range broke fresh consumer installs that our lockfile masked.
314
- playwright-core stays at `1.60.0` (1.61+ rejects camoufox's Juggler and fails every
315
- launch — corroborated upstream: camoufox-js `0.12.0` declares
316
- `peerDependencies: { "playwright-core": "<1.61.0" }`, the same bound this pin holds by
317
- hand). `impit` is exact at `0.14.4` (refreshed from `0.13.0` on 2026-08-30 together with
318
- the camoufox bump) — exact because npm `overrides` do not propagate to consumers, so an
319
- exact pin is the only way to force a version downstream; impit's `only-allow pnpm`
320
- preinstall-guard incident (0.13.1/0.14.0, dropped in 0.14.1) is why floating ranges are
321
- not trusted here. Rationale in full: `src/infrastructure/browser/thread-worker-browser.ts`.
322
-
323
- The 0.10.x→0.12.0 camoufox bump had been held back through two refresh cycles by three
324
- blockers, all since resolved: camoufox restored Windows binaries in `v152.0.4-beta.26`
325
- (2026-07-16); impit's pnpm guard existed only in 0.13.1/0.14.0; and camoufox-js 0.12's
326
- better-sqlite3 13 upgrade, which initially looked like it demanded a C++ toolchain on
327
- every install. Measured on 13.0.3: `prebuilds/` for all eight platform/arch pairs ship
328
- INSIDE its tarball and load at runtime via node-gyp-build — no install script, nothing
329
- for a consumer to approve. What actually broke was tooling, not the binding: npm ≤11's
330
- injected `node-gyp rebuild` needlessly recompiles a binding.gyp with no install script
331
- (and its node-gyp 11.2 cannot detect the VS2026 CI runner image), which is why CI runs
332
- npm 12. Any future bump checks better-sqlite3 first, not camoufox.
333
-
334
- The browser BINARY, by contrast, is not pinned and cannot be. `camoufox-js fetch` takes
335
- no version argument: it walks the `daijro/camoufox` GitHub releases newest-first and takes
336
- the first non-prerelease release carrying an asset for this OS/arch. So the binary a
337
- consumer gets is whatever camoufox published most recently at install time, regardless of
338
- which camoufox-js version is installed — the npm pins do not freeze it, and a future
339
- camoufox release could break launches for fresh installs with no change on our side.
340
- Windows assets were in fact missing from `v146-hardware` through `v152.0.2-alpha` and
341
- returned in `v152.0.4-beta.26` (2026-07-16). Current newest is `v152.0.4-beta.28`
342
- (Firefox 152); it launches and drives cleanly under playwright-core `1.60.0`, verified
343
- directly, as does the older `v135.0.1-beta.24` an existing cache may still hold. The
344
- practical consequence is that browser freshness is independent of the npm pin: a stale
345
- `camoufox-js` does not mean a stale Firefox. Re-verify a real launch when bumping this
346
- stack — the unit and integration suites mock the browser and cannot catch a Juggler
347
- mismatch.
313
+ `@camoufox/camoufox` is pinned to its `0.5` line (`^0.5.7`), `playwright-core` is exact at
314
+ `1.62.1` and `impit` exact at `0.14.5`. The playwright pin follows the launcher's declared
315
+ peer: `@camoufox/camoufox` declares
316
+ `peerDependencies: { "playwright-core": "<1.63" }` (`1.60.0` was the old `camoufox-js`
317
+ bound, which capped `<1.61.0`). `impit` stays exact because npm `overrides` do not
318
+ propagate to consumers, so an exact pin is the only way to force a version downstream;
319
+ impit's `only-allow pnpm` preinstall-guard incident (0.13.1/0.14.0, dropped in 0.14.1) is
320
+ why floating ranges are not trusted here. Rationale in full:
321
+ `src/infrastructure/browser/thread-worker-browser.ts`.
322
+
323
+ The launcher replacement (2026-10-05). `camoufox-js` `0.12.0` was replaced by
324
+ `@camoufox/camoufox` `0.5.7` after the former broke every fresh install. On 2026-10-03
325
+ `v156.0.1-beta.34` was published to the `daijro/camoufox` releases WITHOUT the prerelease
326
+ flag, and the 156-series browser schema had dropped `navigator.product` (with
327
+ `appName`/`appCodeName`). `camoufox-js` `0.12.0` still sent those properties, and its
328
+ `fetch` takes no version argument — it walks the releases newest-first and takes the first
329
+ NON-PRERELEASE asset for this OS/arch. Every install after 2026-10-03 therefore fetched
330
+ the 156 build and died on launch with `UnknownProperty: Unknown property
331
+ navigator.product in config`, while machines holding an older cached browser kept working:
332
+ which is why CI (a fresh cache) went red while local runs stayed green. There was no pin to
333
+ turn — the library had no version argument and no environment variable for the browser.
334
+
335
+ `@camoufox/camoufox` fixes that structurally: every published copy is stamped with the one
336
+ browser build compiled from the same sources (`browser-pin.json`; 0.5.7 pins
337
+ `v156.0.1-beta.34`), and it fetches and launches exactly that build by default. The two
338
+ halves of the stack are released together, so a schema change cannot reach a launcher that
339
+ does not understand it. The browser BINARY is PINNED now, where the old stack left it
340
+ floating, and the practical consequence runs the other way: a stale `@camoufox/camoufox`
341
+ means a stale Firefox, and the browser only moves when this dependency does.
342
+
343
+ The extra install layer this adds: `@camoufox/camoufox` installs under
344
+ `<INSTALL_DIR>/browsers/<repo>/<version>/` (multi-version, with `config.json`,
345
+ `repo_cache.json` and a `.0.5_FLAG` above it) where `camoufox-js` laid the browser flat
346
+ with a root `version.json`, and it derives `INSTALL_DIR` only from the platform cache
347
+ directory — it reads neither `CAMOUFOX_INSTALL_DIR` nor `PLAYWRIGHT_BROWSERS_PATH`.
348
+ `src/infrastructure/browser/camoufox-layout.ts` (install/uninstall twin:
349
+ `scripts/camoufox-layout.cjs`, both driven by the same fixtures in
350
+ `test/unit/infrastructure/camoufox-layout.test.ts`) is the single resolver for that
351
+ location and for the override the launcher does read (`XDG_CACHE_HOME` on Linux,
352
+ `LOCALAPPDATA` on Windows); it also recognises a legacy flat install, which matters
353
+ because `camoufox fetch` runs `rm -rf INSTALL_DIR` over a directory it does not recognise.
354
+ `@camoufox/camoufox` has no `better-sqlite3` dependency (the native surface on the launch
355
+ path is now only `impit`), so the old npm-install-script blocker on this stack is gone.
356
+ Re-verify a real launch when bumping it — the unit and integration suites mock the browser
357
+ and cannot catch a Juggler mismatch.
348
358
 
349
359
  Pinned data stack — `apache-arrow` is a direct dependency at `21.1.0`, and `overrides`
350
360
  forces the whole tree to that single version so LanceDB and Arrow share one Arrow instance
@@ -370,20 +380,24 @@ through 0.39 declares the same
370
380
  `<=18.1.0` Arrow ceiling, so upgrading LanceDB does not resolve the override; it only
371
381
  changes which pairing needs re-validating.
372
382
 
373
- Pinned validation library — `typebox` (TypeBox 1.x, unscoped package name) is pinned to the
374
- exact version the pi host packages depend on (`@earendil-works/pi-ai` /
375
- `@earendil-works/pi-coding-agent` 1.0.0 both pin `1.3.27` — verified 2026-10-01 against 1.0.0
376
- (and against 0.99.2 before it), after an
377
- undocumented drift was caught in the 0.84/0.85 era and realigned; that drift was to `1.3.26`,
378
- and the version now pinned is `1.3.27`, both re-checked directly in the installed host
379
- packages' `package.json`). Every
380
- tool's parameter schema is built with TypeBox here and handed across the boundary to pi's
381
- tool system, so the two must agree on `Value.Check`/`Convert` semantics. A floating `^1.1.38`
382
- range let a fresh consumer install resolve pi-research to a newer TypeBox than pi's, shipping
383
- an untested cross-version pairing; the exact pin keeps pi-research on the same version pi
384
- validates with. Bump it in lockstep with the pi host, not independently. (`undici`, by
385
- contrast, tracks the host's major — the host is on undici 8, and pi-research only uses the
386
- stable `Agent` connector API, so it follows `^8`.)
383
+ Validation library — `typebox` (TypeBox 1.x, unscoped package name) is a PEER dependency
384
+ (`>=1.3.27 <2`) and an exact `1.3.27` devDependency. It is host-provided: pi's extension loader
385
+ warns on every startup when a host-provided package (`typebox`, `@sinclair/typebox`, the
386
+ `@earendil-works/pi-*` packages) sits in `dependencies`, because an installed copy can bypass the
387
+ loader and create a second live module instance (`resource-loader.js`,
388
+ `collectExtensionPackageWarnings`; pi `docs/packages.md`). Under pi the host's own copy is used,
389
+ so the two agree on `Value.Check`/`Convert` by construction. `@earendil-works/pi-ai` and
390
+ `@earendil-works/pi-coding-agent` 1.0.0 through 1.1.0 all pin `1.3.27` (re-checked 2026-10-08 in
391
+ the installed packages' `package.json`), which is why the devDependency, and so every test run and
392
+ the type-check, stays on exactly that version: bump it in lockstep with the pi host, not
393
+ independently. A standalone CLI/SDK install resolves the peer automatically on npm >= 7; the
394
+ `pi-research` bin preflights it with the pi packages and exits 78 with the remedy when a
395
+ peer-skipping install lacks it. The range is bounded below 2 on purpose (the pi packages use
396
+ `>=0.99.0 <2` for the same reason) rather than pi's suggested `"*"`, so a standalone install cannot
397
+ silently pick up a future major. Known trade-off: on a standalone install the peer resolves to the newest 1.x (1.3.35 on 2026-10-05, measured by installing the packed tarball) while the pi packages nest their own exact `1.3.27`, so two TypeBox copies coexist there; under pi only the host's copy exists. Every tool's parameter schema is built with TypeBox here and
398
+ handed across the boundary to pi's tool system. (`undici`, by contrast, tracks the host's major —
399
+ the host is on undici 8, and pi-research only uses the stable `Agent` connector API, so it follows
400
+ `^8`.)
387
401
 
388
402
  Pinned embedding runtime — `@huggingface/transformers` is exact at `4.3.0`. The hold at `4.2.0`
389
403
  was lifted on 2026-10-01 for a security reason, not a feature one: `4.2.0` declares
package/docs/CHANGELOG.md CHANGED
@@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.7.10] - 2026-10-08
9
+
10
+ ### Changed
11
+
12
+ - **The pi host dev dependencies move 1.0.4 → 1.1.0** (`@earendil-works/pi-ai`, `pi-coding-agent`, `pi-tui`; the peer range stays `>=0.99.0 <2`). The 1.0.4 → 1.1.0 diff is additive for this package (declaration diff of the installed packages, 2026-10-08): `pi-coding-agent` adds `durationMs` on the tool render context and `tool_execution_end`, `aborted` on `agent_settled` events, and the `+name`/`-name` tool modifiers; `pi-ai` adds the Decisions-API classifier surface and an optional `AuthProvider.agentName`; `pi-tui` adds the OSC 7501 program-status helpers and `Text.setPaddingX`. `docs/extensions.md` is unchanged and nothing was removed or re-signed, so the 0.99.0 floor is unchanged too. `PI_TESTED_MAX_VERSION` moves to 1.1.0 (`src/core/pi-version.ts`), so a pi 1.1.x host runs without the "newer than the tested ceiling" notice; `npm audit` stays at 0 and type-check (TypeScript 6 and 7), lint and the full unit suite (3305) pass against 1.1.0.
13
+ - **The npm install-script boundary is corrected across every user-facing surface and CI comment.** npm 10 through 11.19 run dependency install scripts (11.16–11.19 print a warning); npm 12, the current npm, blocks them by default — the npm bundled with Node 24/25 is 11.19.0, which still runs them, so a stock Node install fetches the browser during install. The old "11.19+ skips install scripts" wording in `README.md`, `docs/AGENT-SKILL.md`, `docs/SDK.md`, the ES/JA/ZH docs and the `ci.yml` comments was wrong — verified against the npm 11.19.0 source (arborist skips a script only when it is explicitly denied; `strict-allow-scripts` is opt-in) and consistent with this repo's own earlier measurement that 11.16–11.19 "warn but still run". The stale "compile from source on Windows" / `better-sqlite3` 13 references that survived the 1.7.9 launcher swap are gone with it.
14
+ - **Update guidance is documented** (README and `docs/PI-EXTENSION.md`): `pi update --extensions` moves every installed extension to its newest published version, `pi update npm:@lincoln504/pi-research` targets only this one, and a versioned source (`npm:@lincoln504/pi-research@1.7.9`) is pinned and never updates — remove and reinstall it to move versions.
15
+ - **`setup.cjs` announces the ~1.3 GB fetch before it starts**, so the postinstall fetch that runs inside `pi install` / `pi update --extensions` on script-running npm (10–11.19) no longer looks like a hang.
16
+ - In-range dev refresh: `vite` 8.3.2 → 8.3.4.
17
+
18
+ ### Fixed
19
+
20
+ - **The in-host lazy browser fetch no longer writes raw to the terminal.** `ensureBrowserInstalled()` spawned its `camoufox fetch` child with `stdio: 'inherit'`; a child with an inherited fd 1 bypasses pi's JS-level stdout redirection (pi's `takeOverStdout` rewrites `process.stdout.write` for the TUI) and interleaves the download's progress with the host's output stream — in print/JSON/RPC modes stdout is the protocol itself, so raw child output could corrupt it. The child's stdout is now captured in a temp file (its path is logged on failure, the file is removed on success) and stderr stays inherited, so real errors remain visible. Pinned by a test asserting `stdio: ['ignore', 'pipe', 'inherit']` on the fetch child.
21
+
22
+ ### Also shipped since 1.7.9
23
+
24
+ - **`source-map-js` 1.2.2** in the production tree clears GHSA-68fv-2mgg-jv7q from the audit gate (`npm audit`: 0).
25
+ - **The unit suite stops leaking throwaway tmpdirs.**
26
+ - **`docs/AUDIT-GATE.md` records** that a shipped host `npm-shrinkwrap.json` does not propagate a consumer-side `sharp` override (consumer advisory note).
27
+
28
+ ## [1.7.9] - 2026-10-05
29
+
30
+ ### Changed
31
+
32
+ - **The browser launcher moves from `camoufox-js` 0.12.0 to `@camoufox/camoufox` 0.5.7, and the browser build is now pinned.** This fixes the CI break that made every new PR red from 2026-10-03 and the fresh-install break behind it. `camoufox-js` took "the newest non-prerelease GitHub release", so when upstream flagged `v156.0.1-beta.34` non-prerelease (its neighbours `.33`/`.35` are prereleases) every fresh install got a browser whose schema dropped `navigator.product`, and every launch threw `UnknownProperty: navigator.product` (all three `integration-test` jobs; machines with an older cached browser, including the reporter's, never noticed). The new launcher ships `dist/data-files/browser-pin.json` naming the one build it was released and tested with and fetches exactly that, so a pi-research release moves the browser deliberately instead of on upstream's release timing. It is also **MIT** (camoufox-js was MPL-2.0), has **no `better-sqlite3`** (the native build that used to break toolchain-less installs), and adds multiversion support (`camoufox fetch <version>`, `set`, `list`, `remove`). `playwright-core` moves 1.60.0 → 1.62.1, inside the new package's `peerDependencies: "<1.63"`. **Verified by a real headless launch on 2026-10-05**: the pinned `156.0.1-beta.34` browser launched under `playwright-core` 1.62.1, loaded `https://example.com` and reported the expected `navigator` values (`product=Gecko`, `appName=Netscape`). The `ua-parser-js` 1.x override is kept (MIT, vs 2.x's AGPL-3.0-or-later) and was exercised by that same launch; see `docs/THIRD-PARTY-NOTICES.md`.
33
+ - **Install/update/uninstall no longer leaves dead browsers behind.** `@camoufox/camoufox` installs to `<cache>/camoufox/browsers/<repo>/<version>/` (the old layout was flat in that same directory) and never removes a superseded build, so a version bump used to leave a multi-GB browser nothing would ever launch. `src/infrastructure/browser/camoufox-layout.ts` (twinned with `scripts/camoufox-layout.cjs`, kept equivalent by `test/unit/infrastructure/camoufox-layout.test.ts`) is now the single source of truth for location and detection: `setup.cjs` detects the new layout rather than bare directory existence, stashes a legacy flat install aside and only removes it once the new build is in place (the upstream fetch runs `rm -rf` on a directory without its `.0.5_FLAG` BEFORE downloading, so a naive upgrade could destroy a working browser and leave none), prunes superseded versions on upgrade, and `cleanup.cjs` purges every installed build plus the legacy install and any crashed-upgrade stash under an ownership guard. A custom `CAMOUFOX_INSTALL_DIR`/`PLAYWRIGHT_BROWSERS_PATH` is translated into the variable the launcher actually reads (`XDG_CACHE_HOME`/`LOCALAPPDATA`) instead of being silently ignored, and on macOS, where the launcher cannot be relocated, the user is told rather than left with a setting that does nothing. `scripts/verify-package.cjs` gained `verifyLocalRequiresShipped`, which fails the publish gate if a shipped script requires an unshipped local file (the class of bug that would have shipped a `setup.cjs` unable to resolve `camoufox-layout.cjs`).
34
+ - **A provider's in-flight-budget 402 now waits instead of burning the retry budget.** OpenRouter refuses with `in_flight_budget_exhausted` and `Retry-After: 120` when an account's own requests are still open. That is transient and must stay retriable, but the researcher loop's exponential backoff is capped at `PI_RESEARCH_RETRY_DELAY_MS` (default 2000ms), so three attempts could never outlast the window: the run failed and the round degraded to a raw-compilation synthesis. The loop now recognizes the condition, honors the provider's `Retry-After` (clamped to 5-60s; 15s when absent) for the next attempt, and leaves the spent-balance class (`insufficient credits`) stopping immediately as before. Helpers and tests: `test/unit/orchestration/researcher-inflight-budget.test.ts`.
35
+ - **SSRF: three more reserved IPv4 ranges are blocked** — `198.18.0.0/15` (RFC 2544 benchmarking), `192.0.0.0/24` (RFC 6890 IETF protocol assignments) and the TEST-NET-1/2/3 documentation ranges `192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`. Literal-screen tests (including the public neighbours of each range, so the guard cannot silently over-block) are in `test/unit/web-research/private-ip-ranges.test.ts`.
36
+
37
+ - **`typebox` moves from `dependencies` to `peerDependencies` (`>=1.3.27 <2`), with an exact `1.3.27` devDependency.** pi 1.x warns on every startup that host-provided packages must be peer dependencies and that an installed copy "can bypass the extension loader and create duplicate runtime modules" (`collectExtensionPackageWarnings` in pi's `resource-loader.js`); `typebox` is on that list and was the one package left behind when the `@earendil-works/*` trio moved in 1.7.3. The range is bounded below 2 instead of pi's suggested `"*"`, matching the trio. Credit: the proposal in PR #20 (lukey03). The lockfile here is regenerated cleanly (PR #20's lockfile also added `"peer": true` to unrelated packages).
38
+ - **The `pi-research` bin shim also preflights `typebox`.** `dist/cli.mjs` imports it directly, so a peer-skipping install (`--legacy-peer-deps`, yarn classic, pnpm `auto-install-peers=false`) that lacks it would otherwise die with a raw `ERR_MODULE_NOT_FOUND`. It now exits 78 with the remedy, like the pi packages. New test: `also guards typebox` in `test/unit/cli-bin.test.ts`.
39
+ - **Dev dependency refresh to pi 1.0.3** (`@earendil-works/pi-ai`, `pi-coding-agent`, `pi-tui`, plus the new `pi-agent-core`, `pi-codemode`, `pi-mcp`, `chord` and `quickjs-wasi` they pull in). This clears the one `npm audit` finding: pi 1.0.0 shipped an `npm-shrinkwrap.json` pinning the vulnerable `brace-expansion` 5.0.9 (GHSA-q2hr-2g5m-vwhr, GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p); pi 1.0.1 removed the shrinkwrap and pins 5.0.12. The lockfile drops 134 shrinkwrap-nested packages. `npm audit`: 0 vulnerabilities. Type-checks, lint and the full unit suite pass against 1.0.3. The public surface this package uses (extension API, tool `annotations` and `namespace`) is unchanged in 1.0.3, and the tested-ceiling check compares major.minor only, so 1.0.x raises no host notice and `PI_TESTED_MAX_VERSION` stays `1.0.0`. pi 1.0.4 landed the same day and the dev dependency and lock moved to it in-range: its public surface is additive (the MCP tool-name matcher and the resource-tool constants), and type-check (TypeScript 6 and 7), lint, `npm audit` (0) and the full unit suite (3303) pass against it.
40
+
41
+ ### Fixed
42
+
43
+ - **A Camoufox fetch that fails after the browser itself is installed no longer throws away a working browser.** The launcher extracts the pinned build and writes its `.0.5_FLAG` BEFORE it fetches its companion geoip database from a separate GitHub release, so a failure in that second download exits non-zero over a complete, launchable install. `provisionBrowser` treated any non-zero exit as "no browser": it restored the legacy stash on top of the new build, and `setup.cjs` printed "Camoufox browser install failed — pi-research will not work" (exit 1 under `PI_RESEARCH_STRICT_SETUP`, which turned `integration-test (ubuntu-latest)` red on three consecutive CI runs on 2026-10-05 with `UND_ERR_SOCKET other side closed`, while the same job passed on macOS and Windows). It now keeps the installed build when the paired version is usable, discards the legacy stash, and logs the companion-download remedy. Verified that a real headless launch succeeds with the mmdb removed. New cases in `camoufox-layout.test.ts` (both twins) and `browser-lifecycle.test.ts` (the real `setup.cjs`, including the strict-mode exit 0 CI relies on).
44
+ - **Install docs no longer name `better-sqlite3` as a dependency.** The 2026-10-05 launcher move removed it from the tree and lockfile entirely, so the "no install scripts are needed because better-sqlite3 13 ships prebuilt bindings" explanation in `docs/AGENT-SKILL.md`, `docs/SDK.md`, the skill README and the three `DOCUMENTATION-*` translations was describing a package that is no longer installed. It now gives the real reason — the only native piece on the launch path is `impit`, which ships prebuilt binaries — and keeps the `npm approve-scripts better-sqlite3` + `npm rebuild` remedy as historical context for releases before the launcher swap.
45
+ - **Log redaction masks three more bare credential formats.** A Google API key (`AIza` + 35 characters), an npm automation token (`npm_` + 36) and a Hugging Face token (`hf_` + 30 or more) were masked only when they followed a `key=` style name; appearing alone in free text they were logged in clear. They join the known-token pattern in `src/utils/log-utils.ts`, with assertions in `test/unit/utils/log-redaction.test.ts`.
46
+ - **`docs/AUDIT-GATE.md` consumer table re-measured (2026-10-05).** Packed tarball installed into scratch projects: 4 HIGH (one `sharp` 0.33.5 chain via `@lancedb/lancedb`) on both the plain and the `--legacy-peer-deps` shape, 0 with a consumer `sharp` override. The earlier 7 / 4 / 1 / 0 table had two rows that no longer exist (`adm-zip`, and the host-shrinkwrap `brace-expansion`, fixed at the source by pi 1.0.1).
47
+ - In-range refresh: `jsdom` 30.1.1 → 30.1.2, `vite` 8.3.1 → 8.3.2, `sharp` 0.35.4 → 0.35.5 (the `^0.35.0` override; the first release clear of the libvips/libheif advisories) and `webgpu` 0.6.1 → 0.6.2 (the WebGPU adapter probe's Dawn binding; verified on a real GPU, `PROBE_OK 384`). `npm audit` stays at 0.
48
+
8
49
  ## [1.7.8] - 2026-10-01
9
50
 
10
51
  ### Fixed
@@ -169,8 +169,8 @@ Diagnostics & platform
169
169
  | `PI_RESEARCH_SKILL_DIR` | _(auto)_ | Override the bundled research-skill source directory used by the skill installer. |
170
170
  | `PI_RESEARCH_PURGE_BROWSERS` | _(unset)_ | Read by the bundled `scripts/cleanup.cjs`: set `1` to also delete the shared camoufox browser cache (kept by default because other installs may use it). Note npm ≥7 does not run `preuninstall`, so that script does not fire on `npm uninstall` — see [AGENT-SKILL.md](AGENT-SKILL.md). |
171
171
  | `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` | _(unset)_ | Set `1` during `npm install` to skip the camoufox browser download (fetched lazily on first use instead; standard Playwright convention). |
172
- | `CAMOUFOX_INSTALL_DIR` | _(user cache)_ | camoufox-js's own variable, and the only one that can relocate the browser. Sets where pi-research looks the binary up, and is exported to the postinstall fetch and to browser workers. Effective on the pinned camoufox-js 0.12.0+, which honours it again; on older pins (<0.12, which hardcoded their cache directory) it only relocated lookup — see ARCHITECTURE.md. |
173
- | `PLAYWRIGHT_BROWSERS_PATH` | _(user cache)_ | Alias for the above, accepted for compatibility and exported onward as `CAMOUFOX_INSTALL_DIR` — on camoufox-js 0.12.0+ it relocates the download itself, exactly like `CAMOUFOX_INSTALL_DIR`. |
172
+ | `CAMOUFOX_INSTALL_DIR` | _(user cache)_ | Relocates the browser download. Honoured by pi-research, not by the launcher: `@camoufox/camoufox` reads only the platform cache variable, so pi-research maps this value onto it — `XDG_CACHE_HOME` on Linux, `LOCALAPPDATA` on Windows — and exports that to the postinstall fetch and to browser workers, so install and lookup cannot drift. Ignored on macOS, where the launcher derives its cache from `$HOME`; pi-research prints a note there. See ARCHITECTURE.md. |
173
+ | `PLAYWRIGHT_BROWSERS_PATH` | _(user cache)_ | Alias for `CAMOUFOX_INSTALL_DIR`, accepted for compatibility. |
174
174
  | `XDG_CACHE_HOME` | `~/.cache` | Standard XDG variable. When set, every `~/.cache/pi-research/...` path below is rooted at `$XDG_CACHE_HOME/pi-research/...` instead. |
175
175
  | `PI_RESEARCH_BIN` (alias `PI_RESEARCH_PATH`) | _(auto)_ | Agent-skill launcher only: explicit path to the pi-research engine binary when auto-resolution (PATH → local install → npx) should be bypassed. See [agent-skill/pi-research/references/configuration.md](../agent-skill/pi-research/references/configuration.md). |
176
176
  | `PLAYWRIGHT_INSTALL_DEPS` | _(unset)_ | Linux only. Set `true` during `npm install` to also install system libraries via `npx playwright install-deps` (same as `npm run install:system-deps`). |
@@ -83,6 +83,10 @@ coding agents detected on this machine so they can run web research through the
83
83
  CLI, and remove it again — with exact, manifest-tracked cleanup. See
84
84
  [AGENT-SKILL.md](AGENT-SKILL.md) for the full installation flow.
85
85
 
86
+ ### Update
87
+
88
+ `pi update --extensions` moves every installed extension to its newest published version; `pi update npm:@lincoln504/pi-research` targets only this one. A versioned source (`npm:@lincoln504/pi-research@1.7.9`) is pinned and never updates — remove and reinstall it to move versions.
89
+
86
90
  ### Lifecycle
87
91
 
88
92
  - `activate` — registers commands, tools, the TUI controller, and initializes services.
package/docs/SDK.md CHANGED
@@ -21,13 +21,16 @@ classic — the SDK cannot import them, so add the one it loads directly:
21
21
  npm install @earendil-works/pi-coding-agent
22
22
  ```
23
23
 
24
- On npm ≥11.19 (and npm 12), dependency install scripts are skipped by default. Nothing
25
- here needs them: better-sqlite3 13 carries prebuilt bindings for every supported
26
- platform and loads them at runtime, and the stealth browser self-provisions on its
27
- first use. (The `npm approve-scripts better-sqlite3` + `npm rebuild` pair documented by
28
- earlier releases repaired better-sqlite3 12, which downloaded its binding from an
29
- install script — 13 ships the binding instead, and on npm 12.0.2 an approval does not
30
- make a skipped script run anyway.)
24
+ npm 12, the current npm, blocks dependency install scripts by default. Nothing
25
+ here needs them: the only native piece on the launch path is `impit`, which ships
26
+ prebuilt binaries, and the stealth browser self-provisions on its first use. On npm 10
27
+ through 11.19 the `postinstall` fetch runs during install instead; that is the npm
28
+ bundled with Node 24/25 (11.19.0) and with the Node 22.22.2 floor (10.9.7). (The
29
+ `npm approve-scripts better-sqlite3` + `npm rebuild` pair documented by earlier
30
+ releases repaired the old `camoufox-js` launcher, whose `better-sqlite3` binding came
31
+ from an install script — the 2026-10-05 move to `@camoufox/camoufox` removed that
32
+ dependency entirely, and on npm 12.0.2 an approval does not make a skipped script run
33
+ anyway.)
31
34
 
32
35
  Then pick the model: pass `model` to `initResearchSDK`, or set `PI_RESEARCH_MODEL`
33
36
  (env or `~/.pi/research/config.env`). The SDK never follows the model selected
@@ -12,12 +12,12 @@ first-party sources, so no third-party code is inlined).
12
12
 
13
13
  ### Notable dependency licensing
14
14
 
15
- camoufox-js / Camoufox browser
15
+ @camoufox/camoufox / Camoufox browser
16
16
 
17
- The `camoufox-js` npm package itself is licensed under **MPL-2.0** (file-level
18
- copyleft). It is resolved at install time and **not bundled** into this package's
19
- artifacts (the bundled entry points are built `--packages=external`), so no MPL-covered source is
20
- combined into or redistributed by this MIT package. It is used in-process only to
17
+ The `@camoufox/camoufox` npm package itself is licensed under **MIT**. It is the
18
+ launcher that replaced `camoufox-js` 0.12.0 (MPL-2.0) in the current development line.
19
+ It is resolved at install time and **not bundled** into this package's artifacts (the
20
+ bundled entry points are built `--packages=external`). It is used in-process only to
21
21
  configure and launch the Camoufox (Firefox-fork) browser **binary as a separate
22
22
  operating-system process**, which this package then drives over the Playwright/Juggler
23
23
  protocol. The Camoufox binary is downloaded and run separately and is subject to its
@@ -29,8 +29,12 @@ ua-parser-js
29
29
  package free of AGPL-licensed code in its dependency tree, `package.json`
30
30
  `overrides` pins `ua-parser-js` to the `1.x` line, which is MIT-licensed. The
31
31
  1.x and 2.x APIs are compatible for the only surface our direct dependency
32
- (`camoufox-js`) uses, and the pin is behavior-neutral for this package's usage
33
- (no custom `navigator.userAgent`/`fingerprint` is passed to Camoufox).
32
+ (`@camoufox/camoufox`, which declares `^2.0.2`) uses — it calls just
33
+ `new UAParser(ua).getBrowser().name` and `.getOS().name`, both present in 1.x and
34
+ verified against the pinned `1.0.41` — and the pin is behavior-neutral for this
35
+ package's usage (no custom `navigator.userAgent`/`fingerprint` is passed to Camoufox).
36
+ Re-check that surface on every `@camoufox/camoufox` bump: a 2.x-only call would make
37
+ this override a runtime break, not just a licensing choice.
34
38
 
35
39
  youtubei.js / bgutils-js / jsdom (YouTube transcripts)
36
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lincoln504/pi-research",
3
- "version": "1.7.8",
3
+ "version": "1.7.10",
4
4
  "description": "Web research for your agents with smart and safe tooling + knowledge store",
5
5
  "type": "module",
6
6
  "private": false,
@@ -35,6 +35,7 @@
35
35
  "docs/ARCHITECTURE.md",
36
36
  "scripts/setup.cjs",
37
37
  "scripts/cleanup.cjs",
38
+ "scripts/camoufox-layout.cjs",
38
39
  "scripts/build.cjs",
39
40
  "scripts/prepare.cjs",
40
41
  "LICENSE",
@@ -103,11 +104,13 @@
103
104
  "cleanup": "node scripts/cleanup.cjs",
104
105
  "setup": "node scripts/setup.cjs",
105
106
  "install:system-deps": "node scripts/setup.cjs --system-deps",
107
+ "//deps:check": "dependency-cruiser 18.5 refuses Node 25 (supports ^22 || ^24 || >=26) while engines allows >=22.22.2: run this script under Node 22 or 24 (see .nvmrc).",
106
108
  "deps:check": "depcruise --config config/tooling/dependency-cruiser.cjs --output-type err-long src",
107
109
  "deps:generate": "npx --yes madge@8.0.0 --image docs/deps.svg src/index.ts",
108
110
  "verify:changelog": "node scripts/verify-changelog.cjs",
109
111
  "analyze:dependencies": "ncu",
110
- "analyze:compatibility": "depngn"
112
+ "//analyze:compatibility": "depngn needs a node-version argument and a TTY (it calls stdout.clearLine): in CI or a pipe run it as: script -qec \"npx depngn 22.22.2\" /dev/null. Many entries report n/a (it cannot read engines for packages without the field).",
113
+ "analyze:compatibility": "depngn 22.22.2"
111
114
  },
112
115
  "pi": {
113
116
  "extensions": [
@@ -120,32 +123,30 @@
120
123
  "@lancedb/lancedb": "0.39.0",
121
124
  "apache-arrow": "21.1.0",
122
125
  "bgutils-js": "^4.0.3",
123
- "camoufox-js": "^0.12.0",
126
+ "@camoufox/camoufox": "^0.5.7",
124
127
  "esbuild": "^0.28.2",
125
128
  "impit": "0.14.5",
126
129
  "jsdom": "^30.1.0",
127
130
  "node-html-markdown": "^2.0.0",
128
131
  "pdf-oxide-wasm": "^0.3.77",
129
- "playwright-core": "1.60.0",
132
+ "playwright-core": "1.62.1",
130
133
  "poolifier": "^5.3.2",
131
- "typebox": "1.3.27",
132
134
  "undici": "^8.11.2",
133
135
  "youtubei.js": "^18.0.0"
134
136
  },
135
137
  "peerDependencies": {
136
138
  "@earendil-works/pi-ai": ">=0.99.0 <2",
137
139
  "@earendil-works/pi-coding-agent": ">=0.99.0 <2",
138
- "@earendil-works/pi-tui": ">=0.99.0 <2"
140
+ "@earendil-works/pi-tui": ">=0.99.0 <2",
141
+ "typebox": ">=1.3.27 <2"
139
142
  },
140
143
  "optionalDependencies": {
141
144
  "@huggingface/transformers": "4.3.0",
142
145
  "webgpu": "^0.6.0"
143
146
  },
144
147
  "overrides": {
145
- "lodash.isequal": "npm:fast-deep-equal@^3.1.3",
146
148
  "brace-expansion": "^5.0.12",
147
149
  "fast-uri": "^3.1.8",
148
- "ip-address": "^10.2.0",
149
150
  "apache-arrow": "21.1.0",
150
151
  "onnxruntime-web": "1.24.3",
151
152
  "node-domexception": "npm:@nolyfill/domexception@^1.0.28",
@@ -171,6 +172,7 @@
171
172
  "depngn": "^0.6.0",
172
173
  "eslint": "10.11.0",
173
174
  "npm-check-updates": "^23.1.0",
175
+ "typebox": "1.3.27",
174
176
  "typescript": ">=6.0.3 <6.1.0",
175
177
  "vite": "^8.3.1",
176
178
  "vitest": "^5.0.3"