Skip to content

OpenThreads: Full Implementation Roadmap (meta/17 → main)#34

Open
claude[bot] wants to merge 41 commits intomainfrom
meta/17
Open

OpenThreads: Full Implementation Roadmap (meta/17 → main)#34
claude[bot] wants to merge 41 commits intomainfrom
meta/17

Conversation

@claude
Copy link
Copy Markdown

@claude claude Bot commented Apr 11, 2026

Summary

This PR merges the complete implementation of the OpenThreads platform, as tracked in meta issue #17. All 16 implementation issues have been completed across 8 waves.

What Was Implemented

Wave 1 — Foundation

Wave 2 — Contracts

Wave 3 — Core Logic + Adapters (parallel)

Wave 4 — Reply Engine

Wave 5 — Server + More Adapters (parallel)

Wave 6 — UI & Forms (parallel)

Wave 7 — Trust & Hardening

Wave 8 — Release

Test plan

  • CI passes on this PR
  • All 16 issue PRs were merged successfully into meta/17
  • Review CHANGELOG and release artifacts from Release & Developer Experience #16
  • Smoke-test the dev environment (docker compose up)

Closes #17

🤖 Generated with Claude Code

github-actions Bot and others added 30 commits April 10, 2026 21:29
Sets up the complete project bootstrap per issue #1:

Monorepo:
- Bun workspaces with packages: core, storage/mongodb, channels, server, trust
- Shared tsconfig.base.json with per-package extends and project references
- Vite library build config for core, storage/mongodb, channels, trust
- Next.js setup for the server package

Quality tooling:
- ESLint v9 flat config with @typescript-eslint + prettier integration
- Prettier with consistent formatting rules
- lint-staged config in package.json for pre-commit hooks
- husky prepare script (run `bun install` to initialize hooks)

CI/CD:
- GitHub Actions workflow: lint, typecheck, test on push/PR

Dev environment:
- Docker Compose with MongoDB 7.0
- .env.example with documented variables
- Seed script at scripts/seed.ts for sample channels, routes, threads

Co-authored-by: Gustavo Gondim <ggondim@users.noreply.github.com>
The CI workflow needs to be committed by a user with workflows permission.
See .github/workflows/ci.yml in the working tree for the intended content.

Co-authored-by: Gustavo Gondim <ggondim@users.noreply.github.com>
Project Bootstrap — Monorepo, CI, Dev Environment
…erfaces

Implements issue #2 — all core TypeScript contracts for @openthreads/core:

Data model types:
- Channel, Recipient, Thread, Turn, Route, Envelope
- A2H intent types (INFORM, COLLECT, AUTHORIZE, ESCALATE, RESULT)
- Message union type with duck-typing discriminator (intent field = A2H)

Utilities:
- ID generation for ot_thr_*, ot_turn_*, ot_tk_*, ot_ch_sk_* prefixes
- Message classification helpers (isA2HMessage, classifyMessages, etc.)

Interfaces (fully abstract, no implementation):
- StorageAdapter with CRUD for all entities + token lifecycle
- StorageAdapterFactory for pluggable instantiation
- ChannelAdapter with register, sendMessage, render, captureResponse, capabilities
- ChannelCapabilities type flag set

Tests:
- Unit tests for ID generation (prefix validation, uniqueness, type guards)
- Unit tests for message classification (duck-typing, mixed arrays, helpers)

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Add packages/core with full implementation of:

- TokenManager: ephemeral token generation/validation/revocation (ot_tk_*)
  with configurable TTL (default 24h), and channel API key management
  (ot_ch_sk_*) with channel-scoped validation
- ThreadManager: native thread creation with de-duplication on native IDs,
  virtual thread detection via reply-chain grouping, and main thread
  get-or-create per (channel, target) pair
- TurnManager: inbound/outbound turn logging (ot_turn_*) with chronological
  listing per thread
- StorageAdapter interface for pluggable persistence backends
- InMemoryStorageAdapter for testing
- Full unit test suite covering token lifecycle, thread resolution, and
  turn tracking

Refs #5

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Adds the `@openthreads/core` package with:

- **Data model types** (`types.ts`): Channel, Recipient, Route,
  RouteCriteria, InboundMessage, Thread, Turn — derived from VISION.md.

- **Route matching engine** (`router.ts`):
  - `router(routes, message)` — filters enabled routes whose criteria all
    match the inbound message, then returns them sorted by descending
    priority (highest-priority first).
  - Glob/wildcard support via `globToRegex` / `matchGlob` (`*` = any
    chars, `?` = one char; regex metacharacters are escaped so literal
    dots in channel/user IDs behave correctly).
  - Per-field AND semantics; array criteria use OR (any pattern matches).
  - Boolean criteria (`isThread`, `isMention`, `isDM`) accept undefined
    as "match everything".
  - Disabled routes are always excluded.
  - Multiple recipients per route (fan-out) are preserved on each result.
  - Input array is never mutated (sort operates on a copy produced by
    filter).

- **Unit tests** (`router.test.ts`): 40+ cases covering glob edge-cases,
  criterion matching, priority ordering, stable sort, fan-out, overlapping
  routes, disabled routes, no-match, and a realistic multi-route scenario.

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
- packages/core: define all data types (Channel, Recipient, Thread,
  Turn, Route, Token) and the StorageAdapter interface
- packages/storage/mongodb: full MongoStorageAdapter implementation
  with connection pooling, graceful shutdown, and all CRUD methods
- Indexes: unique threadId/turnId, channelId+nativeThreadId,
  channelId+targetId, threadId+timestamp, criteria fields for routes,
  unique token value + TTL on expiresAt
- Migration utilities: ensureAllIndexes + idempotent seed script
- Integration tests: full coverage against real MongoDB via Docker

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Adds the full @openthreads/channels-slack package implementing the
ChannelAdapter interface for Slack using @slack/bolt.

Packages added:
- packages/core — ChannelAdapter interface, A2H types, envelope types
- packages/channels/slack — SlackAdapter class

Features implemented:
- Inbound: message events, app_mention events, /openthreads slash command
- Outbound: text (mrkdwn), Block Kit blocks/buttons/select menus
- Thread support: native Slack thread_ts ↔ OpenThreads threadId (1:1 map)
- A2H AUTHORIZE → Approve/Deny Block Kit buttons (method 1)
- A2H COLLECT with options → static_select menu (method 1)
- A2H COLLECT free-text → thread reply capture (method 2)
- A2H INFORM → plain text message
- Capabilities: { threads, buttons, selectMenus, dms, fileUpload: true, replyMessages: false }

Tests:
- Unit tests for Block Kit builders (blocks.test.ts)
- Unit tests for normalize utilities (normalize.test.ts)
- Integration tests for SlackAdapter with mock App/WebClient (SlackAdapter.test.ts)
- Shared adapter conformance suite (conformance.test.ts)

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Adds packages/channels/discord — a full ChannelAdapter implementation
for Discord using discord.js:

Inbound:
- message create, @mentions (parseMessage)
- slash commands (parseSlashCommand + REST registration)

Outbound:
- plain text messages
- styled Discord embeds (INFORM / AUTHORIZE / COLLECT / ESCALATE)
- message components: approve/deny buttons (AUTHORIZE), select menus
  (COLLECT with closed options)

Threads:
- getThread / createThread supporting both TextChannel threads and
  ForumChannel posts (1:1 mapping with OpenThreads thread IDs)
- ensureThreadActive helper to unarchive threads before sending

A2H (method 1 + method 2):
- buildA2HComponents maps A2H intents to Discord action rows
- awaitResponse captures button/select interactions and thread replies
- Timeout configurable via interactionTimeoutSeconds

Capabilities: { threads, buttons, selectMenus, dms, fileUpload: true;
                replyMessages: false }

Tests:
- Unit tests for message/command parsing, component builders, embeds
- Conformance tests verifying ChannelAdapter interface contract

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Adds the Reply Engine component in packages/core — the component that
processes recipient inbound envelopes, classifies each message item
(Chat SDK vs A2H via duck typing), selects the appropriate reply method
via the automatic decision tree, and orchestrates blocking response
collection.

