From e6bb96a2e7d845b8c7067a2ae8dedabe2eeab72a Mon Sep 17 00:00:00 2001 From: TJ Baker <1617679+zaridan@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:06:32 -0700 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20refresh=20STATUS.md=20=E2=80=94=20A?= =?UTF-8?q?gent=20Inbox=20API=20loop=20complete=20(HT-17/HT-18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store, send, AND the native agent-inbox API (reads + writes) are all merged. Update the status page: the full loop is now behind an HTTP API; Next = first real EmailSender adapter (Gmail), a deploy, HT-16. Drops the stale conversations-v1.md reference (deleted; superseded by agent-inbox-v1.md). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01TqG66PPZreBrj17VbAqe3b --- STATUS.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/STATUS.md b/STATUS.md index 50fd5f1..f89be00 100644 --- a/STATUS.md +++ b/STATUS.md @@ -1,16 +1,16 @@ # Helpthread — Status -**Current state:** the mail engine's core loop is closed end-to-end — an inbound reply is parsed and threaded, conversations and threads are persisted, and an outbound reply is sent with a signed reply token in its Message-ID that a future customer reply threads back on. Everything below the wire (parse → thread → store → send) exists and is tested against real in-process Postgres; what's left is a real email provider adapter, the HTTP/API surface, and a UI. All work lands through a guarded pipeline: every PR runs typecheck, lint, tests-with-coverage, secret scanning, and CodeQL, plus AI review (CodeRabbit) and — for security- and threading-critical code — an independent Codex pass. +**Current state:** the mail engine's full loop is built and behind a native HTTP API — an inbound reply is parsed, threaded, and stored; an Agent can list the inbox, read a conversation, and reply; the reply is sent with a signed token in its Message-ID that a future customer reply threads back on. Everything from the wire up (parse → thread → store → read → reply → send) exists and is tested against real in-process Postgres and real `Request`/`Response` objects. What's left to run it *live* is a concrete email-provider adapter and a deployment. All work lands through a guarded pipeline: every PR runs typecheck, lint, tests-with-coverage, secret scanning, and CodeQL, plus AI review (CodeRabbit) and — for security-, auth-, and threading-critical code — an independent Codex pass. ## Now -**Phase 1 — Core engine, dogfooded.** The full inbound→outbound threading loop is built; turning it toward a runnable, dogfoodable slice (a real send adapter + the conversation API). +**Phase 1 — Core engine, dogfooded.** The inbound→outbound loop and its API are done; building the first real send adapter (Gmail) — the piece that turns the tested engine into actually-sent mail. ## Done **Foundation** - Founding charter ([CHARTER.md](CHARTER.md)) — mission, principles, licensing, architecture, roadmap. -- Behavioral specs: conversation API contract (`specs/api/conversations-v1.md`), mail threading (`specs/mail/threading.md`), outbound sending (`specs/mail/sending.md`), conversation store (`specs/store/conversations.md`). +- Behavioral specs: mail threading (`specs/mail/threading.md`), outbound sending (`specs/mail/sending.md`), conversation store (`specs/store/conversations.md`), and the native Agent Inbox API (`specs/api/agent-inbox-v1.md`). - Platform provider interfaces (`src/providers/`) — queue, scheduler, blob storage, inbound email, and outbound email sender — Vercel-first, not Vercel-only. - Black-box acceptance fixtures (`fixtures/mail/`). - CI/quality foundation: TypeScript (strict, NodeNext) + Biome + Vitest (v8 coverage); CI (quality + secret scan), CodeQL, OpenSSF Scorecard; branch protection requires all checks green. @@ -20,21 +20,27 @@ - **Signed reply tokens** (`src/mail/reply-token.ts`) — HMAC-SHA256 tokens minted into outbound Message-IDs and verified on reply, with key rotation. The cryptographic basis for trustworthy threading. - **RFC 5322 message-id extractor** (`src/mail/message-id.ts`) — comment/quoted-string-aware tokenization, shared across the engine. - **Threading decision** (`src/mail/thread.ts`) — the 5-rule algorithm: a verified token routes a reply to its conversation; no valid token starts a new one; subject is never used. -- **Conversation/thread store** (`src/store/`, `src/db/`) — persistence on a thin, portable raw-SQL layer over PGlite (in-process Postgres) locally, the same SQL destined for Supabase. A valid token to a closed conversation reopens it; to a deleted one, the caller starts fresh. -- **Outbound send** (`src/mail/send.ts`) — mints the reply token into the outbound Message-ID, persists the outbound thread as an outbox item (`pending`→`sent`/`failed`), and hands it to an `EmailSender`. A round-trip test proves a sent reply threads back to the right conversation. +- **Conversation/thread store** (`src/store/`, `src/db/`) — persistence on a thin, portable raw-SQL layer over PGlite (in-process Postgres) locally, the same SQL destined for Supabase. Keyset-paginated listing; a valid token to a closed conversation reopens it, to a deleted one the caller starts fresh. +- **Outbound send** (`src/mail/send.ts`) — mints the reply token into the outbound Message-ID, persists the outbound thread as an outbox item (`pending`→`sent`/`failed`), and hands it to an `EmailSender`. Returns typed outcomes (a delivered message is never reported as failed). A round-trip test proves a sent reply threads back to the right conversation. + +**Agent Inbox API v1** (`src/api/`) — native, framework-agnostic `Request → Response` (a Vercel/Node adapter is a later thin wrapper; Node runtime, since the engine's HMAC uses `node:crypto`). +- Constant-time Bearer auth that runs *before* routing; native `{ error: { code, message } }` envelope; `Cache-Control: no-store` on every response; UUID-shape guards; a top-level catch so nothing leaks as an uncontrolled 500. +- `GET /conversations` (inbox list, newest-activity-first, status filter, keyset cursor) · `GET /conversations/{id}` (conversation + threads) · `POST /conversations/{id}/replies` (derives the headers, mints + sends) · `PATCH /conversations/{id}` (close/reopen). ## Next -- **A real `EmailSender` adapter** (Gmail send / Postmark / SES) — the first one that puts actual mail on the wire, with a wire-level test proving the Message-ID is transmitted verbatim. -- **Send idempotency + delivery worker** ([HT-16](https://resonantiq.atlassian.net/browse/HT-16)) — a dedup key and a worker that retries `pending`/`failed` outbound threads reusing the same Message-ID; required before `sendReply` goes behind a live retrying caller. -- **The six-operation conversation API** and an agent inbox UI (API-first, per the charter). +- **A real `EmailSender` adapter — Gmail** (`users.messages.send`) through the support Google Workspace account: the first thing that puts actual mail on the wire, with a wire-level test proving our Message-ID is transmitted verbatim (Gmail preserves an RFC-compliant custom Message-ID; our token is compliant by construction). Postmark/SES/Resend remain a later one-file swap if scale or deliverability demands it. +- **A deployment** — a thin Vercel/Node route wrapping `createInboxApi`, a real Gmail inbound webhook, and Supabase. +- **Send idempotency + delivery worker** ([HT-16](https://resonantiq.atlassian.net/browse/HT-16)) — required before `sendReply` sits behind a retrying caller. +- **An Agent inbox UI** over the API (API-first, per the charter). ## Not yet / deferred -- Live Vercel + Supabase deployment — deferred to the first deployable milestone; provider adapters are interfaces (inbound + outbound), concrete ones not yet built. +- Live Vercel + Supabase deployment — deferred to the first deployable milestone; the inbound and outbound provider seams exist, concrete adapters are being built now (outbound first). - Agent inbox UI. +- A customer-side / self-service API (a separate future surface, designed native when there are customers to serve). - Marketplace (paid modules, license keys, module registry). --- -_Last updated: 2026-07-10_ +_Last updated: 2026-07-11_ From e63b6e76baf35eec7054130c53b5b2f594f0e11b Mon Sep 17 00:00:00 2001 From: TJ Baker <1617679+zaridan@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:25:22 -0700 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20address=20CodeRabbit=20review=20?= =?UTF-8?q?=E2=80=94=20/api/v1=20prefix=20+=20drop=20stale=20deferred=20UI?= =?UTF-8?q?=20entry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Endpoint paths in STATUS.md now match the implemented routes (src/api/router.ts matches only under /api/v1). Agent inbox UI is listed once, under Next. Co-Authored-By: Claude Fable 5 --- STATUS.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/STATUS.md b/STATUS.md index f89be00..e694a5b 100644 --- a/STATUS.md +++ b/STATUS.md @@ -25,7 +25,7 @@ **Agent Inbox API v1** (`src/api/`) — native, framework-agnostic `Request → Response` (a Vercel/Node adapter is a later thin wrapper; Node runtime, since the engine's HMAC uses `node:crypto`). - Constant-time Bearer auth that runs *before* routing; native `{ error: { code, message } }` envelope; `Cache-Control: no-store` on every response; UUID-shape guards; a top-level catch so nothing leaks as an uncontrolled 500. -- `GET /conversations` (inbox list, newest-activity-first, status filter, keyset cursor) · `GET /conversations/{id}` (conversation + threads) · `POST /conversations/{id}/replies` (derives the headers, mints + sends) · `PATCH /conversations/{id}` (close/reopen). +- `GET /api/v1/conversations` (inbox list, newest-activity-first, status filter, keyset cursor) · `GET /api/v1/conversations/{id}` (conversation + threads) · `POST /api/v1/conversations/{id}/replies` (derives the headers, mints + sends) · `PATCH /api/v1/conversations/{id}` (close/reopen). ## Next @@ -37,7 +37,6 @@ ## Not yet / deferred - Live Vercel + Supabase deployment — deferred to the first deployable milestone; the inbound and outbound provider seams exist, concrete adapters are being built now (outbound first). -- Agent inbox UI. - A customer-side / self-service API (a separate future surface, designed native when there are customers to serve). - Marketplace (paid modules, license keys, module registry).