<!-- markdownlint-disable MD013 MD024 MD060 -->

# Architecture and Implementation Notes

## How it's built

- **Precompiled `dist/`** — TypeScript source compiles to `dist/index.js` via `tsc` (the `prepare` hook on dev installs / at publish time). **`dist/` is gitignored, not committed**; `prepare` always builds it — locally via tsc when typescript is present, otherwise by fetching the pinned compiler transiently via npx (`typescript@7.0.2`, same production-install strategy as pi-free). That fallback is what makes pi's git installs work: they run `npm install --omit=dev` (typescript absent), so without it `prepare` would silently skip and leave `main` pointing at a missing `dist/`, forcing pi to jiti/type-strip `index.ts` on every boot (issue #100). pi loads the compiled JS directly.
- **TUI rendering** — All 8 tools ship custom `renderCall` / `renderResult` components. Progress view shows per-item status, spinner, elapsed time, and download progress in real time. Result view shows expanded preview with format, browser/os profile, package path, chunk count, word count + heading outline, and error details. Phase + category badge for errors.
- **Phase-aware FetchError** — 26 failure codes × 10 fetch phases × 7 categories. `createFetchError()` produces frozen rich error objects. `classifyError()` maps Node errors. `buildUserFacingFetchErrorSummary()` produces agent-friendly messages. `suggestRetryTimeoutMs()` extrapolates from partial download progress.
- **Reading modes** — `src/outline.ts` powers the heading-outline / frugal-preview modes; `src/multi-answer.ts` powers the multi-source cited answer (BM25 over pooled chunks); `src/debug.ts` is the central `PI_WEBAIO_DEBUG=1` tracing helper (search / strategy / cache decisions, stderr-only so the MCP stdio channel stays clean).
- **Google CDP broker (default search path)** — the broker spawns a single persistent headless-Chrome tab (`bin/google-cdp-broker.mjs`) driven by CDP, shared warm across searches (reused until parent death via `--parent-stdin`). It is used unless `PI_WEBAIO_CDP_BROKER=0` opts out to the legacy extractor. Google ignores the deprecated `num` param and renders ~8–10 organic results per SERP page, so the broker paginates through `?start=10`, `?start=20`, … (the same URLs Google's "Next" links use), merging and URL-deduping pages until `max`, SERP exhaustion, or the lane budget. The lane carries a hard 3-second cap measured from when the search starts (in `src/tools/websearch.ts`, passed as `deadlineAt`/`timeoutMs`), so pagination's per-page 2s floor + 3.5s budget never exceeds it and never gates the tool's 7-second overall deadline. On broker failure with budget remaining (>1s of the search deadline left) the search falls through to legacy with a diagnostic envelope; a broker failure inside the final 1s of the deadline intentionally skips fallback (the legacy search could not complete within the deadline) and surfaces as `googleStatus: error` rather than dropping the whole search. A page-2+ pagination failure (network blip, blank tail page) degrades gracefully to the results already collected and is annotated in `googleStatus` (`ok (an extra SERP page failed…)`); a page-1 failure still propagates as a genuine total failure. The broker self-exits on parent death (`--parent-stdin` EOF); availability (`cdpAvailable`) accepts either the broker files or the legacy extractor files, since the fallback covers partial installs.
- **CI** — 4 GitHub Actions jobs: lint+typecheck (with `npm audit` and lockfile check), test (all ~68 suites, 1334 tests), prod-install-build (simulates the real `npm install --omit=dev` path), install-test (ubuntu/windows/macos — tarball verification + entry-point loading). Auto-release on version tag with CHANGELOG-driven GitHub Release notes.
- **Security** — 19 secret patterns (AWS, GitHub, GitLab, npm, PyPI, Slack, Stripe, Google, SendGrid, DigitalOcean, OpenAI including `sk-proj-`/`sk-svcacct-`, Anthropic, Supabase, Vercel, Cloudflare, private keys, passwords in URLs), plus output secret redaction (`src/redact.ts`). SSRF protection via DNS-pinning + fail-closed validation with a non-overridable cloud-metadata floor; blocks surface as a phase-aware `blocked_ssrf` error. Path-traversal guard in `utils.ts`. Prompt injection detection. Default GitHub CodeQL scanning.
