### Changed
- **Authentication simplified to a single environment variable** — replaced the file-based token system (`~/.jean2/auth-token.json` with random token generation, SHA-256 hashing, file I/O) with a single `JEAN2_AUTH_TOKEN` env var. Set it to any string to enable auth; leave it unset to disable. The old `JEAN2_AUTH_ENABLED` env var and `auth-token.json` file are no longer used. `jean2 init` no longer generates a token file. `jean2 auth` now shows the current auth status and masked token from the env var instead of subcommands (`show`, `regenerate`). Validation uses timing-safe comparison against the env var directly.

### Fixed
- **AI SDK stream errors are no longer silently swallowed** — `streamText()` catches errors from `doStream()` and emits them as `{ type: "error" }` chunks in the stream rather than throwing. The stream processing switch in `agent.ts` was missing a handler for this delta type, causing all provider errors (rate limits, server errors, timeouts, etc.) to be silently ignored and the assistant message to appear as an empty completed bubble. Now re-throws into the existing error handling path.
- **Assistant message status is set to `error` on failure** — when retries are exhausted, `retry.ts` now tracks the last assistant message through the stream, yields `message.updated` with `status: 'error'` and the error message, and persists it to the database. Previously the message stayed in `streaming` status forever on error.
- **Message update is broadcast before error event** — `message.updated` with error status is now yielded before the typed error event (`error.rate_limit`, `error.server`, etc.). The message-router switch-case returns early on error events, so the message update must come first for clients to see it.
- **Sandbox model errors are properly classified** — the sandbox model (`SandboxLanguageModel`) now creates errors with appropriate HTTP status codes and AI SDK error flags (`isRateLimitError`, `isRetryableError`, `isTimeoutError`) based on the `--type` flag, so `classifyApiError()` can correctly categorize them instead of falling through to "unknown".
- **Classified errors are preserved through retry** — when `agent.ts` throws a `ClassifiedError` plain object (non-retryable case), `retry.ts` now detects and reuses it via `isClassifiedError()` instead of re-classifying, which would produce `[object Object]` as the error message.
- **Forking a compacted session no longer shows a spurious "Compaction interrupted" error** — when forking a session that had been compacted, the compaction summary's `parentId` was not remapped to the new trigger ID. The orphaned-trigger recovery query would then find the forked trigger with no matching outcome and persist a failure message. Forking now builds an old→new ID map and remaps `parentId` on all copied messages.
- **Incremental compaction is more efficient** — on second and subsequent compactions, only new messages since the last summary are sent to the LLM instead of re-sending the entire pre-compaction history.
