Files
pi-perplexity/AGENTS.md
T

81 lines
3.8 KiB
Markdown

# pi-perplexity
## Stack
- TypeScript extension for `@mariozechner/pi-coding-agent`; loaded directly via jiti, no build artifact.
- Node >=18.14.1 runtime APIs only: `fetch`, `crypto.randomUUID()`, `Buffer.from(..., "base64url")`, `Intl`.
- Pi-bundled packages live in `peerDependencies` with `"*"`: `@mariozechner/pi-tui`, `@mariozechner/pi-ai`, `@sinclair/typebox`.
## Commands
```bash
npm run typecheck
npm test
node --import @mariozechner/jiti/register --input-type=module -e "import('./src/index.ts').then((m)=>{ const entry = m.default?.default ?? m.default; if (typeof entry !== 'function') throw new Error('default export missing'); })"
```
## File-Scoped Commands
| Task | Command |
|------|---------|
| Single test file | `npm run test:build && node --test --experimental-test-module-mocks ".tmp-test/test/search/stream.test.js"` |
| Live E2E opt-in | `PI_PERPLEXITY_E2E=1 npm test` |
## Structure
- `src/index.ts` — extension entry and `perplexity_search` tool definition.
- `src/auth/` — JWT extraction, email OTP login, token storage.
- `src/search/` — Perplexity SSE request, stream merge, result formatting, models.
- `src/render/` — TUI renderers for tool calls and results.
- `src/commands/` — slash commands such as login/config.
- `test/` — automated Node test suite; `.tmp-test/` is generated by `npm run test:build`.
- `local_tests/` — manual/debug scripts and captured dumps; do not treat as automated coverage.
- `docs/design-decisions.md` — rationale for non-obvious behavior.
## Extension Conventions
- Entry point exports a default factory from `src/index.ts`.
- Tool name is `perplexity_search`.
- Tool `execute` signature is `(toolCallId, params, signal, onUpdate, ctx)`.
- Tool results use `{ content: [{ type: "text", text }], details: { ... } }`.
- String enum params use `StringEnum` from `@mariozechner/pi-ai`; `Type.Union([Type.Literal(...)])` breaks Google models.
- Stream/API types stay loose: all reverse-engineered event fields are optional.
## Perplexity Protocol Gotchas
- Endpoint: `POST https://www.perplexity.ai/rest/sse/perplexity_ask`.
- Required Perplexity constants live in `src/constants.ts`.
- Searches default to `is_incognito: true` to avoid polluting user history.
- The response is `data:` JSON lines with `[DONE]`, not a fully standard SSE stream.
- Events are incremental snapshots: shallow-merge top level, merge blocks by `intended_usage`, splice markdown via `chunk_starting_offset`, accumulate sources.
- Completion is `event.final === true` or `event.status === "COMPLETED"`.
- Answer extraction order: markdown blocks → `ask_text` blocks → event text.
- Source extraction order: `web_results` block → `sources_list`; deduplicate by URL.
## Auth Gotchas
- Auth tries macOS app token first via `defaults read ai.perplexity.mac authToken`, unless `PI_AUTH_NO_BORROW=1`.
- Email OTP fallback uses `ctx.ui.input()`.
- Tokens are stored at `~/.config/pi-perplexity/auth.json` with mode `0600`.
- Stored tokens have no expiry tracking; `loadToken()` returns any cached token and expiry surfaces as 401/403 at request time.
- AUTH errors preserve the cached token; users explicitly clear/re-auth with `/perplexity-login --force`.
## Boundaries
### Always
- Pass `AbortSignal` through to network calls.
- Return user-readable error text from tool execution instead of throwing raw errors.
- Keep runtime dependency-free; use platform APIs for HTTP, SSE parsing, JWT decoding, and UUIDs.
### Ask First
- Changing Perplexity request headers, model IDs, or auth flow behavior.
- Adding package dependencies or generated build artifacts.
- Deleting captured local test dumps or token-related files.
### Never
- Never commit secrets, credentials, `.env`, cached auth tokens, or live Perplexity response dumps containing private data.
- Never clear cached auth automatically on transient 401/403 responses.