Key components:
- types/index.ts: full data model (ChatSDKMessage, A2HMessage, ChannelAdapter,
  ReplyEngineConfig, and all supporting types)
- reply-engine/normalizer.ts: normalize single object → 1-item array
- reply-engine/classifier.ts: duck-typing classification (intent field = A2H)
- reply-engine/method-selector.ts: decision tree for methods 1–4 with
  capture hierarchy for method 2
- reply-engine/response-collector.ts: TimeoutError, withTimeout(), and
  ResponseRegistry for pending form submissions (methods 3 & 4)
- reply-engine/index.ts: ReplyEngine class orchestrating all of the above

Unit tests cover: classification, method selection across all intent/capability
combinations, normalizer, mixed arrays, method 4 batching, trust layer,
timeout handling, escalation handler, and ResponseRegistry.

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Implements the full ChannelAdapter interface for WhatsApp using the
Baileys library (WhatsApp Web protocol).

Key features:
- SessionManager: QR-code auth, multi-file auth-state persistence,
  automatic reconnection with exponential backoff, clean shutdown
- Inbound: text, image, video, audio, document, sticker, button/list
  responses; virtual thread IDs via quoted-reply chains
- Outbound: text, buttons (max 3), list messages, image/video/audio/
  document media
- A2H method 1: AUTHORIZE with ≤3 options → WhatsApp interactive buttons
- A2H method 3: AUTHORIZE >3 options / COLLECT / complex intents → external
  form link
- A2H method 2: quoted-reply capture for free-text responses (pending
  capture map with configurable timeout)
- Capabilities: { threads: false, buttons: true, selectMenus: false,
  replyMessages: true, dms: true, fileUpload: true }
- Conformance test suite covering capabilities, inbound/outbound, A2H
  flows, and lifecycle

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Adds the @openthreads/channel-telegram package implementing the full
ChannelAdapter interface for Telegram Bot API integration.

- packages/core: ChannelAdapter interface, A2H types, message envelope types
- packages/channels/telegram:
  - TelegramApiClient: lightweight Telegram Bot API HTTP client
  - TelegramAdapter: full ChannelAdapter implementation
  - InMemoryThreadStore: virtual threads via reply chains (reply_to_message_id)
  - Inbound parsing: text, commands, callback queries, attachments
  - Outbound formatting: plain text, HTML, MarkdownV2, inline/reply keyboards
  - A2H renderer: AUTHORIZE → inline keyboard (✅/❌), COLLECT+options → buttons,
    COLLECT free-text → reply-capture (method 2)
  - Capabilities: { threads: false, buttons: true, selectMenus: false,
    replyMessages: true, dms: true, fileUpload: true }
  - Comprehensive unit tests for all modules

Closes #8

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Implements issue #11 — full HTTP surface for OpenThreads server:

**Recipient Inbound (core send endpoints)**
- POST /send/channel/:channelId/target/:groupOrUser — new thread variant
- POST /send/channel/:channelId/target/:groupOrUser/thread/:threadId — existing thread
- Dual auth: ephemeral token (?token=) OR channel API key (Authorization: Bearer)
- Returns 202 for fire-and-forget, 200 with intent details for blocking A2H

**Channel Inbound Webhooks**
- POST /webhook/:channelId — generic webhook receiver
- Slack signing secret verification (HMAC-SHA256, replay protection)
- Telegram webhook secret token verification
- Event normalization (Slack, Telegram, generic)
- Router integration: fan-out to matched recipients via fetch()

**Management CRUD API**
- GET/POST /api/channels — list and create channels (auto-generates API key)
- GET/PUT/DELETE /api/channels/:id
- GET/POST /api/recipients
- GET/PUT/DELETE /api/recipients/:id
- GET/POST /api/routes — with priority ordering
- GET/PUT/DELETE /api/routes/:id
- GET /api/threads — filterable by channelId and targetId
- GET /api/threads/:threadId
- GET /api/threads/:threadId/turns

**Auth**
- Ephemeral token validation (verifyEphemeralToken)
- Channel API key validation (verifyChannelApiKey)
- Combined send auth (verifySendAuth) — token first, key fallback
- Management API key (MANAGEMENT_API_KEY env var, dev bypass)

