2 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 c538086f2b the chat says which mailbox it is in, and reads a brief instead of a prefix
the same arrangement proxy-manager got, with one difference that turned out to
matter: an email chat does not run in the plugin's chats directory, it runs in
the ACCOUNT's, beside emails.db.

three things.

the panel declares its chat context. it declared none, and it did not need to
while email was part of the platform — ChatPanelWrapper recognised the screen by
`workspace.id === 'email'` and supplied it. a plugin's workspace key is
`screens/plugin/email`, so that id became `plugin/email` on extraction day and
the branch silently stopped matching. every email chat since has run somewhere
with no mailbox anywhere near it. declaring it here is what that branch's own
comment asked for, and it carries the mailbox too, which a hardcoded screen id
never could — with two accounts the chat follows the picker.

the prompt prefix is now chat/CLAUDE.md. a prefix rides every message: paid per
turn forever, invisible as a design artefact because it lives in a typescript
constant, and visible in the one place it should not be. the file costs nothing
per turn and the person it is about can open it. it says more than the prefix
did, because it can afford to: answer from the local db and not from gmail, the
screen already shows what you would paste back, and a bulk delete over 19k rows
is not reversible from here.

the panel gets a header, so the conversation picker is scoped to this mailbox's
directory rather than absent. panelId is a prop now instead of the hardcoded
"email-chat" — two of these could never have held different conversations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-26 18:42:59 +00:00
Claude Opus 5 980a9d3c38 email, extracted from the officer platform
IMAP and Gmail sync into a per-account SQLite store, with the mail read and
composed in the browser.

moved whole from src/servers/sidecar/email (14 files), src/servers/api/email,
officer_db/src/email, and the Screens/Dashboard/Email tree. what changed on the
way out:

- credentials are encrypted. the column was plaintext jsonb holding an IMAP app
  password that the SMTP transport read straight out of the row; it is now
  AES-256-GCM under this plugin's own 'email' secret-store purpose, sealed and
  opened in queries.ts alone so nothing above it can hold ciphertext by
  accident. no plaintext fallback on read - a value that does not verify is an
  error.
- the router derives its prefix from mountPrefix() instead of the literal
  '/api/email', which was only correct for a first-party publisher.
- the frontend became panels.ts + layout.ts. the two panel appTypes that were
  null are now declared, because the platform screen used to override them by
  panel id and a plugin has no such hook.
- account management became a modal. it was a section in the platform's
  Settings, which a plugin cannot contribute to, and a panel does not work
  either: PluginScreen renders the workspace locked, so a panel outside the
  default layout is unreachable. it follows Compose, which had already solved
  this.
- 17 palette classes replaced with semantic tokens.

not verified: this has never run. it was switched off in the platform on
2026-08-13 and its table was never created, so nothing has exercised sync,
sending or the UI since. the first install is the first test.
2026-08-18 00:29:50 +00:00