Skip to content

feat: Platform bot linking API for multi-platform CoPilot#12615

Draft
Bentlybro wants to merge 12 commits intodevfrom
feat/platform-bot-linking
Draft

feat: Platform bot linking API for multi-platform CoPilot#12615
Bentlybro wants to merge 12 commits intodevfrom
feat/platform-bot-linking

Conversation

@Bentlybro
Copy link
Copy Markdown
Member

Summary

Adds the account linking system that enables CoPilot to work across multiple chat platforms (Discord, Telegram, Slack, Teams, WhatsApp, GitHub, Linear) using the Vercel Chat SDK.

Problem

To deploy CoPilot as bots on Discord, Telegram, Slack, Teams, etc., we need a way to map platform user identities to AutoGPT accounts. A Discord user is 353922987235213313, the same person on Telegram is 123456789 — we need to know they're the same AutoGPT user.

Solution

Universal link-back flow

Instead of implementing OAuth per-platform, users link via AutoGPT's existing auth:

  1. User messages the CoPilot bot on any platform
  2. Bot detects they're not linked → generates a one-time token
  3. Bot sends: "Link your account: https://platform.agpt.co/link/{token}"
  4. User clicks → logs in to AutoGPT → link is created
  5. All future messages resolve to their AutoGPT account

This works identically on every platform — no platform-specific OAuth needed.

What's added

Database:

  • PlatformType enum (DISCORD, TELEGRAM, SLACK, TEAMS, WHATSAPP, GITHUB, LINEAR)
  • PlatformLink model — maps (platform, platform_user_id) → AutoGPT user
  • PlatformLinkToken model — one-time link tokens with expiry

API endpoints (/api/platform-linking):

Endpoint Auth Purpose
POST /tokens Bot API key Create a link token
GET /tokens/{token}/status Bot API key Check if linking complete
POST /resolve Bot API key Resolve platform user → AutoGPT user
POST /tokens/{token}/confirm JWT (user) Complete the link
GET /links JWT (user) List my linked platforms
DELETE /links/{link_id} JWT (user) Unlink a platform

Context

This is part of the "bot anywhere" initiative — deploying CoPilot to every major chat platform via Vercel's Chat SDK. The Chat SDK handles all the messaging/transport; this PR handles the identity layer.

TODO (follow-up PRs)

  • Add API key auth for bot-facing endpoints
  • Frontend linking page (/link/{token})
  • Bot service using Chat SDK (separate repo: copilot-multibot)
  • Token cleanup cron job
  • Rate limiting on token creation

Adds the account linking system that enables CoPilot to work across
multiple chat platforms (Discord, Telegram, Slack, Teams, etc.) via
the Vercel Chat SDK.

## What this adds

### Database (Prisma + migration)
- PlatformType enum (DISCORD, TELEGRAM, SLACK, TEAMS, WHATSAPP, GITHUB, LINEAR)
- PlatformLink model - maps platform user IDs to AutoGPT accounts
  - Unique constraint on (platform, platformUserId)
  - One AutoGPT user can link multiple platforms
- PlatformLinkToken model - one-time tokens for the linking flow

### API endpoints (/api/platform-linking)

Bot-facing (called by the bot service):
- POST /tokens - Create a link token for an unlinked platform user
- GET /tokens/{token}/status - Check if linking is complete
- POST /resolve - Resolve platform identity → AutoGPT user ID

User-facing (JWT auth required):
- POST /tokens/{token}/confirm - Complete the link (user logs in first)
- GET /links - List all linked platform identities
- DELETE /links/{link_id} - Unlink a platform identity

## Linking flow
1. User messages bot on Discord/Telegram/etc
2. Bot calls POST /resolve → not linked
3. Bot calls POST /tokens → gets link URL
4. Bot sends user the link
5. User clicks → logs in to AutoGPT → frontend calls POST /confirm
6. Bot detects link on next message (or polls /status)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0a97118-95ce-4dd4-9a3f-c5af886df154

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/platform-bot-linking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added platform/backend AutoGPT Platform - Back end size/xl labels Mar 31, 2026
Switched from backend.data.db.get_prisma() to the standard
PlatformLink.prisma() / PlatformLinkToken.prisma() pattern
used throughout the codebase.
@github-actions github-actions bot added the platform/frontend AutoGPT Platform - Front end label Mar 31, 2026
@Bentlybro
Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer
Copy link
Copy Markdown

Queued a review for PR #12615 at 7778de1.

Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Copy link
Copy Markdown

@autogpt-pr-reviewer autogpt-pr-reviewer bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 8 specialists have reported. Now I have everything I need. Let me compile the final verdict.

