778d7d3f12938c1d15d7b9a78aa7a361523a0060
clicking a message did nothing. the reading pane stayed on "Select an email to read" whatever you opened, and it had been that way since email became a plugin. the reader asks the URL which message is open. it asked for `emailId`, which was the param name back when email was part of the platform and declared its own `/email/:emailId` route. a plugin declares no routes: the platform mounts every one at `<route>` and `<route>/:section`, so the id arrives as `section`. asking for `emailId` got undefined, `selectedId` was null on every render, and the pane rendered its empty state. nothing else was wrong, which is why this took so long to place — the list showed, the link navigated, the URL changed, and both API routes return full data (verified against the real 19k-message store: /messages carries every snippet, /thread/:id returns 55KB of html). one undefined param, and the whole right-hand side of the screen looked dead. still accepts `emailId`, so this keeps working if email ever gets its own route again. the underlying smell is the platform naming a plugin's param for it — worth fixing there, not here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
IMAP and Gmail accounts, synced into a per-account SQLite store on your own machine and searchable offline.
- Your mail stays local. Each account gets its own
emails.dbunder the platform's data directory, with an FTS5 index and Gmail-style search operators. Nothing is sent anywhere to be indexed. - IMAP or Gmail. Generic IMAP over
imapflow, or Gmail's history API where an account is connected through the platform's Google integration. - Real-time. IMAP IDLE watchers push new mail as it lands, with a slow cron poll as a backstop.
- Composing and sending, with attachments, over the account's own SMTP.
Status — read this before installing
This plugin has never been run. It was extracted from the Officer platform on 2026-08-17, where it had been switched off since 2026-08-13 — its database table was commented out of the schema and therefore never created, so nothing has exercised sync, sending or the UI since. The code is believed good (the sidecar starts cleanly and typechecks) but that is not the same as working.
Treat the first install as the first test. docs/email-plugin-extraction.md in the platform repository
records what was moved and what was decided.
Known gaps
- Credentials are stored unencrypted.
email_accounts.credentialsis a jsonb blob holding the IMAP app password in the clear. The platform has a secret store with per-purpose keys and this plugin does not use it yet. Deliberately left alone during the extraction so that a first-install failure has exactly one possible cause; it is the first thing to fix afterwards. - The
email_dbagent tool is not part of this plugin. It is resolved at runtime fromPI_TOOLS_DIRSand the platform points it at the account's SQLite store. The email chat panel's prompt assumes it exists. - Account management placement is unsettled — see the extraction doc.
Languages
TypeScript
100%