Skip to content

fix(platform/copilot): bypass Vercel SSE proxy, refactor hook architecture#12210

Closed
0ubbe wants to merge 10 commits intodevfrom
fix/copilot-stream-robustness
Closed

fix(platform/copilot): bypass Vercel SSE proxy, refactor hook architecture#12210
0ubbe wants to merge 10 commits intodevfrom
fix/copilot-stream-robustness

Conversation

@0ubbe
Copy link
Contributor

@0ubbe 0ubbe commented Feb 26, 2026

Summary

Reliability and architecture improvements for the CoPilot SSE streaming pipeline.

Frontend

  • SSE proxy bypass: Connect directly to the Python backend for SSE streams, avoiding the Next.js serverless proxy and its 800s Vercel function timeout ceiling
  • Hook refactor: Decompose the 490-line useCopilotPage monolith into focused domain modules:
    • helpers.ts — pure functions (deduplicateMessages, resolveInProgressTools)
    • store.ts — Zustand store for shared UI state (sessionToDelete, drawer open/close)
    • useCopilotStream.ts — SSE transport, useChat wrapper, reconnect/resume logic, stop+cancel
    • useCopilotPage.ts — thin orchestrator (~160 lines)
  • ChatSidebar consolidation: Reads sessionToDelete from Zustand store instead of duplicating delete state/mutation locally
  • Auth error handling: getAuthHeaders() throws on failure instead of silently returning empty headers; 401 errors show user-facing toast
  • Stale closure fix: Use refs for reconnect guards to avoid stale closures during rapid reconnect cycles
  • Session switch resume: Clear hasResumedRef on session switch so returning to a session with an active stream auto-reconnects
  • Target session cache invalidation: Invalidate the target session's React Query cache on switch so active_stream is accurate for resume
  • Dedup hardening: Content-fingerprint dedup resets on non-assistant messages, preventing legitimate repeated responses from being dropped
  • Marker prefixes: Hex-suffixed markers ([__COPILOT_ERROR_f7a1__]) to prevent LLM false-positives

Backend (minimal)

  • Faster heartbeat: 10s → 3s interval to keep SSE alive through proxies/LBs
  • Faster stall detection: SSE subscriber queue timeout 30s → 10s
  • Marker prefixes: Matching hex-suffixed prefixes for error/system markers

Test plan

  • Verify SSE streams connect directly to backend (no Next.js proxy in network tab)
  • Verify reconnect works on transient disconnects (up to 3 attempts with backoff)
  • Verify auth failure shows user-facing toast
  • Verify switching sessions and switching back shows messages and resumes active stream
  • Verify deleting a chat from sidebar works (shared Zustand state)
  • Verify mobile drawer delete works (shared Zustand state)
  • Verify marker prefix parsing still works with hex-suffixed prefixes
  • pnpm format && pnpm lint && pnpm types pass

🤖 Generated with Claude Code

@0ubbe 0ubbe requested a review from a team as a code owner February 26, 2026 12:00
@0ubbe 0ubbe removed the request for review from a team February 26, 2026 12:00
@github-project-automation github-project-automation bot moved this to 🆕 Needs initial review in AutoGPT development kanban Feb 26, 2026
@0ubbe 0ubbe requested review from Pwuts and Swiftyos February 26, 2026 12:00
@github-actions github-actions bot added platform/frontend AutoGPT Platform - Front end platform/backend AutoGPT Platform - Back end labels Feb 26, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Added timing and robustness to Copilot SDK streaming and transcript handling, introduced partial-message streaming support, tightened SSE heartbeat timeout, switched frontend SSE to direct backend with JWT auth and improved reconnect/dedup logic, and updated marker parsing to use escaped hex-suffixed prefixes.

Changes

Cohort / File(s) Summary
Backend SDK service & config
autogpt_platform/backend/backend/copilot/sdk/service.py, autogpt_platform/backend/backend/copilot/config.py
Added timing instrumentation and heartbeat logs, moved stream lock release to a finally block, added transcript download timeout handling (config: transcript_download_timeout), and improved resume/transcript flow and SDK options (include_partial_messages).
Backend streaming & response adapter
autogpt_platform/backend/backend/api/features/chat/routes.py, autogpt_platform/backend/backend/copilot/sdk/response_adapter.py
Reduced SSE wait timeout from 30s→10s for heartbeats; added handling of StreamEvent messages to enable incremental partial-message streaming and internal state tracking for streamed text.
SDK tests
autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
Updated tests to expect include_partial_messages in ClaudeAgentOptions fields.
Frontend SSE, reconnect & dedup
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
Switched SSE transport to direct backend URL using fresh JWT Authorization headers, refactored reconnect state to refs, adjusted exponential-backoff formula, added auth-failure handling to stop reconnects, and improved message deduplication fingerprinting.
Frontend marker parsing
autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
Replaced marker prefixes with hex-suffixed variants, added escapeRegExp helper, precompiled regexes for markers, and updated parseSpecialMarkers to use them.
Frontend legacy route docs/export
autogpt_platform/frontend/src/app/api/chat/sessions/[sessionId]/stream/route.ts
Added LEGACY SSE PROXY documentation block and exported maxDuration = 800 (no runtime behavior change).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Frontend Client
    participant Auth as Auth Service
    participant Backend as Python Backend (SSE)

    Note over Client,Backend: Direct SSE with JWT auth and reconnection
    Client->>Auth: request fresh JWT (getWebSocketToken)
    Auth-->>Client: JWT token
    Client->>Backend: SSE request (Authorization: Bearer <token>)
    Backend-->>Client: SSE stream events / heartbeats

    loop Reconnect (exponential backoff)
        Client->>Auth: request fresh JWT
        Auth-->>Client: JWT token
        Client->>Backend: resume SSE (Authorization header)
        Backend-->>Client: continue stream
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Swiftyos
  • Pwuts

Poem

🐰 I hopped through logs with stopwatch bright,

I held the lock and timed the night.
I fetched a JWT and tried again,
streamed bits of text like carrot rain,
now code and rabbit both delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main architectural changes: bypassing the Vercel SSE proxy and refactoring the hook structure, which are primary focuses throughout the changeset.
Description check ✅ Passed The description is comprehensive and directly aligned with the changeset, covering frontend SSE proxy bypass, hook refactoring, authentication handling, backend streaming improvements, and marker prefixes across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/copilot-stream-robustness

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.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

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

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

Comment on lines +86 to +88
// Only dedup if this assistant message is identical to the previous one
if (fingerprint && fingerprint === lastAssistantFingerprint) return false;
if (fingerprint) lastAssistantFingerprint = fingerprint;

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch — this was a real bug. Fixed in commit 1ad10bf: the fingerprint tracker now resets when a non-assistant message is encountered, so [assistant: "Done!", user: "thanks", assistant: "Done!"] correctly keeps both assistant messages.

//
// This route is kept as a fallback / for backwards compatibility.
// See useCopilotPage.ts for the direct SSE transport implementation.
// ---------------------------------------------------------------------------
Copy link
Contributor

Choose a reason for hiding this comment

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

🔥

0ubbe added a commit that referenced this pull request Feb 26, 2026
Fixes false-positive deduplication when identical assistant responses
are separated by user messages (e.g. "Done!" → user → "Done!").
The fingerprint tracker now resets when a non-assistant message is
encountered, so only truly consecutive duplicates are filtered.

Addresses Sentry bot review comment on PR #12210.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot removed the conflicts Automatically applied to PRs with merge conflicts label Feb 26, 2026
@github-actions
Copy link
Contributor

Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly.

Comment on lines +33 to +37
const { token, error } = await getWebSocketToken();
if (error || !token) {
console.warn("[Copilot] Failed to get auth token:", error);
return {};
}

This comment was marked as outdated.

Base automatically changed from fix/copilot-subtask-concurrency-limit to dev February 26, 2026 13:50
@github-actions github-actions bot added the conflicts Automatically applied to PRs with merge conflicts label Feb 26, 2026
@github-actions
Copy link
Contributor

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

- Connect directly to Python backend for SSE, bypassing the Next.js
  serverless proxy and its 800s Vercel function timeout
- Add content-fingerprint deduplication that resets on non-assistant
  messages, preventing false removal of legitimately repeated responses
- Throw on auth failure in getAuthHeaders() instead of silently
  returning empty headers (fixes Sentry HIGH severity)
- Use refs instead of state for reconnect attempt counter to avoid
  stale closures in rapid reconnect cycles
- Add hex-suffixed marker prefixes to prevent LLM false-positives
- Mark SSE proxy route as legacy with fallback comment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@0ubbe 0ubbe force-pushed the fix/copilot-stream-robustness branch from ff9437b to 3d172e7 Compare February 26, 2026 15:12
@github-actions github-actions bot removed the conflicts Automatically applied to PRs with merge conflicts label Feb 26, 2026
@github-actions
Copy link
Contributor

Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🔴 Merge Conflicts Detected

The following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.

  • Fix CoPilot stop button by cancelling backend chat stream #12116 (Deeven-Seru · updated 2d ago)

    • 📁 autogpt_platform/
      • backend/Dockerfile (3 conflicts, ~76 lines)
      • backend/backend/api/features/builder/db.py (2 conflicts, ~8 lines)
      • backend/backend/api/features/chat/model_test.py (deleted here, modified there)
      • backend/backend/api/features/chat/routes.py (9 conflicts, ~433 lines)
      • backend/backend/api/features/chat/service_test.py (deleted here, modified there)
      • backend/backend/api/features/chat/tools/find_block_test.py (deleted here, modified there)
      • backend/backend/api/features/chat/tools/run_block_test.py (deleted here, modified there)
      • backend/backend/api/features/chat/tools/workspace_files.py (deleted here, modified there)
      • backend/backend/api/features/library/db.py (1 conflict, ~19 lines)
      • backend/backend/copilot/config.py (2 conflicts, ~9 lines)
      • backend/backend/copilot/model.py (10 conflicts, ~400 lines)
      • backend/backend/copilot/response_model.py (1 conflict, ~5 lines)
      • backend/backend/copilot/service.py (3 conflicts, ~376 lines)
      • backend/backend/copilot/stream_registry.py (4 conflicts, ~130 lines)
      • backend/backend/copilot/tools/__init__.py (1 conflict, ~4 lines)
      • backend/backend/copilot/tools/agent_generator/dummy.py (2 conflicts, ~33 lines)
      • backend/backend/copilot/tools/agent_generator/service.py (2 conflicts, ~12 lines)
      • backend/backend/copilot/tools/bash_exec.py (1 conflict, ~20 lines)
      • backend/backend/copilot/tools/check_operation_status.py (added there)
      • backend/backend/copilot/tools/feature_requests.py (9 conflicts, ~79 lines)
      • backend/backend/copilot/tools/feature_requests_test.py (8 conflicts, ~59 lines)
      • backend/backend/copilot/tools/find_block.py (1 conflict, ~6 lines)
      • backend/backend/copilot/tools/models.py (3 conflicts, ~37 lines)
      • backend/backend/copilot/tools/run_block.py (1 conflict, ~14 lines)
      • backend/backend/copilot/tools/sandbox.py (3 conflicts, ~24 lines)
      • backend/backend/copilot/tools/test_run_block_details.py (4 conflicts, ~20 lines)
      • backend/backend/copilot/tools/web_fetch.py (1 conflict, ~18 lines)
      • backend/backend/util/settings.py (1 conflict, ~5 lines)
      • backend/backend/util/test.py (1 conflict, ~4 lines)
      • backend/poetry.lock (3 conflicts, ~23 lines)
      • backend/pyproject.toml (1 conflict, ~5 lines)
      • frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/Block.tsx (1 conflict, ~8 lines)
      • frontend/src/app/(platform)/build/components/legacy-builder/Flow/Flow.tsx (deleted here, modified there)
      • frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx (4 conflicts, ~51 lines)
      • frontend/src/app/(platform)/copilot/tools/CreateAgent/CreateAgent.tsx (6 conflicts, ~181 lines)
      • frontend/src/app/(platform)/copilot/tools/GenericTool/GenericTool.tsx (3 conflicts, ~813 lines)
      • frontend/src/app/(platform)/copilot/tools/RunBlock/RunBlock.tsx (2 conflicts, ~11 lines)
      • frontend/src/app/(platform)/copilot/useCopilotPage.ts (3 conflicts, ~114 lines)
      • frontend/src/app/api/chat/sessions/[sessionId]/stream/route.ts (1 conflict, ~27 lines)
      • frontend/src/app/api/chat/tasks/[taskId]/stream/route.ts (deleted here, modified there)
      • frontend/src/app/api/openapi.json (3 conflicts, ~29 lines)
      • frontend/src/app/globals.css (1 conflict, ~8 lines)
      • frontend/src/tests/pages/build.page.ts (1 conflict, ~15 lines)
  • feat(platform): Add file upload to copilot chat [SECRT-1788] #12220 (ntindle · updated 1d ago)

    • 📁 autogpt_platform/frontend/src/app/(platform)/copilot/
      • useCopilotPage.ts (3 conflicts, ~243 lines)
  • feat(backend/copilot): async polling for agent-generator + SSE auto-reconnect #12199 (majdyz · updated 3d ago)

    • 📁 autogpt_platform/frontend/src/app/(platform)/copilot/
      • useCopilotPage.ts (1 conflict, ~200 lines)
  • fix(copilot): prevent double output from StreamFinish/mark_task_completed race #12195 (Otto-AGPT · updated 1d ago)

    • 📁 autogpt_platform/frontend/src/app/(platform)/copilot/
      • useCopilotPage.ts (1 conflict, ~171 lines)
  • chore(frontend): Fix react-doctor warnings + add CI job #12163 (0ubbe · updated 1d ago)

    • 📁 autogpt_platform/frontend/src/app/(platform)/copilot/
      • components/ChatMessagesContainer/ChatMessagesContainer.tsx (1 conflict, ~6 lines)
      • tools/RunAgent/components/AgentDetailsCard/AgentDetailsCard.tsx (2 conflicts, ~119 lines)
  • feat(copilot): render context compaction as tool-call UI events #12250 (Otto-AGPT · updated 19m ago)

    • 📁 autogpt_platform/backend/backend/copilot/sdk/
      • service.py (1 conflict, ~9 lines)

