# v0.17.0 — PDF agent (LiteParse v2 in offscreen document)

Pie can now read PDFs you have open in Chrome's built-in viewer — remote (`https://…/paper.pdf`) and local (`file://…/paper.pdf`). The sidepanel agent gets three new read-class tools: `read_pdf`, `search_pdf`, `get_pdf_outline`. Parsing happens in an MV3 offscreen document hosting LiteParse v2 (PDFium WASM, Apache-2.0, ~4 MB), so the heavy WASM stays out of the service-worker hot path and out of every web tab.

## The three tools

- **`get_pdf_outline()`** — title + total pages + table of contents. Recommended first call for unfamiliar PDFs.
- **`read_pdf(page_range?, max_chars?)`** — fetch specific pages. `page_range` accepts `"1"`, `"1-3"`, `"1,3,5"`, `"1-3,7"`. Default 8000-char budget, cut at page boundaries.
- **`search_pdf(query, max_results?)`** — case-insensitive substring search across the whole PDF, returns snippets with surrounding context per matching page.

The system prompt advises the LLM to start with `get_pdf_outline` on unfamiliar PDFs and use `search_pdf` to locate a term before pulling full pages. `read_page` on a PDF tab now returns a `pdf_tab:` error pointing at the PDF tools, so the LLM self-corrects on the next turn.

## Local PDF flow

Local PDFs (`file://…/x.pdf`) need Chrome's per-extension **Allow access to file URLs** toggle. When you open a `file://*.pdf` tab the sidepanel surfaces a guided card that one-clicks you to the extension settings page; the card auto-dismisses once you flip the toggle and return.

The pin-gate was relaxed for `file://*.pdf` only (other `file://` URLs are still refused). Chrome's PDF viewer is sealed and can't navigate, so the URL itself is a safe pin identity.

## Lifecycle and cache

- Offscreen document is created lazily on the first PDF tool call and lives for the SW's lifetime.
- Parse results are cached in-memory in the offscreen, keyed by `tab.url`. SW idle → offscreen evicted → re-parse next call. Edit a PDF in place mid-session and the cache still serves the old text; close the tab or restart Chrome to bust it.
- WASM is fetched once via `chrome.runtime.getURL("liteparse.wasm")` and reused across PDFs.

## Manifest changes

- `permissions`: adds `"offscreen"`
- `content_security_policy.extension_pages`: gains `'wasm-unsafe-eval'` (scoped to extension pages, not content scripts)
- `web_accessible_resources`: lists `liteparse.wasm` + the offscreen HTML

The zip ships about 4 MB larger because of the bundled WASM.

## Known limitations

- **OCR / scanned PDFs**: not supported. Pages with no text layer return a `scanned_pdf:` error.
- **Encrypted PDFs**: not supported. Returns `encrypted_pdf:` error.
- **Image-only content**: text-only path; PDF figures are not extracted or rendered.
- **>100 MB payloads**: rejected with `too_large:` error.
- **Sidepanel mounted on an already-open `file://*.pdf` tab**: the permission card only triggers on `tabs.onUpdated`, not on first sidepanel open. The LLM's `file_access_denied:` error text still guides the user.

## 中文摘要

Pie 现在能读 PDF 了。Chrome 默认 PDF viewer 里打开的远程 PDF（论文 URL 一类）和本地 PDF（`file://`）都行。

**新增三个工具** — 都是 read-class，不弹 confirm：
- `get_pdf_outline()` — 看目录 + 总页数。生疏的 PDF 先调它。
- `read_pdf(page_range)` — 拿指定页。`"1"` / `"1-3"` / `"1,3,5"` / `"1-3,7"` 都行。
- `search_pdf(query)` — 全文搜，返回命中页 + 上下文片段。

**解析架构**：MV3 offscreen document 跑 LiteParse v2 WASM（PDFium 引擎，4 MB），SW 通过消息桥访问。结果按 `tab.url` 缓存在 offscreen 内存里，SW 退休时缓存自然清空。

**本地 PDF**：第一次需要在 `chrome://extensions` 给本扩展开 "Allow access to file URLs"。sidepanel 会弹引导卡片一键跳过去。卡片回到 sidepanel 时自动消失。

**已知限制**：
- 扫描件 / 加密 PDF / 100MB+ 大文件 — 返回结构化错误，不静默失败
- PDF 里的图片 / 公式渲染 — 暂不抽（v0.17 是文本 MVP）
- 已经开着的 `file://*.pdf` tab，sidepanel 第一次打开时不会主动弹权限卡（要靠 LLM 错误回包提示，后续 patch）

**zip 体积**：因为打包了 WASM，比 v0.16.0 大约多 4 MB。