PR #12615 — feat: Platform bot linking API for multi-platform CoPilot
Author: Bentlybro | Requested by: ntindle | Files: routes.py (+381), routes_test.py (+25), rest_api.py (+6), migration.sql (+47), schema.prisma (+48), openapi.json (+212)

🎯 Verdict: REQUEST_CHANGES

What This PR Does

Adds a new backend subsystem for linking external chat platform identities (Discord, Telegram, Slack, etc.) to AutoGPT user accounts. This enables a multi-platform CoPilot bot to know which AutoGPT account a chat user belongs to. The flow: bot creates a one-time link token → sends user a URL → user clicks it, logs in, and confirms → the platform identity is now mapped to their account. The PR includes API routes, Prisma schema/migration, and OpenAPI spec updates. No frontend UI is included — that's planned as a follow-up.


Specialist Findings

🛡️ Security 🔴 — Three bot-facing endpoints (POST /tokens, GET /tokens/{token}/status, POST /resolve) ship with zero authentication. The code has a TODO acknowledging this (routes.py:103-108). Any internet caller can: (1) flood the token table via POST /tokens (no rate limiting, no cap per user), (2) enumerate linked users via POST /resolve by probing platform IDs — leaking internal user_id and platform_username, (3) poll any token's status and extract the linked user_id. Additionally, confirm_link_token (routes.py:210-263) performs 5 separate DB calls with no transaction — a race condition on double-click can trigger an unhandled unique constraint violation (500 error instead of clean 409).

  • 🔴 routes.py:98-108 — Unauthenticated bot endpoints: must add API key/service token auth before merge
  • 🔴 routes.py:210-263 — Race condition in confirm flow: no transaction wrapping check-and-consume
  • 🟠 routes.py:181-195/resolve leaks user_id to unauthenticated callers
  • 🟠 routes.py:158-160/tokens/{token}/status leaks user_id without auth
  • 🟠 routes.py:98-140 — Token flooding: unlimited pending tokens per platform user, no rate limit

🏗️ Architecture ⚠️ — Module structure follows the api/features/<name>/ convention correctly. Router registration is consistent. However: (1) Enum duplicationPlatformType is defined in Prisma schema AND as a Python VALID_PLATFORMS set (routes.py:34-41); these will drift when someone adds a platform to Prisma without updating the Python set. Fix: import from prisma.enums. (2) No service layer — other features (otto, mcp) separate routes from data access; this puts Prisma calls directly in handlers. (3) No expired token cleanup mechanism despite having an expiresAt index. (4) Hardcoded link_url base URL (routes.py:139) breaks staging/self-hosted. (5) Redundant @@index([token]) when @unique already creates an index on PlatformLinkToken.token.

  • ⚠️ routes.py:34-41 / schema.prisma — Enum duplication will drift
  • ⚠️ routes.py:139 — Hardcoded https://platform.agpt.co base URL
  • ⚠️ schema.prisma:1349 — Redundant index on token (unique already indexes)

Performance ✅ — All endpoints are O(1) indexed lookups, which is correct. The hot path (POST /resolve, called on every bot message) hits the composite unique index — efficient. Concerns: (1) confirm_link_token does 4 DB round trips without a transaction (also a correctness issue). (2) POST /tokens does a check-then-create (2 round trips) that could be a single upsert. (3) Redundant @@index([token]) doubles write cost on that column. (4) Token table grows unbounded — no TTL/cleanup.

  • ⚠️ schema.prisma:1349 — Remove redundant @@index([token])
  • ⚠️ No token cleanup job — table grows forever

🧪 Testing 🔴 — Only 25 lines of tests covering a single trivial helper (_validate_platform). Zero endpoint tests across all 6 API routes. The codebase has well-established patterns (TestClient + mock_jwt_user fixture + mocked Prisma) in sibling features (chat/routes_test.py, otto/routes_test.py, library/routes_test.py). Missing: happy-path linking flow, expired/used token handling, duplicate link prevention (409s), ownership checks on DELETE, auth enforcement on user-facing endpoints, race condition handling on concurrent confirm. Estimated coverage: ~5% of new code.

  • 🔴 routes_test.py — Only tests _validate_platform, zero endpoint coverage
  • Missing: ~20+ test cases for the 6 endpoints following existing codebase patterns