🟢 Low Risk — File Overlap Only

These PRs touch the same files but different sections (click to expand)

Summary: 6 conflict(s), 0 medium risk, 5 low risk (out of 11 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

@0ubbe 0ubbe force-pushed the fix/copilot-stream-robustness branch from afd9ec9 to 2ecc588 Compare February 27, 2026 12:54
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts (1)

255-269: Consider consistent case handling for auth error detection.

The auth error checks mix case-sensitive and case-insensitive comparisons:

  • Lines 258-260 use exact case matching
  • Line 261 uses .toLowerCase() for "401"

This could miss lowercase variants like "unauthorized" from some HTTP clients/backends.

♻️ Optional: Consistent lowercase comparison
+      const lowerMessage = error.message.toLowerCase();
       const isAuthError =
-        error.message.includes("Authentication failed") ||
-        error.message.includes("Unauthorized") ||
-        error.message.includes("Not authenticated") ||
-        error.message.toLowerCase().includes("401");
+        lowerMessage.includes("authentication failed") ||
+        lowerMessage.includes("unauthorized") ||
+        lowerMessage.includes("not authenticated") ||
+        lowerMessage.includes("401");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/app/`(platform)/copilot/useCopilotPage.ts
around lines 255 - 269, The auth error detection in isAuthError mixes
case-sensitive checks with a single lowercased check, so replace the mixed
comparisons by normalizing error.message (e.g., toLowerCase()) and then checking
against lowercase strings ("authentication failed", "unauthorized", "not
authenticated", "401") to ensure consistent matching; update the isAuthError
logic in the useCopilotPage handler that builds the isAuthError variable (and
keep the subsequent toast call for authentication errors) so all comparisons are
case-insensitive and reliable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@autogpt_platform/frontend/src/app/`(platform)/copilot/useCopilotPage.ts:
- Around line 255-269: The auth error detection in isAuthError mixes
case-sensitive checks with a single lowercased check, so replace the mixed
comparisons by normalizing error.message (e.g., toLowerCase()) and then checking
against lowercase strings ("authentication failed", "unauthorized", "not
authenticated", "401") to ensure consistent matching; update the isAuthError
logic in the useCopilotPage handler that builds the isAuthError variable (and
keep the subsequent toast call for authentication errors) so all comparisons are
case-insensitive and reliable.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between afd9ec9 and 2ecc588.

📒 Files selected for processing (4)
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
  • autogpt_platform/frontend/src/app/api/chat/sessions/[sessionId]/stream/route.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/api/chat/sessions/[sessionId]/stream/route.ts
  • autogpt_platform/backend/backend/copilot/sdk/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: Check PR Status
  • GitHub Check: end-to-end tests
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (10)
autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Use Node.js 21+ with pnpm package manager for frontend development
Always run 'pnpm format' for formatting and linting code in frontend development

autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Run pnpm format to auto-fix formatting issues before completing work
Run pnpm lint to check for lint errors and fix any that appear before completing work

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/frontend/**/*.{tsx,ts}: Use function declarations for components and handlers (not arrow functions) in React components
Only use arrow functions for small inline lambdas (map, filter, etc.) in React components
Use PascalCase for component names and camelCase with 'use' prefix for hook names in React
Use Tailwind CSS utilities only for styling in frontend components
Use design system components from 'src/components/' (atoms, molecules, organisms) in frontend development
Never use 'src/components/legacy/' in frontend code
Only use Phosphor Icons (@phosphor-icons/react) for icons in frontend components
Use generated API hooks from '@/app/api/generated/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/
'
Use React Query for server state (via generated hooks) in frontend development
Default to client components ('use client') in Next.js; only use server components for SEO or extreme TTFB needs
Use '' component for rendering errors in frontend UI; use toast notifications for mutation errors; use 'Sentry.captureException()' for manual exceptions
Separate render logic from data/behavior in React components; keep comments minimal (code should be self-documenting)

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/frontend/**/*.{ts,tsx}: No barrel files or 'index.ts' re-exports in frontend code
Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development

Run pnpm types to check for type errors and fix any that appear before completing work

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}: Format frontend code using pnpm format
Never use components from src/components/__legacy__/*

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

autogpt_platform/frontend/src/**/*.{ts,tsx}: Structure components as ComponentName/ComponentName.tsx + useComponentName.ts + helpers.ts and use design system components from src/components/ (atoms, molecules, organisms)
Use generated API hooks from @/app/api/__generated__/endpoints/ with pattern use{Method}{Version}{OperationName} and regenerate with pnpm generate:api
Use function declarations (not arrow functions) for components and handlers
Separate render logic from business logic with component.tsx + useComponent.ts + helpers.ts structure
Colocate state when possible, avoid creating large components, use sub-components in local /components folder
Avoid large hooks, abstract logic into helpers.ts files when sensible
Use arrow functions only for callbacks, not for component declarations
Avoid comments at all times unless the code is very complex
Do not use useCallback or useMemo unless asked to optimize a given function

autogpt_platform/frontend/src/**/*.{ts,tsx}: Use function declarations (not arrow functions) for components and handlers
Use type-safe generated API hooks via Orval + React Query for data fetching
Use React Query for server state management and co-locate UI state in components/hooks
Separate render logic (.tsx) from business logic (use*.ts hooks)
Use only shadcn/ui (Radix UI primitives) with Tailwind CSS for UI components
Use Phosphor Icons only for all icon implementations
Use ErrorCard component for render errors, toast for mutations, and Sentry for exceptions
Use design system components from src/components/ (atoms, molecules, organisms)
Never use src/components/__legacy__/* components
Use generated API hooks from @/app/api/__generated__/endpoints/ with pattern use{Method}{Version}{OperationName}
Use Tailwind CSS only for styling with design tokens
Do not use useCallback or useMemo unless asked to optimize a specific function
Never type with any unless a variable/attribute can actually be of any type

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Tailwind CSS only for styling, use design tokens, and use Phosphor Icons only

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not type hook returns, let Typescript infer as much as possible

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never type with any, if no types available use unknown

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)

Fully capitalize acronyms in symbols, e.g. graphID, useBackendAPI

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/use*.ts

📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)

autogpt_platform/frontend/src/**/use*.ts: Extract component logic into custom hooks grouped by concern, with each hook in its own .ts file
Do not type hook returns; let TypeScript infer types as much as possible

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
🧠 Learnings (4)
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/**/*.{tsx,ts} : Use generated API hooks from '@/app/api/__generated__/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/*'

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/src/lib/supabase/middleware.ts : Update 'frontend/lib/supabase/middleware.ts' when adding protected routes in frontend

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
📚 Learning: 2026-02-04T16:50:51.495Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-04T16:50:51.495Z
Learning: Applies to autogpt_platform/frontend/src/app/(platform)/**/*.tsx : If adding protected frontend routes, update `frontend/lib/supabase/middleware.ts`

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/**/*.{ts,tsx} : Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
🧬 Code graph analysis (1)
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts (2)
autogpt_platform/frontend/src/services/environment/index.ts (1)
  • environment (140-166)
autogpt_platform/frontend/src/lib/supabase/actions.ts (1)
  • getWebSocketToken (105-145)
🔇 Additional comments (6)
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts (6)

22-29: LGTM!

The reduced RECONNECT_MAX_ATTEMPTS to 3 is reasonable for faster failure feedback. The getBackendBaseUrl() helper correctly strips the /api suffix to construct the SSE endpoint path.


31-39: LGTM!

Clean implementation that mirrors the WebSocket token pattern. The error message thrown here correctly matches the auth error detection in onError (line 258).


68-97: LGTM!

The revised deduplication strategy is well-designed. By comparing only consecutive assistant messages, it correctly handles:

  • Hydration/stream boundary duplicates (same content, different IDs)
  • Legitimate repeated responses separated by user messages

The empty fingerprint guard on line 87 (fingerprint &&) safely handles edge cases with empty or non-text parts.


181-223: LGTM!

The switch from useState to useRef for reconnectAttemptsRef correctly addresses stale closure reads during rapid reconnect cycles. The backoff calculation 2 ** (nextAttempt - 1) produces the expected progression: 1s → 2s → 4s for attempts 1-3.


331-358: LGTM!

The reconnectAttemptsRef resets are correctly placed:

  • Line 334: Clears on session switch to prevent stale counts
  • Line 354: Clears after successful stream completion to allow fresh reconnect attempts

153-179: Async callbacks are supported by DefaultChatTransport.

The ai SDK explicitly supports PromiseLike return types for prepareSendMessagesRequest and similar prepare methods, so the async calls to getAuthHeaders() work as intended.

@0ubbe 0ubbe changed the title fix(platform): harden SSE reconnection, dedup, and marker robustness fix(platform): copilot stream robustness — bypass proxy, partial streaming, lock contention Feb 27, 2026
…switch resume

- Enable `include_partial_messages` in SDK options for token-by-token
  streaming; add StreamEvent handler in SDKResponseAdapter to convert
  content_block deltas to StreamTextStart/Delta/End
- Reduce heartbeat interval 10s→3s and queue timeout 30s→10s for faster
  stall detection
- Move stream lock release to top of finally block so users can send
  follow-up messages immediately (before slow persist/upload)
- Add transcript_download_timeout (3s) to prevent GCS hangs in local dev
- Add timing instrumentation throughout SDK startup path
- Pre-compile marker regexes to module-level constants
- Use getAGPTServerBaseUrl() instead of custom getBackendBaseUrl()
- Fix stale isReconnectScheduled via ref mirror
- Clear hasResumedRef on session switch so returning to a session with
  an active stream auto-reconnects

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 1117-1122: The unguarded await lock.release() in the finally block
can raise CancelledError and abort the subsequent shielded persistence and
transcript upload; change the release to be cancellation-safe by shielding or
catching cancellation (e.g., use asyncio.shield on lock.release() or wrap await
lock.release() in a try/except asyncio.CancelledError and then call
asyncio.shield(lock.release())/re-run release) so that the shielded persistence
and transcript upload always run; update the code around lock.release() in the
finally block (the await lock.release() call) to use asyncio.shield or a
cancellation-aware pattern so downstream persistence/transcript upload always
executes.

In `@autogpt_platform/frontend/src/app/`(platform)/copilot/useCopilotPage.ts:
- Around line 68-80: The existing dedup logic in useCopilotPage builds a
fingerprint from msg.parts using only text and toolCallId, causing messages with
the same toolCallId but different tool state to be dropped; update the
fingerprint computation (used where msg.role === "assistant" and compared to
lastAssistantFingerprint) to include relevant tool-state fields such as any
part.state, part.output, and part.errorText (in addition to text and toolCallId)
so that changes to tool state produce a different fingerprint and won’t be
filtered out.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2ecc588 and 80d8a60.

📒 Files selected for processing (7)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py
  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Seer Code Review
  • GitHub Check: types
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (python)
  • GitHub Check: end-to-end tests
🧰 Additional context used
📓 Path-based instructions (22)
autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Use Node.js 21+ with pnpm package manager for frontend development
Always run 'pnpm format' for formatting and linting code in frontend development

autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Run pnpm format to auto-fix formatting issues before completing work
Run pnpm lint to check for lint errors and fix any that appear before completing work

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/frontend/**/*.{tsx,ts}: Use function declarations for components and handlers (not arrow functions) in React components
Only use arrow functions for small inline lambdas (map, filter, etc.) in React components
Use PascalCase for component names and camelCase with 'use' prefix for hook names in React
Use Tailwind CSS utilities only for styling in frontend components
Use design system components from 'src/components/' (atoms, molecules, organisms) in frontend development
Never use 'src/components/legacy/' in frontend code
Only use Phosphor Icons (@phosphor-icons/react) for icons in frontend components
Use generated API hooks from '@/app/api/generated/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/
'
Use React Query for server state (via generated hooks) in frontend development
Default to client components ('use client') in Next.js; only use server components for SEO or extreme TTFB needs
Use '' component for rendering errors in frontend UI; use toast notifications for mutation errors; use 'Sentry.captureException()' for manual exceptions
Separate render logic from data/behavior in React components; keep comments minimal (code should be self-documenting)

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/frontend/**/*.{ts,tsx}: No barrel files or 'index.ts' re-exports in frontend code
Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development

Run pnpm types to check for type errors and fix any that appear before completing work

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}: Format frontend code using pnpm format
Never use components from src/components/__legacy__/*

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

autogpt_platform/frontend/src/**/*.{ts,tsx}: Structure components as ComponentName/ComponentName.tsx + useComponentName.ts + helpers.ts and use design system components from src/components/ (atoms, molecules, organisms)
Use generated API hooks from @/app/api/__generated__/endpoints/ with pattern use{Method}{Version}{OperationName} and regenerate with pnpm generate:api
Use function declarations (not arrow functions) for components and handlers
Separate render logic from business logic with component.tsx + useComponent.ts + helpers.ts structure
Colocate state when possible, avoid creating large components, use sub-components in local /components folder
Avoid large hooks, abstract logic into helpers.ts files when sensible
Use arrow functions only for callbacks, not for component declarations
Avoid comments at all times unless the code is very complex
Do not use useCallback or useMemo unless asked to optimize a given function