**Infrastructure**
- lib/db.ts — MongoDB singleton with full CRUD + index setup
- lib/auth.ts — typed auth middleware helpers
- lib/rate-limit.ts — in-memory sliding-window rate limiter
- lib/logger.ts — structured request logging helpers
- lib/fanout.ts — concurrent recipient delivery via fetch()
- GET /health (and /api/health) — database ping + uptime
- instrumentation.ts — graceful shutdown (SIGTERM/SIGINT → MongoDB close)
- serverExternalPackages: ['mongodb'] in next.config.ts

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
github-actions Bot and others added 11 commits April 10, 2026 23:28
- GET /form/[formKey] — server component that lazily creates a FormRecord
  from the turn on first access, checks TTL expiry, renders FormClient
- FormClient — Ant Design 5 UI with AUTHORIZE (approve/deny + context),
  COLLECT (free-text, multi-field, select/radio/checkbox), batch layout,
  and loading/success/error/expired states
- POST /api/form/[formKey] — validates responses, marks form submitted in
  MongoDB, resolves in-process formRegistry promise to unblock Reply Engine
- FormResponseRegistry singleton (globalThis) bridges form submission to
  in-process Reply Engine without cross-module type coupling
- FormRecord type + CRUD helpers + TTL index in db.ts
- AntdRegistry wrapper in layout.tsx for Next.js SSR support
- antd ^5, @ant-design/icons ^5, @ant-design/nextjs-registry ^1 added

fixes #12

Co-authored-by: Gustavo Gondim <ggondim@users.noreply.github.com>
Implements the full management dashboard UI for the OpenThreads server.

## Dashboard pages (/dashboard/*)
- Overview: live stats (channel/route/thread counts)
- Channels: list + add wizard (platform → credentials → test → save),
  edit/delete, masked API key with eye/copy toggle
- Routes: ReactFlow visual flow (channel → route → recipient nodes),
  drag-to-create edges, criteria drawer, priority list, test route matching
- Threads: searchable + filterable table, click-to-detail
- Thread Detail: chronological turn timeline, A2H intent rendering,
  raw envelope collapsible
- Settings: global TTL/trust-layer config + per-channel overrides

## New API endpoints
- POST /api/routes/test — simulate inbound criteria → matching route IDs
- GET /api/settings — read global settings from MongoDB
- PUT /api/settings — update global settings

## Updated
- GET /api/threads — channelId now optional; added search query param
- lib/db.ts — listThreads(), AppSettings/ChannelOverride types,
  getSettings(), updateSettings()
- Root page redirects to /dashboard
- Root layout wraps with AntdRegistry for Next.js SSR CSS handling
- lib/api-client.ts — new typed API client for dashboard pages

fixes #13

Co-authored-by: Gustavo Gondim <ggondim@users.noreply.github.com>
…y protection

Implements the @openthreads/trust package and server integration for Issue 14.

packages/trust:
- JWS signing via Web Crypto API (ES256/ECDSA P-256) — no external deps
  signIntent/signResponse bind intent → response into a verifiable evidence chain
- Replay protection: ReplayGuard with nonce store + timestamp validation
- Audit logging: AuditLogger + InMemoryAuditStorage + AuditStorageAdapter interface
- Strong auth: TOTP (RFC 6238 via Web Crypto HMAC-SHA1), WebAuthn challenge gen/verify
- AuthChallengeManager: issue + verify challenges (webauthn, totp, sms_otp)
- TrustLayerManager: single entry point wiring all subsystems together
  Auto-generates ES256 key pair; enabled=false means zero overhead

packages/server:
- GET /api/audit: query audit log (turnId, threadId, eventType, date range filters)
- POST /api/form/:key/auth: issue auth challenge before form submission
- PUT  /api/form/:key/auth: verify challenge (TOTP code or WebAuthn assertion)
- Form GET: returns requiresAuth=true + emits intent_rendered audit event
- Form POST: requires verified challengeId when TRUST_LAYER_ENABLED=true
- TrustLayerManager singleton (globalThis) with MongoDB-backed audit storage
- audit_log MongoDB collection with indexes

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Implements Issue #15 — Integration Testing & Hardening:

**Resilience**
- `packages/server/src/lib/retry.ts`: Exponential backoff retry utility
  (`withRetry`, `computeRetryDelay`) with configurable attempts, delays, and
  per-error `retryable` predicate.
- `packages/server/src/lib/fanout.ts`: Extended with `deliverWithRetry` —
  automatically retries 5xx/network failures; treats 4xx as non-retryable.
- `packages/server/src/lib/deduplication.ts`: Idempotent inbound message
  deduplication via in-memory LRU store with per-entry TTL.  Includes
  platform-specific key builders (Slack, Telegram, WhatsApp, generic).
- `packages/server/src/lib/graceful-storage.ts`: `withGracefulStorage` helper
  catches storage errors and returns a safe fallback; `StorageHealthMonitor`
  tracks sliding-window error rate for circuit-breaking.
- `packages/channels/src/reconnect.ts`: Generic `ReconnectManager` for
  WebSocket-based adapters (Slack Socket Mode, Discord, etc.) with the same
  exponential-backoff pattern used by the WhatsApp `SessionManager`.

**Adapter Conformance**
- `packages/channels/src/conformance-suite.ts`: `runConformanceSuite()` factory
  generates a standardised Bun test suite for any `ChannelAdapter`.  Covers
  interface shape, capability flags, handler registration, and lifecycle.
  Handles the different method naming conventions across adapters (Slack/WA/
  Discord/Telegram).
- `packages/channels/src/mocks/mock-channel-server.ts`: In-process mock servers
  for Slack, Telegram, and generic HTTP webhooks.  Used to simulate platform
  callbacks in unit and E2E tests.

**E2E Test Scenarios (Issue #15 — all 7 scenarios)**
- `packages/core/tests/e2e/lifecycle.test.ts`: Full message lifecycle tests
  exercising all seven E2E scenarios from the issue using `InMemoryStorageAdapter`
  and core managers (no real HTTP or database).

**Unit Tests**
- `packages/server/tests/retry.test.ts`
- `packages/server/tests/deduplication.test.ts`
- `packages/server/tests/graceful-storage.test.ts`
- `packages/server/tests/fanout.test.ts`
- `packages/channels/tests/reconnect.test.ts`

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
Packaging & Deployment:
- Dockerfile for OpenThreads server (multi-stage Bun build)
- docker-compose.yml production profile (app service, --profile production)
- Helm chart scaffold (deploy/helm/openthreads/) with deployment, service,
  ingress, secret, HPA, serviceaccount templates
- create-openthreads scaffold package (bunx create-openthreads <name>)

Observability:
- Structured JSON logger with configurable LOG_LEVEL + LOG_FORMAT env vars
- Prometheus-compatible metrics endpoint (GET /api/metrics) with counters
  for messages in/out, A2H intents, fanout, HTTP requests, active threads
- OpenTelemetry tracing setup in instrumentation.ts (enabled via
  OTEL_EXPORTER_OTLP_ENDPOINT, gracefully skipped if SDK not installed)

A2H Layer 2 preparation:
- Stub types for Layer 2 intents: POLICY, REVOKE, DELEGATE, SCOPE
- isLayer1Intent() / isLayer2Intent() type guards
- IntentHandlerRegistry extension point in reply-engine/intent-handler.ts
  for registering custom handlers (Layer 2 or overrides)
- Export all new types from @openthreads/core

Documentation:
- docs/self-hosting.md (Docker, env vars, MongoDB, production checklist)
- docs/adapter-authoring.md (ChannelAdapter interface guide)
- docs/channels/slack.md, docs/channels/telegram.md

Examples:
- examples/plain-http/ — minimal Bun webhook consumer with cURL snippets
- examples/n8n/ — n8n webhook node integration guide
- examples/langgraph/ — LangGraph agent with A2H AUTHORIZE flow (Python)

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
@claude claude Bot mentioned this pull request Apr 11, 2026
16 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Meta: Implementation Roadmap

1 participant