📖 Quality ⚠️ — Code is clean and well-structured with good module-level docstring explaining the flow. Issues: (1) LinkTokenStatusResponse.status is bare str — should be Literal["pending", "linked", "expired"] (routes.py:67). (2) delete_link returns dict instead of a response model (routes.py:310). (3) platform fields across all models are bare str — should use a shared Literal/enum type. (4) No max_length/min_length on platform_user_id (routes.py:54). (5) Logger uses f-strings instead of lazy %s formatting. (6) OpenAPI operation IDs are auto-generated and ugly (e.g., "getPlatform-linkingCheck if a link token has been consumed").

  • ⚠️ routes.py:67status: str → use Literal
  • ⚠️ routes.py:310-> dict → proper response model
  • ⚠️ routes.py:54 — No input length validation on platform_user_id

📦 Product ⚠️ — The backend API is well-designed for the linking flow, but the feature is unusable end-to-end: the hardcoded link_url points to https://platform.agpt.co/link/{token} which returns 404 because no frontend /link/{token} page exists yet. Error messages are developer-speak, not user-friendly (e.g., exposing internal IDs in 409 responses). The channelId field is stored but never used — hints at an unimplemented callback mechanism. Token expiry of 30 minutes is reasonable but tight for mobile users. No notification after linking completes — the bot must poll.

  • ⚠️ Frontend /link/{token} page missing — link is a dead end today
  • ⚠️ routes.py:120 — Error messages expose internal platform user IDs
  • ⚠️ routes.py:63channelId stored but never used (dead field)

📬 Discussion ✅ — CI passing (25/26 checks, only cosmetic skips). No human reviews submitted yet (reviewDecision: REVIEW_REQUIRED). CodeRabbit skipped (detected draft). Author's PR description explicitly lists follow-up TODOs: API key auth, frontend page, bot service, token cleanup, rate limiting. No linked tracking issues.

🔎 QA ✅ — All new API endpoints are functional and accessible. Live testing confirmed:

  • POST /resolve returns correct responses (200 for valid, 422 for invalid input, 400 for bad platform)
  • POST /tokens creates tokens with proper link_url and expires_at
  • GET /tokens/{token}/status returns pending/404 correctly
  • Auth-protected endpoints (GET /links, POST /confirm, DELETE /links/{id}) properly return 401 when unauthenticated
  • Frontend is completely unaffected — all pages (landing, copilot, library, build, marketplace) load correctly
  • No console errors related to this PR

Landing page
Dashboard after signup
Copilot chat
Library
Build page
Marketplace


Blockers (Must Fix)

  1. routes.py:98-108 — Bot-facing endpoints have NO authentication. POST /tokens, GET /tokens/{token}/status, and POST /resolve are completely open. Anyone can create tokens, enumerate linked users, and extract internal user IDs. The TODO comment is not an acceptable substitute. Add API key auth before merge. (Flagged by: Security 🔴, Architecture, Product, Discussion)

  2. routes.py:210-263 — Race condition in confirm_link_token. Five separate DB calls with no transaction. Concurrent requests can create duplicate links; the unique constraint violation surfaces as an unhandled 500 error. Wrap in a Prisma transaction or use atomic conditional update. (Flagged by: Security 🔴, Performance, Architecture)

  3. routes_test.py — Virtually no test coverage. Only 3 tests for a trivial helper. Zero endpoint tests across 6 routes and 381 lines of new code. The codebase has clear testing patterns that were not followed. Add at minimum: happy-path linking flow, expired/used token handling, duplicate prevention, auth enforcement. (Flagged by: Testing 🔴)

Should Fix (Follow-up OK)

  1. routes.py:34-41 / schema.prisma — Enum duplication. VALID_PLATFORMS Python set will drift from PlatformType Prisma enum. Import from prisma.enums instead. (Architecture, Quality)
  2. routes.py:139 — Hardcoded base URL. https://platform.agpt.co/link/{token} should be configurable via env var. Breaks staging/dev/self-hosted. (Architecture, Product)
  3. schema.prisma:1349 — Redundant @@index([token]). The @unique already creates an index. Remove to avoid doubled write cost. (Performance, Architecture)
  4. routes.py:67 — Loose typing. status: str should be Literal["pending", "linked", "expired"]. Similarly, platform fields across models should use a shared type. (Quality)
  5. routes.py:310delete_link returns dict. Only endpoint without a proper response model. Define DeleteLinkResponse. (Quality)
  6. Token table cleanupPlatformLinkToken rows accumulate forever. Add a periodic cleanup job. (Architecture, Performance)
  7. routes.py:54 — No input length validation on platform_user_id. A trivially long string would be persisted. Add Field(max_length=255, min_length=1). (Quality)
  8. routes.py:98-140 — Token flooding. No cap on pending tokens per (platform, platformUserId). Limit to 1 active token per identity. (Security, Performance)

Risk Assessment

Merge risk: HIGH | Rollback: EASY (new feature, no changes to existing tables/routes)

The core design is sound and the implementation is clean, but the three blockers — unauthenticated security-sensitive endpoints, a race condition, and near-zero test coverage — must be resolved before this merges. The feature is additive with easy rollback (drop the migration, remove the router), which reduces deployment risk.


REVIEW_COMPLETE
PR: #12615
Verdict: REQUEST_CHANGES
Blockers: 3

@github-project-automation github-project-automation bot moved this from 🆕 Needs initial review to 🚧 Needs work in AutoGPT development kanban Mar 31, 2026
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Fixes all blockers and should-fix items from the automated review:

## Blockers fixed

1. **Bot-facing endpoint auth** — Added X-Bot-API-Key header auth for
   all bot-facing endpoints (POST /tokens, GET /tokens/{token}/status,
   POST /resolve). Configurable via PLATFORM_BOT_API_KEY env var.
   Dev mode allows keyless access.

2. **Race condition in confirm_link_token** — Replaced 5 separate DB
   calls with atomic update_many (WHERE token=X AND usedAt=NULL).
   If another request consumed the token first, returns 410 cleanly
   instead of a 500 unique constraint violation.

3. **Test coverage** — Added tests for: Platform enum, bot API key
   auth (valid/invalid/missing), request validation (empty IDs,
   too-long IDs, invalid platforms), response model typing.

## Should-fix items addressed

- **Enum duplication** — Created Python Platform(str, Enum) that
  mirrors the Prisma PlatformType. Used throughout request models
  for validation instead of bare strings.
- **Hardcoded base URL** — Now configurable via PLATFORM_LINK_BASE_URL
  env var. Defaults to https://platform.agpt.co/link.
- **Redundant @@index([token])** — Removed from schema and migration.
  The @unique already creates an index.
- **Literal status type** — LinkTokenStatusResponse.status is now
  Literal['pending', 'linked', 'expired'] not bare str.
- **delete_link returns dict** — Now returns DeleteLinkResponse model.
- **Input length validation** — Added min_length=1, max_length=255
  to platform_user_id and platform_username fields.
- **Token flooding** — Existing pending tokens are invalidated before
  creating a new one (only 1 active token per platform identity).
- **Resolve leaks user info** — Removed platform_username from
  resolve response (only returns user_id + linked boolean).
- **Error messages** — Sanitized to not expose internal IDs.
- **Logger** — Switched from f-strings to lazy %s formatting.
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
@Bentlybro
Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer
Copy link
Copy Markdown

Queued a review for PR #12615 at 781224f.

Copy link
Copy Markdown

@autogpt-pr-reviewer autogpt-pr-reviewer bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 8 specialists have reported. Compiling the final verdict now.


PR #12615 — feat: Platform bot linking API for multi-platform CoPilot
Author: Bentlybro | Requested by: ntindle | Files: routes.py (+442), routes_test.py (+136), rest_api.py (+6), schema.prisma (+50), migration.sql (+44), openapi.json (+236)

🎯 Verdict: REQUEST_CHANGES

What This PR Does

Adds a backend API for linking external chat platform identities (Discord, Telegram, Slack, etc.) to AutoGPT user accounts. A bot creates a one-time link token, the user clicks it to log in and confirm, and the bot can then resolve the user's identity on subsequent messages. The Prisma schema, migration, and OpenAPI spec are all included. No frontend UI yet (backend-only first PR).

Specialist Findings

🛡️ Security ⚠️ — Three significant issues found.
⚠️ routes.py:74 — API key comparison uses Python != (not constant-time). Must use hmac.compare_digest() to prevent timing side-channel attacks on the bot API key.
⚠️ routes.py:56-68verify_bot_api_key is dead code with a stub body (# Check headerreturn None). Never called by any endpoint, but if someone wires it up by mistake, it would pass all requests. Remove entirely.
⚠️ routes.py:60-66, 71-73 — Default deployment (BOT_API_KEY="", ENV=development) has zero bot authentication. An attacker could create tokens, resolve identities, and poll status freely. Needs a loud startup warning or require explicit opt-in for dev bypass.
⚠️ routes.py:275-306 — TOCTOU race between token consumption and link creation. Two concurrent requests with different tokens for the same platform identity could both pass the find_first check; the DB unique constraint catches it but produces an unhandled 500 instead of a clean 409. Wrap in try/except.

🏗️ Architecture ✅ — Clean module structure follows existing backend/api/features/ patterns. Route registration is standard. Dual auth (API key for bot, JWT for user) is well-separated.
⚠️ routes.py:143-145 — Header extraction via Depends(lambda req: ...) is a hack that breaks FastAPI's dependency injection (see QA finding below). Should be a proper Header() dependency or typed function.
⚠️ No expired token cleanup mechanism — PlatformLinkToken rows accumulate forever. The @@index([expiresAt]) suggests cleanup was planned but never implemented.
⚠️ Python Platform enum manually mirrors Prisma PlatformType — adding a platform to one without the other causes runtime failures with no compile-time safety.

Performance ✅ — Indexes are appropriate for the query patterns. Unique constraint on (platform, platformUserId) covers the hot-path /resolve query.
⚠️ routes.py:234-255/resolve is called on every incoming bot message with no caching. A TTL cache (60-300s) would eliminate most DB hits since link status rarely changes.
⚠️ Missing composite index on PlatformLinkToken(platform, platformUserId) — both create_link_token and confirm_link_token query by this pair.
⚠️ routes.py:341-363list_my_links returns all results unbounded. Add pagination as a guardrail.
⚠️ routes.py:245, 160, 303 — Uses find_first where find_unique with the compound key would be more efficient and intentional.

🧪 Testing ⚠️ — Coverage estimated at ~15%. Tests exist for enum values, API key validation, Pydantic models, and response models — but zero route handlers are tested.
⚠️ No tests for: create_link_token, confirm_link_token, get_link_token_status, resolve_platform_user, list_my_links, delete_link
⚠️ No integration tests with TestClient — the broken Depends(lambda req: ...) pattern would have been caught immediately by any API-level test
⚠️ No tests for race condition handling, token expiry, 409 conflicts, or IDOR protection on delete_link
⚠️ routes_test.py:48,83,100 — Class-level imports (from ... import X inside class body, accessed via self.X) are non-idiomatic; use module-level imports

📖 Quality ⚠️ — Good documentation (module docstring, handler docstrings, schema comments). Pydantic models well-typed with descriptions.
⚠️ routes.py:56-70 — Dead verify_bot_api_key function alongside working _check_bot_api_key is confusing. Two auth functions with overlapping names but different approaches.
⚠️ routes.py:143-145# noqa: ARG005 on lambda dependencies signals a code smell — the lambda hack requires lint suppression.
⚠️ routes.py:49LINK_BASE_URL has no trailing-slash guard; f"{LINK_BASE_URL}/{token}" would produce double-slash if configured with trailing /.
⚠️ OpenAPI spec has spurious "name": "req", "in": "query", "required": true parameters on bot endpoints — a client-facing bug from the lambda Depends pattern.

📦 Product ✅ — The linking flow follows industry-standard patterns (similar to Slack OAuth linking). Token expiry of 30 minutes is reasonable. One-to-many (user → platforms) and one-to-one (platform identity → user) enforcement is correct. Error messages are user-friendly.
⚠️ channelId is stored in PlatformLinkToken but never used — no completion notification to the user on the platform side after linking. Acceptable as a follow-up.
⚠️ No frontend route for /link/{token} — users clicking the link will 404. Must be documented as a required follow-up.
⚠️ platformUsername has no update mechanism — goes stale if user changes their display name. Consider updating on each /resolve call.

📬 Discussion ✅ — PR is in draft state. One bot review (autogpt-pr-reviewer) requested changes with 3 blockers and 8 should-fix items. Bentlybro addressed most in follow-up commits (7bf10c6, 8a91bd8, 781224f). No human reviews yet. CI passing (26/27 checks, one pending/skipped). No merge conflicts.
⚠️ Still unaddressed from prior bot review: no rate limiting, no token cleanup, and the verify_bot_api_key dead code.

🔎 QA ❌ — CRITICAL BUG: All 3 bot-facing endpoints are non-functional.
🔴 routes.py:143-145, 179-181, 213-215Depends(lambda req: req.headers.get("x-bot-api-key")) does not work. FastAPI interprets the untyped req parameter as a required query parameter, not the Request object. Result: 422 "Field required" without ?req=, and 500 'str' object has no attribute 'headers' with it. POST /tokens, GET /tokens/{token}/status, and POST /resolve are all broken.
✅ User-facing endpoints work correctly: POST /tokens/{token}/confirm (401 without JWT, 404 for invalid tokens), GET /links (returns list, 401 without auth), DELETE /links/{id} (404 for nonexistent, 401 without auth)
✅ Frontend unaffected — landing page, login, signup, copilot dashboard, build page all load normally

landing
copilot-dashboard
copilot-loggedin
build-page

Blockers (Must Fix)

  1. routes.py:143-145, 179-181, 213-215 — All bot-facing endpoints are broken. The Depends(lambda req: req.headers.get("x-bot-api-key")) pattern fails because FastAPI cannot infer req is the Request. Fix:

    from fastapi import Request
    async def get_bot_api_key(request: Request) -> str | None:
        return request.headers.get("x-bot-api-key")

    Then use x_bot_api_key: str | None = Depends(get_bot_api_key) on each endpoint. (Flagged by QA ❌, Architecture 🏗️, Quality 📖)

  2. routes.py:74 — API key comparison must use hmac.compare_digest(request_api_key, BOT_API_KEY) instead of != to prevent timing side-channel attacks. (Flagged by Security 🛡️)

  3. routes.py:56-68 — Remove the dead verify_bot_api_key function entirely. It has a broken stub body and is never called, but could be mistakenly wired up as an auth dependency that passes everything. (Flagged by Security 🛡️, Architecture 🏗️, Quality 📖, Product 📦)

  4. routes.py:275-306 — Wrap the PlatformLink.prisma().create() in a try/except for UniqueConstraintViolation to handle the TOCTOU race gracefully (return 409 instead of 500). (Flagged by Security 🛡️, Architecture 🏗️, Performance ⚡)

Should Fix (Follow-up OK)

  1. routes.py:234-255 — Add a TTL cache on resolve_platform_user — this is called on every bot message and hits the DB each time.
  2. routes_test.py — Add integration tests for route handlers using TestClient. The lambda Depends bug would have been caught by any API-level test. At minimum: happy path for create/confirm/resolve/list/delete.
  3. migration.sql — Add composite index PlatformLinkToken(platform, platformUserId) for the queries in create_link_token and confirm_link_token.
  4. routes.py:341-363 — Add pagination to list_my_links (take/skip or cursor-based).
  5. routes.py:60-66, 71-73 — Add startup warning when BOT_API_KEY is empty, or require explicit DISABLE_BOT_AUTH=true for dev mode.
  6. Add a periodic cleanup job for expired PlatformLinkToken rows.
  7. Document the frontend /link/{token} route as a required follow-up in the PR description.
  8. openapi.json — The spurious "req" query parameter on bot endpoints will confuse API clients and generated SDKs. Resolves automatically when blocker #1 is fixed.

Risk Assessment

Merge risk: HIGH — Core feature endpoints (bot-facing) are non-functional due to the FastAPI dependency injection bug. If merged as-is, no bot could use this API.
Rollback: EASY — New feature with no changes to existing code paths. Drop migration, remove router, done.


REVIEW_COMPLETE
PR: #12615
Verdict: REQUEST_CHANGES
Blockers: 4

Adds two bot-facing endpoints that let the bot service call CoPilot
on behalf of linked users without needing their JWT:

- POST /api/platform-linking/chat/session — create a CoPilot session
- POST /api/platform-linking/chat/stream — send message + stream SSE response

Both authenticated via X-Bot-API-Key header. The bot passes user_id
from the platform-linking resolve step. Backend reuses the existing
CoPilot pipeline (enqueue_copilot_turn → Redis stream → SSE).

This is the last missing piece — the bot can now:
1. Resolve platform user → AutoGPT account (existing)
2. Create a chat session (new)
3. Stream CoPilot responses back to the user (new)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
1. **Broken bot auth (CRITICAL)** — Replaced `Depends(lambda req: ...)`
   with proper `async def get_bot_api_key(request: Request)` dependency.
   The lambda pattern caused FastAPI to interpret `req` as a required
   query parameter, making all bot endpoints return 422.

2. **Timing-safe key comparison** — Switched from `!=` to
   `hmac.compare_digest()` to prevent timing side-channel attacks.

3. **Removed dead code** — Deleted the unused `verify_bot_api_key`
   function that had a stub body passing all requests.

4. **TOCTOU race in confirm** — Wrapped `PlatformLink.prisma().create()`
   in try/except for unique constraint violations. Concurrent requests
   with different tokens for the same identity now get a clean 409
   instead of an unhandled 500.

Also: regenerated openapi.json (removes spurious `req` query parameter
that was leaking from the broken lambda pattern).
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
@Bentlybro
Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer
Copy link
Copy Markdown

Queued a review for PR #12615 at a268291.

@github-actions github-actions bot added the conflicts Automatically applied to PRs with merge conflicts label Mar 31, 2026
@github-actions
Copy link
Copy Markdown
Contributor

This pull request has conflicts with the base branch, please resolve those so we can evaluate the pull request.

Copy link
Copy Markdown

@autogpt-pr-reviewer autogpt-pr-reviewer bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 8 specialists have reported. Compiling the final verdict now.


PR #12615 — feat: Platform bot linking API for multi-platform CoPilot
Author: Bentlybro | SHA: a268291 | Status: Draft → dev
Files: routes.py (+628), routes_test.py (+136), rest_api.py (+6), schema.prisma (+47), migration.sql (+44), openapi.json (+274)

🎯 Verdict: REQUEST_CHANGES

What This PR Does

Adds a backend API and database schema for linking external chat platform identities (Discord, Telegram, Slack, Teams, WhatsApp, GitHub, Linear) to AutoGPT user accounts. The flow: a CoPilot bot encounters an unlinked user → generates a one-time token → user clicks a link to confirm → bot can then proxy chat requests as that user. Includes 8 API endpoints (token CRUD, identity resolution, link management, chat proxy) and two new Prisma models (PlatformLink, PlatformLinkToken). Backend-only — no frontend link page exists yet.


Specialist Findings

🛡️ Security ⛔ — Two critical issues found. The bot chat proxy (/chat/stream, /chat/session) accepts any user_id and only checks that the user has some platform link — a single compromised BOT_API_KEY grants impersonation of every linked user across all platforms (routes.py:443-460). Worse, the dev-mode auth bypass fires when PLATFORM_BOT_API_KEY is unset because ENV defaults to "development" (routes.py:57-63) — meaning all bot endpoints are completely unauthenticated if ops forgets one env var. Additionally: no rate limiting on token creation or resolve (enables DB flooding and user enumeration), /tokens/{token}/status leaks internal user_id to any bot-key holder, and TOCTOU race in confirm_link_token can burn a token without creating a link.

🏗️ Architecture ⚠️ — The linking endpoints are clean and self-contained, following existing feature module patterns. However, the chat proxy (/chat/stream) is a coupling bomb — it inline-imports 6 symbols from backend.copilot internals and reimplements the full CoPilot SSE path including Redis stream subscriptions, keepalives, and turn lifecycle (routes.py:395-530). If CoPilot streaming evolves, this breaks silently. The Platform Python enum must stay manually synchronized with Prisma's PlatformType — no codegen, no validation test, guaranteed future desync. Token invalidation via usedAt = now() conflates "cancelled" with "redeemed," causing get_link_token_status to return status: "linked", user_id: null for invalidated tokens — a semantic bug.
⚠️ Chat proxy should be extracted to a dedicated module or use a CoPilot service facade rather than reaching into internals.

Performance ⚠️ — The hot-path /resolve endpoint is optimal (hits unique index on PlatformLink(platform, platformUserId) — single indexed lookup per message). However, PlatformLinkToken is missing a composite index on (platform, platformUserId) — the update_many in create_link_token and find_first in get_link_token_status will do sequential scans, worsening as the table grows unbounded (no cleanup mechanism for expired tokens). confirm_link_token does 4 sequential DB round-trips where 3 would suffice (the find_first before create is redundant given the unique constraint catch). SSE stream has no maximum connection duration — stuck responses hold connections indefinitely.
⚠️ schema.prisma:PlatformLinkToken — add @@index([platform, platformUserId])

🧪 Testing ⛔ — Only ~15% of the 628 new lines are covered. The test file validates enums, Pydantic models, and API key checking — zero endpoint-level tests. No tests for: the confirm flow (race conditions, expiry, double-click), the delete authorization check (IDOR), chat proxy endpoints, token expiry rejection, or the production-mode 503 branch when ENV != "development". The repo has established patterns (chat/routes_test.py, otto/routes_test.py) for TestClient-based endpoint testing that this PR should follow. The TestResponseModels class tests that True == True — nearly zero value.

📖 Quality ✅ — Well-structured module with clear section separators and thorough header docstring. Minor items: get_bot_api_key name suggests retrieval not extraction; 5 response models lack docstrings while request models have them; OpenAPI operation IDs are auto-generated sentence slugs (e.g., "postPlatform-linkingCreate a copilot session for a linked user (bot-facing)") — should use explicit operation_id for SDK codegen. Redundant string check at routes.py:277 ("Unique" in str(exc) is already covered by .lower()). Inline imports in bot_chat_stream are intentional but undocumented.

📦 Product ⚠️ — The API returns a link_url pointing to https://platform.agpt.co/link/{token} but no frontend /link/{token} page exists — users clicking the link get a 404 on day one. Error messages are developer-facing with no recovery guidance (e.g., "Token not found" with no "request a new link" prompt). The chat proxy grants the bot indefinite impersonation access with no consent disclosure during linking, no visibility into bot activity, and no easy revoke (the settings UI doesn't exist). PLATFORM_LINK_BASE_URL defaults to production — staging/dev will generate links pointing at prod.

📬 Discussion ✅ — PR is in Draft state, no human reviews submitted (reviewDecision: REVIEW_REQUIRED). Two automated reviews from autogpt-pr-reviewer bot: Round 1 flagged 3 blockers + 8 should-fix items → all addressed in commit 7bf10c6. Round 2 flagged 4 new blockers (broken Depends(lambda), timing attack on key comparison, dead function, TOCTOU race) → all addressed in commit a268291. Author is highly responsive, turning around fixes within 30-60 minutes. CI: 26/27 checks passing (only chromatic skipped — not required). No merge conflicts. Third review round triggered but not yet completed.

🔎 QA ✅ — All 8 new API endpoints are live and correctly serving. Full endpoint-by-endpoint validation confirmed: token creation returns proper response shape, status polling works, auth-protected endpoints return 401 without JWT, input validation rejects invalid platforms/empty IDs/oversized fields. The complete linking flow (create → status → resolve → confirm-requires-auth) works as designed. Frontend is fully functional with no regressions — landing, signup, onboarding, copilot, build, and library pages all render correctly.

Landing page
Copilot dashboard
Swagger - platform linking endpoints


Blockers (Must Fix)

  1. routes.py:57-63 — Dev-mode auth bypass defaults to unauthenticated in production. If PLATFORM_BOT_API_KEY is unset and ENV isn't explicitly set, all bot endpoints are wide open. Invert the default: reject when no key is configured, or require explicit ENV=development opt-in. (Security + Architect cross-reference)

  2. routes.py:443-460 — Bot can impersonate any linked user with a single API key. /chat/stream and /chat/session accept arbitrary user_id without verifying the caller's platform scope. Require platform + platform_user_id in BotChatRequest and verify the resolved link matches. (Security)

  3. routes_test.py — Near-zero endpoint test coverage for 628 lines of route logic. No tests for confirm flow, delete authorization (IDOR), chat proxy, token expiry, or production-mode 503. Must add endpoint-level tests following the repo's established TestClient pattern before merge. (Testing)

Should Fix (Follow-up OK)

  1. schema.prisma:PlatformLinkToken — Add @@index([platform, platformUserId]). Multiple queries filter on this pair but only token and expiresAt are indexed. Sequential scans will degrade as the table grows unbounded. (Performance + Architect cross-reference)

  2. routes.py:395-530 — Extract chat proxy from platform_linking module. The /chat/stream endpoint couples tightly to 6 CoPilot internals. Should use a service facade or live in a dedicated copilot_proxy module. (Architect)

  3. routes.py:151-157 — Token invalidation conflates "cancelled" with "used." Setting usedAt on invalidated tokens causes get_link_token_status to report status: "linked" with user_id: null. Add a status field or use invalidatedAt. (Architect + Quality)

  4. No TTL/cleanup for PlatformLinkToken table. Expired tokens accumulate forever. Add a periodic cleanup job. (Performance + Architect)

  5. routes.py:36PLATFORM_LINK_BASE_URL defaults to production URL. Staging/dev environments will generate broken links pointing at prod. Consider requiring explicit config or deriving from request host. (Product)

  6. No rate limiting on POST /tokens and POST /resolve. Enables DB flooding and user enumeration with a compromised key. (Security)

Risk Assessment

Merge risk: HIGH — The dev-mode auth bypass (#1) is a production security hole if env vars aren't perfectly configured. The impersonation scope issue (#2) means a single compromised key compromises all users.
Rollback: EASY — New feature behind new endpoints, no changes to existing functionality. Drop the migration to remove tables.


REVIEW_COMPLETE
PR: #12615
Verdict: REQUEST_CHANGES
Blockers: 3

Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
…outes, chat_proxy

- Split 628-line routes.py into 4 files under ~300 lines each:
  models.py (Pydantic models), auth.py (bot API key validation),
  routes.py (linking routes), chat_proxy.py (bot chat endpoints)
- Move all inline imports to top-level
- Read PLATFORM_BOT_API_KEY per-request instead of module-level global
- Read PLATFORM_LINK_BASE_URL per-request in create_link_token
- Use backend Settings().config.enable_auth for dev-mode bypass
  instead of raw ENV env var
- Add Path validation (max_length=64, regex) on token path params
- Update test imports to match new module paths
Bentlybro added a commit that referenced this pull request Apr 1, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Apr 1, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Apr 1, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicts Automatically applied to PRs with merge conflicts platform/backend AutoGPT Platform - Back end platform/frontend AutoGPT Platform - Front end size/xl

Projects

Status: 🚧 Needs work
Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant