# Pie v0.5.2 — Multi-pin + agent-driven tabs (Phase 5 + M5 + v1.5)

**Release date:** 2026-05-05
**Manifest:** `0.5.2`

This release adds three large agent capabilities on top of the v0.5.0 MVP: image input (Phase 5), explicit pin-mode UX (M5), and multi-pin + agent-driven tab creation/focus (v1.5 Path A).

---

## Highlights

- **Image input (Phase 5).** Paste / drag / upload up to 3 images per turn; the agent can also call `capture_visible_tab` / `capture_fullpage_tab` to take its own screenshots. Anthropic / OpenAI / OpenRouter vision are wired today; ZhiPu / Bailian / MiniMax vision is on the v1.1 backlog.
- **Pin mode state machine (M5).** Sessions now have an explicit `auto` / `task` / `user` pin mode instead of a free-form pinned-tab field. The PinnedTabDropdown lets you lock a session to a specific tab (`user` mode) or let it follow the active tab during exploration (`auto` mode); `task` mode is auto-applied during agent runs and cleared at task end.
- **Multi-pin + agent-driven tabs (v1.5).** The agent can now create new tabs (`open_url`) and switch its focus across multiple pinned tabs (`focus_tab`) within a single task. User-mode pins also support multi-select.

## Agent capabilities — new tools

- **`open_url(url, active?)`** — agent creates a new tab.
  - Strict allowlist: `protocol === 'http:' | 'https:'` only. `javascript:`, `data:`, `file:`, `chrome://`, etc. are rejected at the LLM-arg validation step.
  - IDN homograph defense: hostnames are displayed in punycode in the confirm card so visually similar Cyrillic / lookalike domains don't slip past human review.
  - Always shows a per-call confirm card. The newly created tab is appended to the session's `pinnedTabs[]` so the agent can address it via `focus_tab` afterwards.
- **`focus_tab(tabId)`** — switch the agent's snapshot focus to one of the session's pinned tabs.
  - Low-risk (no observable side effect): only the agent's internal focus pointer changes. Takes effect on the **next** iteration — the current iteration's snapshot was already taken.
  - Pinned tab list is rendered in the system prompt so the agent knows which `tabId`s are valid.
- **`capture_visible_tab(quality?)` / `capture_fullpage_tab(quality?)`** — screenshot tools (Phase 5). Always-high risk (capture is sensitive: covers credentials / private info on the page). Per-task screenshot budget; bytes never persisted to `chrome.storage`.

## Multi-pin storage shape

`SessionMeta` now carries `pinnedTabs: Array<{tabId, origin}>` instead of a single `pinnedTabId/Origin` pair. Behavior per pin mode:

| Mode   | `pinnedTabs[]`                                           | R7 cross-session lock | Drift check |
|--------|----------------------------------------------------------|-----------------------|-------------|
| `auto` | empty                                                    | skip                  | skip        |
| `task` | `[chat-start capture, ...open_url additions]`            | include all entries   | walk all    |
| `user` | `≥1` (user picks via PinnedTabDropdown, multi-select OK) | include all entries   | skip        |

`SessionAgentState.currentFocusTabId` (task-scoped) tracks which pin the agent is currently looking at; reset to `pinnedTabs[0].tabId` at chat-start, mutated by `focus_tab`, cleared at task end.

## ⚠️ BREAKING — chrome.storage shape

The `@deprecated` single-pin fields (`SessionMeta.pinnedTabId`, `SessionMeta.pinnedOrigin`, `SessionIndexEntry.pinnedTabId`) are **deleted in this release**. Storage no longer dual-writes them.

**What this means for you:**
- If you upgrade from a v0.5.0 / pre-v1.5 build, existing **single-pin** sessions are still readable (the loop's per-iteration origin check picks up real drift even when `pinnedTabs[]` is missing). Pre-v1.5 sessions are now treated as pin-less in the drift card; the next chat-start will re-capture pin via the M5 `auto → task` upgrade path.
- If you had explicitly multi-tab pin selections from a v1.5-pre-cleanup test build, **re-set them** via the PinnedTabDropdown after upgrade.
- Manifest version bumped `0.5.0 → 0.5.2`. Reload the unpacked extension (`chrome://extensions → reload`) to pick up the schema change.

## Safety

- **Pin-mode-aware K-9.** `close_tabs` only refuses to close a session's pinned tab when the session is in `user` mode (the user explicitly locked it). `task` and `auto` mode allow the close — the agent's per-iteration origin check then aborts the task gracefully ("page closed") rather than producing a confirm-card deadlock.
- **`open_url` per-call approval.** Always-high risk, always shows the confirm card. SkillsList shows a per-call approval badge for skills that include `open_url` in their `allowedTools`.
- **`focus_tab` is no-side-effect.** Mutating only the internal focus pointer; the new tab's snapshot doesn't render until the next iteration, giving the LLM a clean barrier.
- **Image input untrusted-content boundary.** Page-derived screenshot bytes carry an `<untrusted_image>` system-prompt notice (R15) so the LLM can't be tricked by adversarial in-image instructions.

## Known limitations / v1.5.1 backlog

- **PinnedTabDropdown tab list is mount-only.** It doesn't refresh while open under multi-select usage. Cosmetic; v1.5.1 will subscribe to `chrome.tabs.onCreated/onRemoved/onUpdated` for the dropdown's lifetime.
- **Phantom pin pruning.** When a tab in `pinnedTabs[]` is closed manually (or crashes), the entry stays in the array. The loop's per-iteration origin check fails-soft on dead tabs (clean abort, no security issue), but the user-visible pin set may carry zombies until the next chat-start. v1.5.1 will add a `chrome.tabs.onRemoved` listener AND/OR chat-start re-validation against live `chrome.tabs.get`.
- **`tabTargets` / `contentPreview` confirm-card wire drop.** Pre-existing since Phase 3: the panel's agent-confirm-request handler does not destructure these fields off the wire even though the SW emits them. `close_tabs` / `group_tabs` / `get_tab_content` confirm cards lose the origin list and content preview. K-1 informed-approval shortfall; tracked for fix.
- **`ownerToken.tabId` doesn't refresh on `focus_tab`.** Metadata-only inconsistency (CDP keyboard tools route via `ctx.tabId`, not `ownerToken.tabId`, so routing is always correct). Defense-in-depth fix in v1.5.1.
- **Gemini provider** still missing; ZhiPu / Bailian / MiniMax vision pending per-model capability registry (§7 ROADMAP).

## Provider matrix

| Provider | Tools | Vision |
|---|---|---|
| Anthropic Claude | ✅ | ✅ |
| OpenAI | ✅ | ✅ |
| OpenRouter | ✅ | ✅ |
| MiniMax | ✅ | (per-model — pending §7) |
| ZhiPu (智谱) | ✅ | (per-model — pending §7) |
| Bailian (百炼) | ✅ | (per-model — pending §7) |

## Install

Same as v0.5.0 — load `dist/` as an unpacked extension after `pnpm install && pnpm build`. Chrome Web Store listing remains in review.

## Links

- **Roadmap:** [`docs/ROADMAP.md`](../ROADMAP.md)
- **v1.5 invariant trace:** [`docs/solutions/2026-05-03-multi-session-invariant-trace.md`](../solutions/2026-05-03-multi-session-invariant-trace.md)
- **Phase 5 invariant trace:** [`docs/solutions/2026-05-04-multimodal-image-input-v1.md`](../solutions/2026-05-04-multimodal-image-input-v1.md)