autogpt_platform/frontend/src/**/*.{ts,tsx}: Use function declarations (not arrow functions) for components and handlers
Use type-safe generated API hooks via Orval + React Query for data fetching
Use React Query for server state management and co-locate UI state in components/hooks
Separate render logic (.tsx) from business logic (use*.ts hooks)
Use only shadcn/ui (Radix UI primitives) with Tailwind CSS for UI components
Use Phosphor Icons only for all icon implementations
Use ErrorCard component for render errors, toast for mutations, and Sentry for exceptions
Use design system components from src/components/ (atoms, molecules, organisms)
Never use src/components/__legacy__/* components
Use generated API hooks from @/app/api/__generated__/endpoints/ with pattern use{Method}{Version}{OperationName}
Use Tailwind CSS only for styling with design tokens
Do not use useCallback or useMemo unless asked to optimize a specific function
Never type with any unless a variable/attribute can actually be of any type

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Tailwind CSS only for styling, use design tokens, and use Phosphor Icons only

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Component props should be interface Props { ... } (not exported) unless the interface needs to be used outside the component

Use type Props = { ... } (not exported) for component props unless used outside the component

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
autogpt_platform/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never type with any, if no types available use unknown

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/app/(platform)/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

If adding protected frontend routes, update frontend/lib/supabase/middleware.ts

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
autogpt_platform/frontend/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)

Fully capitalize acronyms in symbols, e.g. graphID, useBackendAPI

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)

Put sub-components in a local components/ folder within the feature directory

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
autogpt_platform/frontend/src/**/[A-Z]*/**/*.{ts,tsx}

📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)

Structure components as ComponentName/ComponentName.tsx + useComponentName.ts + helpers.ts

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

autogpt_platform/backend/**/*_test.py: Always review snapshot changes with git diff before committing when updating snapshots with poetry run pytest --snapshot-update
Use pytest with snapshot testing for API responses in test files
Colocate test files with source files using the *_test.py naming convention

Files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py
autogpt_platform/backend/**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

Run poetry run test for backend testing (runs pytest with docker based postgres + prisma)

Files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
autogpt_platform/backend/backend/api/features/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Update routes in '/backend/backend/api/features/' and add/update Pydantic models in the same directory for API development

When modifying API routes, update corresponding Pydantic models in the same directory and write tests alongside the route file

Files:

  • autogpt_platform/backend/backend/api/features/chat/routes.py
autogpt_platform/backend/backend/api/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

autogpt_platform/backend/backend/api/**/*.py: Use FastAPI for building REST and WebSocket endpoints
Use JWT-based authentication with Supabase integration

Files:

  • autogpt_platform/backend/backend/api/features/chat/routes.py
autogpt_platform/frontend/src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not type hook returns, let Typescript infer as much as possible

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
autogpt_platform/frontend/src/**/use*.ts

📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)

autogpt_platform/frontend/src/**/use*.ts: Extract component logic into custom hooks grouped by concern, with each hook in its own .ts file
Do not type hook returns; let TypeScript infer types as much as possible

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
🧠 Learnings (8)
📚 Learning: 2026-02-26T10:12:58.845Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12207
File: autogpt_platform/frontend/src/components/ai-elements/conversation.tsx:0-0
Timestamp: 2026-02-26T10:12:58.845Z
Learning: Guideline: Do not apply dark mode CSS classes (e.g., dark:text-*) to copilot UI components until dark mode support is implemented. Applies to all copilot-related components (paths containing /copilot/). When reviewing, search for dark:* class names within copilot components and refactor to use conditional class sets or feature-flag gates, ensuring no dark-mode styles are present in the code paths that render copilot UI unless dark mode support is officially enabled.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
📚 Learning: 2026-02-27T10:45:49.499Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:49.499Z
Learning: Prefer using generated OpenAPI types from '@/app/api/__generated__/' for payloads defined in openapi.json (e.g., MCPToolsDiscoveredResponse, MCPToolOutputResponse). Use inline TypeScript interfaces only for payloads that are SSE-stream-only and not exposed via OpenAPI. Apply this pattern to frontend tool components (e.g., RunMCPTool) and related areas where similar SSE/openapi-discrepancies occur; avoid re-implementing types when a generated type is available.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
📚 Learning: 2026-02-27T15:58:44.424Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:58:44.424Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/**/*.{tsx,ts} : Use generated API hooks from '@/app/api/__generated__/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/*'

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/src/lib/supabase/middleware.ts : Update 'frontend/lib/supabase/middleware.ts' when adding protected routes in frontend

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/**/*.{ts,tsx} : Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
📚 Learning: 2026-02-04T16:50:51.495Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-04T16:50:51.495Z
Learning: Applies to autogpt_platform/frontend/src/app/(platform)/**/*.tsx : If adding protected frontend routes, update `frontend/lib/supabase/middleware.ts`

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
🧬 Code graph analysis (3)
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts (2)
autogpt_platform/frontend/src/lib/supabase/actions.ts (1)
  • getWebSocketToken (105-145)
autogpt_platform/frontend/src/services/environment/index.ts (1)
  • environment (140-166)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/sdk/transcript.py (1)
  • download_transcript (402-453)
autogpt_platform/backend/backend/executor/cluster_lock.py (2)
  • release (118-130)
  • release (239-251)
autogpt_platform/backend/backend/copilot/sdk/response_adapter.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (3)
  • StreamBaseResponse (47-55)
  • StreamStartStep (86-93)
  • StreamTextDelta (116-121)
🔇 Additional comments (7)
autogpt_platform/backend/backend/copilot/config.py (1)

95-99: Config addition is clean and correctly scoped.

Making transcript download timeout configurable here is a good, low-risk change that supports the resilience goal.

autogpt_platform/backend/backend/api/features/chat/routes.py (1)

490-490: Timeout tightening looks appropriate for stream stall recovery.

Line [490] and Line [643] reduce queue wait latency while preserving heartbeat behavior, which matches the PR’s robustness intent.

Also applies to: 643-643

autogpt_platform/backend/backend/copilot/sdk/sdk_compat_test.py (1)

108-108: Good compatibility coverage update.

Including include_partial_messages in the asserted option surface is the right guard for this PR’s partial-streaming dependency.

autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx (1)

34-55: Marker parsing hardening is solid.

Escaped, precompiled marker regexes with distinct hex-suffixed prefixes are a good reliability improvement for streaming text parsing.

Also applies to: 62-63, 71-72

autogpt_platform/backend/backend/copilot/sdk/service.py (1)

605-617: Transcript timeout handling is a good resilience improvement.

The explicit timeout + fallback on download avoids long hangs when storage is unreachable and keeps stream startup predictable.

autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts (1)

23-31: Direct backend transport and ref-based reconnect guards are well implemented.

This is a strong reliability upgrade: explicit JWT headers for backend SSE plus ref-based reconnect state avoids stale-closure reconnect bugs.

Also applies to: 145-167, 173-177

autogpt_platform/backend/backend/copilot/sdk/response_adapter.py (1)

22-23: StreamEvent support is integrated cleanly with existing adapter state.

The event-to-protocol mapping and post-Assistant reset logic are solid, and the duplicate-text guard is a practical fit for partial-message streaming.

Also applies to: 60-64, 83-85, 104-107, 133-135, 249-291

Comment on lines +1117 to +1122
# Release stream lock FIRST to allow new streams for this session.
# Session persist and transcript upload can be slow — releasing the
# lock early prevents "Another stream is already active" errors when
# the user sends a new message immediately after a stream completes.
await lock.release()

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C5 'finally:|await lock\.release\(|asyncio\.shield\(upsert_chat_session|asyncio\.shield\(_try_upload_transcript' autogpt_platform/backend/backend/copilot/sdk/service.py

Repository: Significant-Gravitas/AutoGPT

Length of output: 2650


🏁 Script executed:

cd autogpt_platform/backend/backend/copilot/sdk && grep -n "CancelledError\|shield\|finally:" service.py | head -20

Repository: Significant-Gravitas/AutoGPT

Length of output: 1052


Guard await lock.release() to prevent cancellation from skipping shielded cleanup.

At line 1121, await lock.release() is unguarded and the first awaited operation in the finally block. A CancelledError at this point will immediately propagate and skip the subsequent shielded persistence and transcript upload operations, risking message and transcript loss on interrupted streams.

Suggested fix
 finally:
-        await lock.release()
+        try:
+            await lock.release()
+        except asyncio.CancelledError:
+            logger.warning(
+                "[SDK] [%s] Cancelled during lock release; continuing cleanup",
+                session_id[:12],
+            )
+            release_cancelled = True
+        except Exception as release_err:
+            logger.warning(
+                "[SDK] [%s] Failed to release stream lock: %s",
+                session_id[:12],
+                release_err,
+                exc_info=True,
+            )

         # --- Persist session messages ---
         if session is not None:
             try:
                 await asyncio.shield(upsert_chat_session(session))

Add at end of finally block:

+        if release_cancelled:
+            raise asyncio.CancelledError
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 1117 -
1122, The unguarded await lock.release() in the finally block can raise
CancelledError and abort the subsequent shielded persistence and transcript
upload; change the release to be cancellation-safe by shielding or catching
cancellation (e.g., use asyncio.shield on lock.release() or wrap await
lock.release() in a try/except asyncio.CancelledError and then call
asyncio.shield(lock.release())/re-run release) so that the shielded persistence
and transcript upload always run; update the code around lock.release() in the
finally block (the await lock.release() call) to use asyncio.shield or a
cancellation-aware pattern so downstream persistence/transcript upload always
executes.

Comment on lines +68 to +80
if (msg.role === "assistant") {
const fingerprint = msg.parts
.map(
(p) =>
("text" in p && p.text) ||
("toolCallId" in p && p.toolCallId) ||
"",
)
.join("|");

// Only dedup if this assistant message is identical to the previous one
if (fingerprint && fingerprint === lastAssistantFingerprint) return false;
if (fingerprint) lastAssistantFingerprint = fingerprint;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Dedup fingerprint can drop legitimate tool-state updates.

At Line [69]-Line [75], the fingerprint uses only text/toolCallId. If two consecutive assistant messages share the same toolCallId but differ in state, output, or errorText, the newer message can be incorrectly filtered out.

💡 Proposed fix
-      const fingerprint = msg.parts
-        .map(
-          (p) =>
-            ("text" in p && p.text) ||
-            ("toolCallId" in p && p.toolCallId) ||
-            "",
-        )
-        .join("|");
+      const fingerprint = msg.parts.map((p) => JSON.stringify(p)).join("|");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (msg.role === "assistant") {
const fingerprint = msg.parts
.map(
(p) =>
("text" in p && p.text) ||
("toolCallId" in p && p.toolCallId) ||
"",
)
.join("|");
// Only dedup if this assistant message is identical to the previous one
if (fingerprint && fingerprint === lastAssistantFingerprint) return false;
if (fingerprint) lastAssistantFingerprint = fingerprint;
if (msg.role === "assistant") {
const fingerprint = msg.parts.map((p) => JSON.stringify(p)).join("|");
// Only dedup if this assistant message is identical to the previous one
if (fingerprint && fingerprint === lastAssistantFingerprint) return false;
if (fingerprint) lastAssistantFingerprint = fingerprint;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/app/`(platform)/copilot/useCopilotPage.ts
around lines 68 - 80, The existing dedup logic in useCopilotPage builds a
fingerprint from msg.parts using only text and toolCallId, causing messages with
the same toolCallId but different tool state to be dropped; update the
fingerprint computation (used where msg.role === "assistant" and compared to
lastAssistantFingerprint) to include relevant tool-state fields such as any
part.state, part.output, and part.errorText (in addition to text and toolCallId)
so that changes to tool state produce a different fingerprint and won’t be
filtered out.

0ubbe and others added 3 commits February 28, 2026 00:18
When switching sessions, useChat destroys the Chat instance, losing all
messages. Recovery depends on REST hydration + stream resume, but the
target session's query cache could be stale (cached before the user sent
their last message), causing hasActiveStream to be false and the resume
effect to never fire — leaving the user with an empty chat and a stuck
stop button.

Fix: invalidate both the source AND target session queries on switch,
guaranteeing a fresh fetch with accurate active_stream state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

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

@github-actions github-actions bot added the conflicts Automatically applied to PRs with merge conflicts label Feb 27, 2026
…d hook into domain modules

Backend: revert partial streaming, timing instrumentation, transcript
timeout, and lock ordering changes — keep only heartbeat (3s) and
hex-suffixed marker prefixes needed for direct browser SSE.

Frontend: decompose the 490-line useCopilotPage into focused modules:
- helpers.ts: pure functions (deduplicateMessages, resolveInProgressTools)
- store.ts: Zustand store for shared UI state (sessionToDelete, drawer)
- useCopilotStream.ts: SSE transport, useChat, reconnect, resume, stop
- useCopilotPage.ts: thin orchestrator (~160 lines)
- ChatSidebar: reads sessionToDelete from store instead of duplicating

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot removed the conflicts Automatically applied to PRs with merge conflicts label Feb 27, 2026
@github-actions
Copy link
Contributor

Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly.

@0ubbe 0ubbe changed the title fix(platform): copilot stream robustness — bypass proxy, partial streaming, lock contention fix(platform/copilot): bypass Vercel SSE proxy, refactor hook architecture Feb 27, 2026
majdyz
majdyz previously approved these changes Feb 27, 2026
@github-project-automation github-project-automation bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Feb 27, 2026
Copy link

@autogpt-reviewer autogpt-reviewer left a comment

Choose a reason for hiding this comment

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

PR #12210 — fix(platform/copilot): bypass Vercel SSE proxy, refactor hook architecture
Author: 0ubbe | Re-review #2 (f448f66e6e1bdf) | Files: 10 (+487/-308)

🎯 Verdict: APPROVE

What This PR Does

Switches copilot SSE streaming from a Next.js serverless proxy (subject to Vercel's 800s function timeout) to a direct browser→Python backend connection with JWT auth. Decomposes the 490-line useCopilotPage monolith into focused domain modules: helpers.ts (pure functions), store.ts (Zustand shared UI state), useCopilotStream.ts (SSE transport/reconnect), and a thin useCopilotPage.ts orchestrator. Also hardens marker prefixes with hex suffixes to prevent LLM false-positives, fixes stale closure bugs in reconnect logic, and improves auth error handling with user-facing toasts.

Previous Blockers — ALL RESOLVED ✅

Blocker Status Evidence
🔴 CORS not configured for direct SSE Resolved ntindle (maintainer) confirmed this was a local setup issue, not a code bug. Backend CORSMiddleware correctly uses backend_cors_allow_origins setting. QA confirms zero CORS errors.
🔴 isReconnectScheduled stale closure → infinite reconnect Fixed Now uses dual ref+state pattern (useCopilotStream.ts:78-83). QA confirmed no reconnect loops.

Specialist Findings

🛡️ Security ✅ — JWT auth follows existing WebSocket pattern. Token fetched via server action (not stored client-side). Backend enforces auth via Depends(auth.get_user_id). Marker hex suffixes reduce LLM false-positives. No info leakage in error messages. Zustand store holds UI-only state.

🏗️ Architecture ✅ — Clean hook decomposition with unidirectional data flow (useChatSessionuseCopilotStreamuseCopilotPage). No circular dependencies. getBackendBaseUrl() duplication eliminated — now uses environment.getAGPTServerBaseUrl(). Server action for auth is architecturally sound (one-shot RPC, not subject to timeout). CORS handled by existing middleware. Legacy proxy route preserved as fallback.

Performance ✅ — SSE proxy elimination is a net latency win. Regex pre-compiled at module level (fixed from v1). deduplicateMessages is O(n), bounded by conversation length. Heartbeat 10s→3s acceptable (negligible bandwidth, monitor at scale). One should-fix: JWT fetched per-request via server action (~50-150ms overhead) — should cache client-side.

🧪 Testing ⚠️Zero new tests for ~600 lines of new/refactored code. helpers.ts has two pure functions (deduplicateMessages, resolveInProgressTools) that are trivially unit-testable. useCopilotStream.ts (300 LOC) contains the reconnect logic that was the source of the previous blocker bug — high-risk untested area. Auth error string matching is fragile and untested. CI test(3.11) failure is pre-existing flaky run_agent_test — unrelated.

📖 Quality ✅ — Well-structured decomposition. No any types. Consistent naming conventions. Function declarations used per project convention. 3 should-fixes: (1) isUserStoppingRef leaked as mutable ref across hook boundary — should expose callback instead, (2) unsafe type assertion on refetchSession result, (3) DeleteTarget.title typed as string | null | undefined — normalize to string | null.

📦 Product ✅ — SSE proxy bypass solves the core Vercel timeout pain point. Consecutive-only dedup correctly preserves legitimate repeated responses. Session switch auto-reconnect works. 2 should-fixes: (1) auth error toast has no action button/redirect to login, (2) marker prefix change breaks backward compatibility for existing conversations stored with old format.

📬 Discussion ✅ — Both original blockers resolved. 3/7 CodeRabbit findings fixed, 4 remaining are nitpick-level. All 3 Sentry findings addressed. majdyz APPROVED latest commit (informed reviewer — author of related PR #12205). CI effectively green. 6 PRs have merge conflicts — recommend merging this first since the refactoring makes old useCopilotPage.ts conflicts moot.

🔎 QA ✅ — Full QA pass. SSE streaming works end-to-end including tool execution. Zero CORS errors. No reconnect loops. Session switching works cleanly with no message duplication. Sign up, login, copilot chat, new chat creation all functional.

QA Screenshots

Should Fix (Follow-up OK)

  1. helpers.ts — Add unit tests for deduplicateMessages and resolveInProgressTools — Pure functions, trivially testable, high value. Dedup logic is non-trivial and the reconnect area was the source of the previous blocker.
  2. useCopilotStream.ts:19-26 — Cache JWT client-sidegetAuthHeaders() calls server action per-request (~50-150ms). Supabase tokens valid ~1hr. Cache with TTL or use client-side supabase.auth.getSession().
  3. ChatMessagesContainer.tsx:34-35 — Support old marker prefixes for backward compat — Marker change from [COPILOT_ERROR][__COPILOT_ERROR_f7a1__] breaks parsing of existing stored conversations. Add regex alternation for both formats.
  4. useCopilotStream.ts — Expose resetStopFlag() callback instead of leaking isUserStoppingRef — Mutable ref crossing hook boundary is fragile.
  5. store.ts:5 — Normalize DeleteTarget.title to string | nullstring | null | undefined is a code smell.
  6. useCopilotStream.ts:152-157 — Consistent case-insensitive auth error matching — Currently mixes case-sensitive and case-insensitive checks.
  7. helpers.ts:42-44 — Include tool state in dedup fingerprint — Current fingerprint uses only text/toolCallId, may drop messages where tool state changed but ID matches.
  8. No feature flag for SSE transport — Consider NEXT_PUBLIC_USE_DIRECT_SSE env var to toggle between direct and proxy modes at runtime.

Risk Assessment

Merge risk: LOW | Rollback: EASY (revert to proxy route, which is preserved as legacy)

@ntindle Both previous blockers resolved — CORS confirmed as setup issue, stale closure fixed with ref+state pattern. QA passes clean with SSE streaming, tool execution, and session switching all working. Architecture is a significant improvement (490-line monolith → 4 focused modules). Approved by majdyz. Zero code blockers — recommend merge. Note: 6 PRs have conflicts; merging this first is optimal since the refactoring obsoletes old useCopilotPage.ts conflict targets.

0ubbe and others added 2 commits March 2, 2026 12:58
…11 event loop stability

On Python 3.11, the httpx transport inside Prisma can reference a stale
(closed) event loop when session-scoped async fixtures are evaluated long
after the initial server fixture connected Prisma. This caused all 9
run_agent_test.py tests using setup_test_data to fail with
"Event loop is closed" on the 3.11 CI job.

Add _ensure_db_connected() health check at the start of each test data
fixture that does a cheap SELECT 1 and reconnects if the connection is
stale.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@0ubbe 0ubbe closed this Mar 2, 2026
@github-project-automation github-project-automation bot moved this to Done in Frontend Mar 2, 2026
@github-project-automation github-project-automation bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Mar 2, 2026
@0ubbe
Copy link
Contributor Author

0ubbe commented Mar 2, 2026

Continue on #12254

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end platform/frontend AutoGPT Platform - Front end size/l size/m size/xl

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants