fix(copilot): abort SSE stream and disconnect backend listeners on session switch#12766
fix(copilot): abort SSE stream and disconnect backend listeners on session switch#12766
Conversation
…ssion switch
When users switch between chat sessions, the old SSE fetch was not being
aborted and backend XREAD listeners were left running until timeout. This
caused the UI to show "running" with no output — refreshing revealed the
backend had completed fine.
Frontend:
- Call sdkStop() on session switch to abort the old transport's fetch
- Fire-and-forget DELETE to new disconnect endpoint for the old session
- Store resumeStream/sdkStop in refs to prevent stale closures in the
wake re-sync handler, reconnect timer, and resume effect
Backend:
- Add disconnect_all_listeners() to stream_registry that cancels all
active XREAD listener tasks for a given session
- Add DELETE /sessions/{id}/stream endpoint for frontend to signal
session switch, so listeners are released immediately
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a DELETE API to disconnect all SSE listeners for a chat session, implements pod-local logic to cancel per-session stream listener tasks, provides a fire-and-forget frontend helper, and updates the frontend stream hook to call the helper during session switches. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant FE as Frontend Hook
participant Helper as disconnectSessionStream
participant API as Backend API
participant Registry as StreamRegistry
participant Listener as SSE Listener Task
FE->>Helper: fire-and-forget DELETE /api/chat/sessions/{sessionId}/stream
Helper->>API: authenticated DELETE request
API->>Registry: disconnect_all_listeners(sessionId)
Registry->>Listener: identify & cancel matching listener tasks
Registry-->>API: return count disconnected
API-->>Helper: 204 No Content
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
🔍 PR Overlap DetectionThis check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early. 🔴 Merge Conflicts DetectedThe following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.
🟡 Medium Risk — Some Line OverlapThese PRs have some overlapping changes:
🟢 Low Risk — File Overlap OnlyThese PRs touch the same files but different sections (click to expand)
Summary: 1 conflict(s), 1 medium risk, 6 low risk (out of 8 PRs with file overlap) Auto-generated on push. Ignores: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #12766 +/- ##
==========================================
+ Coverage 64.30% 64.38% +0.08%
==========================================
Files 1815 1817 +2
Lines 132920 133155 +235
Branches 14379 14421 +42
==========================================
+ Hits 85468 85730 +262
+ Misses 44783 44734 -49
- Partials 2669 2691 +22
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Fixes Copilot SSE stream “stuck running” behavior when switching chat sessions by ensuring old client streams are stopped and backend Redis XREAD listener tasks are explicitly disconnected.
Changes:
- Frontend: adds session-switch cleanup (stop old stream + request backend disconnect) and uses refs to avoid stale-closure reconnect/resume callbacks.
- Frontend: adds a fire-and-forget helper to call the backend stream disconnect endpoint.
- Backend: introduces listener cancellation (
disconnect_all_listeners) and a newDELETE /sessions/{session_id}/streamroute.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts | Adds ref-based callbacks and session-switch cleanup to stop/disconnect old streams. |
| autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts | Adds disconnectSessionStream() helper to call new backend DELETE disconnect endpoint. |
| autogpt_platform/backend/backend/copilot/stream_registry.py | Adds disconnect_all_listeners(session_id) to cancel active listener tasks for a session. |
| autogpt_platform/backend/backend/api/features/chat/routes.py | Adds DELETE /sessions/{session_id}/stream endpoint to trigger listener disconnects. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts (1)
165-173: Use the generated chat client for this new endpoint.This ad-hoc
fetchbypasses the repo’s generated API surface, so the newDELETE /sessions/{session_id}/streamroute won’t be represented alongside the rest of the chat mutations. Please regenerate the chat client and call the generated function here instead of adding another direct backend request path.As per coding guidelines "Use generated API hooks from '@/app/api/generated/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/*'" and "Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development".
🤖 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/helpers.ts around lines 165 - 173, The disconnectSessionStream function currently issues a raw fetch to DELETE /api/chat/sessions/{sessionId}/stream; replace this with the generated chat client function (after running pnpm generate:api) and call that generated endpoint instead of fetch. Import the appropriate generated function from '@/app/api/__generated__/endpoints/' (the mutation for deleting session stream), invoke it with sessionId and the existing auth headers (or use the generated client's auth flow), and propagate or swallow errors consistently (keeping the existing .catch behavior if desired). Update the import and remove getCopilotAuthHeaders()/fetch usage in disconnectSessionStream so the new generated API surface is used.
🤖 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/stream_registry.py`:
- Around line 1163-1181: The current disconnect logic in stream_registry.py
inspects only the in-memory _listener_sessions keyed by session_id, so DELETE
/sessions/{session_id}/stream is pod-local and tears down all subscribers for
that session; change this to a subscriber-scoped, cross-pod signal by
introducing a per-subscriber listener token (returned to clients) and a
Redis-backed fan-out: store subscriber entries (token ->
worker/session/listener-id) in Redis when creating listeners, have workers
subscribe to a Redis pub/sub channel, and when DELETE is called publish an
unsubscribe message with that listener token; update the cancel code to match on
the token (not just sid==session_id) and have each worker on receiving the
publish find the corresponding local task in _listener_sessions and cancel it
(keeping the current safe await/wait_for handling for task shutdown).
In `@autogpt_platform/frontend/src/app/`(platform)/copilot/useCopilotStream.ts:
- Around line 430-433: When switching sessions, set isUserStoppingRef.current =
true before calling sdkStopRef.current() and disconnectSessionStream(prevSid) so
the previous stream's onError handler (which captures the old sessionId) won't
treat the resulting AbortError as an unexpected disconnect and enqueue a
reconnect; modify the cleanup branch that checks prevSid !== sessionId to set
isUserStoppingRef.current = true, then call sdkStopRef.current() and
disconnectSessionStream(prevSid) (mirroring how stop() sets isUserStoppingRef
before calling sdkStop), ensuring the same guard used to prevent reconnects is
applied during session switches.
---
Nitpick comments:
In `@autogpt_platform/frontend/src/app/`(platform)/copilot/helpers.ts:
- Around line 165-173: The disconnectSessionStream function currently issues a
raw fetch to DELETE /api/chat/sessions/{sessionId}/stream; replace this with the
generated chat client function (after running pnpm generate:api) and call that
generated endpoint instead of fetch. Import the appropriate generated function
from '@/app/api/__generated__/endpoints/' (the mutation for deleting session
stream), invoke it with sessionId and the existing auth headers (or use the
generated client's auth flow), and propagate or swallow errors consistently
(keeping the existing .catch behavior if desired). Update the import and remove
getCopilotAuthHeaders()/fetch usage in disconnectSessionStream so the new
generated API surface is used.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 42c2ae11-fa3e-4475-b56c-eb7ad23cf86e
📒 Files selected for processing (4)
autogpt_platform/backend/backend/api/features/chat/routes.pyautogpt_platform/backend/backend/copilot/stream_registry.pyautogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.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). (14)
- GitHub Check: check API types
- GitHub Check: integration_test
- GitHub Check: lint
- GitHub Check: Agent
- GitHub Check: Seer Code Review
- GitHub Check: end-to-end tests
- GitHub Check: type-check (3.11)
- GitHub Check: type-check (3.12)
- GitHub Check: test (3.13)
- GitHub Check: type-check (3.13)
- GitHub Check: test (3.12)
- GitHub Check: test (3.11)
- GitHub Check: Analyze (python)
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (11)
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 developmentFormat frontend code using
pnpm format
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.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/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.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
autogpt_platform/frontend/**/*.{ts,tsx}: Fully capitalize acronyms in symbols, e.g.graphID,useBackendAPI
Use function declarations (not arrow functions) for components and handlers
Nodark:Tailwind classes — the design system handles dark mode
Use Next.js<Link>for internal navigation — never raw<a>tags
Noanytypes unless the value genuinely can be anything
No linter suppressors (//@ts-ignore``,// eslint-disable) — fix the actual issue
Keep files under ~200 lines; extract sub-components or hooks into their own files when a file grows beyond this
Keep render functions and hooks under ~50 lines; extract named helpers or sub-components when they grow longer
Use generated API hooks from `@/app/api/generated/endpoints/` with pattern `use{Method}{Version}{OperationName}` and regenerate with `pnpm generate:api`
Do not use `useCallback` or `useMemo` unless asked to optimise a given function
Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
Use ErrorCard for render errors, toast for mutations, and Sentry for exceptions in the frontend
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
autogpt_platform/frontend/src/**/*.{ts,tsx}: Use generated API hooks from@/app/api/__generated__/endpoints/following the patternuse{Method}{Version}{OperationName}, and regenerate withpnpm generate:api
Separate render logic from business logic using component.tsx + useComponent.ts + helpers.ts pattern, colocate state when possible and avoid creating large components, use sub-components in local/componentsfolder
Use function declarations for components and handlers, use arrow functions only for callbacks
Do not useuseCallbackoruseMemounless asked to optimise a given function
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/frontend/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
No barrel files or
index.tsre-exports in the frontendDo not type hook returns, let Typescript infer as much as possible
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/frontend/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not type hook returns, let Typescript infer as much as possible
Extract component logic into custom hooks grouped by concern, not by component. Each hook should represent a cohesive domain of functionality (e.g., useSearch, useFilters, usePagination) rather than bundling all state into one useComponentState hook. Put each hook in its own
.tsfile.
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never type with
any, if no types available useunknown
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
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
autogpt_platform/backend/**/*.py: Usepoetry run ...command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies likeopenpyxl
Use absolute imports withfrom backend.module import ...for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoidhasattr/getattr/isinstancefor type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no# type: ignore,# noqa,# pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use%sfor deferred interpolation indebuglog statements for efficiency; use f-strings elsewhere for readability (e.g.,logger.debug("Processing %s items", count)vslogger.info(f"Processing {count} items"))
Sanitize error paths by usingos.path.basename()in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Usetransaction=Truefor Redis pipelines to ensure atomicity on multi-step operations
Usemax(0, value)guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...
Files:
autogpt_platform/backend/backend/copilot/stream_registry.pyautogpt_platform/backend/backend/api/features/chat/routes.py
autogpt_platform/{backend,autogpt_libs}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/copilot/stream_registry.pyautogpt_platform/backend/backend/api/features/chat/routes.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
Files:
autogpt_platform/backend/backend/api/features/chat/routes.py
autogpt_platform/backend/**/api/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)
autogpt_platform/backend/**/api/**/*.py: UseSecurity()instead ofDepends()for authentication dependencies to get proper OpenAPI security specification
Follow SSE (Server-Sent Events) protocol: usedata:lines for frontend-parsed events (must match Zod schema) and: commentlines for heartbeats/status
Files:
autogpt_platform/backend/backend/api/features/chat/routes.py
🧠 Learnings (15)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:22.025Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12604
File: autogpt_platform/backend/backend/copilot/sdk/security_hooks.py:165-171
Timestamp: 2026-03-30T11:49:37.770Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/security_hooks.py`, the `web_search_count` and `total_tool_call_count` circuit-breaker counters in `create_security_hooks` are intentionally per-turn (closure-local), not per-session. Hooks are recreated per stream invocation in `service.py`, so counters reset each turn. This is an accepted v1 design: it caps a single runaway turn (incident d2f7cba3: 179 WebSearch calls, $20.66). True per-session persistence via Redis is deferred to a later iteration. Do not flag these as a per-session vs. per-turn mismatch bug.
📚 Learning: 2026-04-08T17:28:40.824Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:40.824Z
Learning: Applies to autogpt_platform/frontend/**/*.{ts,tsx} : Fully capitalize acronyms in symbols, e.g. `graphID`, `useBackendAPI`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts
📚 Learning: 2026-03-24T02:23:31.305Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12526
File: autogpt_platform/frontend/src/app/(platform)/copilot/components/RateLimitResetDialog/RateLimitResetDialog.tsx:0-0
Timestamp: 2026-03-24T02:23:31.305Z
Learning: In the Copilot platform UI code, follow the established Orval hook `onError` error-handling convention: first explicitly detect/handle `ApiError`, then read `error.response?.detail` (if present) as the primary message; if not available, fall back to `error.message`; and finally fall back to a generic string message. This convention should be used for generated Orval hooks even if the custom Orval mutator already maps details into `ApiError.message`, to keep consistency across hooks/components (e.g., `useCronSchedulerDialog.ts`, `useRunGraph.ts`, and rate-limit/reset flows).
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-01T18:54:16.035Z
Learnt from: Bentlybro
Repo: Significant-Gravitas/AutoGPT PR: 12633
File: autogpt_platform/frontend/src/app/(platform)/library/components/AgentFilterMenu/AgentFilterMenu.tsx:3-10
Timestamp: 2026-04-01T18:54:16.035Z
Learning: In the frontend, the legacy Select component at `@/components/__legacy__/ui/select` is an intentional, codebase-wide visual-consistency pattern. During code reviews, do not flag or block PRs merely for continuing to use this legacy Select. If a migration to the newer design-system Select is desired, bundle it into a single dedicated cleanup/migration PR that updates all Select usages together (e.g., avoid piecemeal replacements).
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-07T09:24:16.582Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12686
File: autogpt_platform/frontend/src/app/(no-navbar)/onboarding/steps/__tests__/PainPointsStep.test.tsx:1-19
Timestamp: 2026-04-07T09:24:16.582Z
Learning: In Significant-Gravitas/AutoGPT’s `autogpt_platform/frontend` (Vite + `vitejs/plugin-react` with the automatic JSX transform), do not flag usages of React types/components (e.g., `React.ReactNode`) in `.ts`/`.tsx` files as missing `React` imports. Since the React namespace is made available by the project’s TS/Vite setup, an explicit `import React from 'react'` or `import type { ReactNode } ...` is not required; only treat it as missing if typechecking (e.g., `pnpm types`) would actually fail.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-02T05:43:49.128Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12640
File: autogpt_platform/frontend/src/app/(no-navbar)/onboarding/steps/WelcomeStep.tsx:13-13
Timestamp: 2026-04-02T05:43:49.128Z
Learning: Do not flag `import { Question } from "phosphor-icons/react"` as an invalid import. `Question` is a valid named export from `phosphor-icons/react` (as reflected in the package’s generated `.d.ts` files and re-exports via `dist/index.d.ts`), so it should be treated as a supported named export during code reviews.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 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/stream_registry.pyautogpt_platform/backend/backend/api/features/chat/routes.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.
Applied to files:
autogpt_platform/backend/backend/copilot/stream_registry.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).
Applied to files:
autogpt_platform/backend/backend/copilot/stream_registry.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.
Applied to files:
autogpt_platform/backend/backend/copilot/stream_registry.pyautogpt_platform/backend/backend/api/features/chat/routes.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.
Applied to files:
autogpt_platform/backend/backend/copilot/stream_registry.pyautogpt_platform/backend/backend/api/features/chat/routes.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.
Applied to files:
autogpt_platform/backend/backend/copilot/stream_registry.pyautogpt_platform/backend/backend/api/features/chat/routes.py
📚 Learning: 2026-03-07T07:43:15.754Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 12328
File: autogpt_platform/frontend/src/app/api/openapi.json:1116-1118
Timestamp: 2026-03-07T07:43:15.754Z
Learning: In Significant-Gravitas/AutoGPT, v2 chat endpoints often declare HTTPBearerJWT at the router level while using Depends(auth.get_user_id) that returns None for unauthenticated users; effective behavior is optional auth. Keep this convention unless doing a repo-wide OpenAPI update; prefer clarifying descriptions over per-operation security changes.
Applied to files:
autogpt_platform/backend/backend/api/features/chat/routes.py
📚 Learning: 2026-03-17T06:48:26.471Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-11T08:40:59.673Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 12328
File: autogpt_platform/frontend/src/app/(platform)/copilot/useLoadMoreMessages.ts:49-61
Timestamp: 2026-03-11T08:40:59.673Z
Learning: In `autogpt_platform/frontend/src/app/(platform)/copilot/useLoadMoreMessages.ts`, clearing `olderMessages` (and resetting `oldestSequence`/`hasMore`) when `initialOldestSequence` shifts on the same session is intentional. Pages already fetched were based on a now-stale cursor; retaining them risks sequence gaps or duplicates. `ScrollPreserver` keeps the currently visible viewport intact, so only unvisited older pages are dropped. This is a deliberate safe-refetch design tradeoff.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
🔇 Additional comments (2)
autogpt_platform/backend/backend/api/features/chat/routes.py (1)
357-379: Looks good.The new endpoint revalidates session ownership before disconnecting, so it doesn’t let one user tear down another user’s stream listeners.
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts (1)
249-255: Good stale-closure fix.Keeping
sdkStopandresumeStreamin refs here makes the wake/reconnect/session-switch callbacks call the latest SDK functions instead of whichever render created the callback.
|
This pull request has conflicts with the base branch, please resolve those so we can evaluate the pull request. |
…connect-on-session-switch
…reconnect strip, test DELETE
- useCopilotStream: set isUserStoppingRef=true BEFORE sdkStop on session
switch so the old stream's async onError (AbortError) short-circuits
the reconnect path instead of queuing a reconnect for the new session.
- useCopilotStream: restore stale-partial-assistant-message strip in the
reconnect-timer callback — backend replays from 0-0 on resume and the
strip was only present on the hydration path.
- stream_registry: document pod-local / session-scoped cancellation
limitation for disconnect_all_listeners; XREAD timeout bounds worst
case in multi-pod deployments.
- routes_test: add 204 and 404 coverage for DELETE /sessions/{id}/stream.
…, use generated client
- useCopilotStream: schedule reset of isUserStoppingRef via setTimeout(0)
so the old stream's async onError sees it as true, but the new session
is not left permanently flagged (was blocking hydration-triggered
auto-resume on switch-back). Fixes false-positive flagged by sentry.
- helpers: switch disconnectSessionStream to the generated
deleteV2DisconnectSessionStream client, removing the raw fetch /
environment import — addresses coderabbit nitpick.
- openapi.json: regenerate to include DELETE /sessions/{id}/stream.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts (1)
257-263: Consider extracting the stream lifecycle state machine into a smaller hook.
useCopilotStreamnow owns transport setup, reconnect backoff, wake re-sync, hydration merge, cancellation, and session-switch cleanup in one file. Pulling the reconnect/session-switch logic into a focused helper hook would make this easier to reason about and keep closer to the repo’s size guidance.As per coding guidelines, keep files under ~200 lines and keep hooks under ~50 lines; extract named helpers or hooks when they grow longer.
Also applies to: 430-473
🤖 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/useCopilotStream.ts around lines 257 - 263, Extract the stream lifecycle/state-machine logic out of useCopilotStream into a focused hook (e.g., useCopilotStreamLifecycle or useStreamLifecycle) that owns reconnect/backoff, wake re-sync, hydration merge, cancellation, and session-switch cleanup; move the related code currently co-located with sdkStopRef and resumeStreamRef (and the logic around lines ~430-473) into that new hook, keep useCopilotStream responsible only for transport setup and high-level orchestration, wire the new hook to accept stable refs (sdkStopRef, resumeStreamRef) and callbacks from useCopilotStream, and export the lifecycle hook as a named function so tests or callers can import it independently.
🤖 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/useCopilotStream.ts:
- Around line 257-263: Extract the stream lifecycle/state-machine logic out of
useCopilotStream into a focused hook (e.g., useCopilotStreamLifecycle or
useStreamLifecycle) that owns reconnect/backoff, wake re-sync, hydration merge,
cancellation, and session-switch cleanup; move the related code currently
co-located with sdkStopRef and resumeStreamRef (and the logic around lines
~430-473) into that new hook, keep useCopilotStream responsible only for
transport setup and high-level orchestration, wire the new hook to accept stable
refs (sdkStopRef, resumeStreamRef) and callbacks from useCopilotStream, and
export the lifecycle hook as a named function so tests or callers can import it
independently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c858ab00-2391-4439-9cd9-3af4c8fe5e6d
📒 Files selected for processing (3)
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.tsautogpt_platform/frontend/src/app/api/openapi.json
📜 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). (6)
- GitHub Check: end-to-end tests
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: Seer Code Review
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (7)
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 developmentFormat frontend code using
pnpm format
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.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/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.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
autogpt_platform/frontend/**/*.{ts,tsx}: Fully capitalize acronyms in symbols, e.g.graphID,useBackendAPI
Use function declarations (not arrow functions) for components and handlers
Nodark:Tailwind classes — the design system handles dark mode
Use Next.js<Link>for internal navigation — never raw<a>tags
Noanytypes unless the value genuinely can be anything
No linter suppressors (//@ts-ignore``,// eslint-disable) — fix the actual issue
Keep files under ~200 lines; extract sub-components or hooks into their own files when a file grows beyond this
Keep render functions and hooks under ~50 lines; extract named helpers or sub-components when they grow longer
Use generated API hooks from `@/app/api/generated/endpoints/` with pattern `use{Method}{Version}{OperationName}` and regenerate with `pnpm generate:api`
Do not use `useCallback` or `useMemo` unless asked to optimise a given function
Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
Use ErrorCard for render errors, toast for mutations, and Sentry for exceptions in the frontend
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
autogpt_platform/frontend/src/**/*.{ts,tsx}: Use generated API hooks from@/app/api/__generated__/endpoints/following the patternuse{Method}{Version}{OperationName}, and regenerate withpnpm generate:api
Separate render logic from business logic using component.tsx + useComponent.ts + helpers.ts pattern, colocate state when possible and avoid creating large components, use sub-components in local/componentsfolder
Use function declarations for components and handlers, use arrow functions only for callbacks
Do not useuseCallbackoruseMemounless asked to optimise a given function
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/frontend/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
No barrel files or
index.tsre-exports in the frontendDo not type hook returns, let Typescript infer as much as possible
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/frontend/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not type hook returns, let Typescript infer as much as possible
Extract component logic into custom hooks grouped by concern, not by component. Each hook should represent a cohesive domain of functionality (e.g., useSearch, useFilters, usePagination) rather than bundling all state into one useComponentState hook. Put each hook in its own
.tsfile.
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
autogpt_platform/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never type with
any, if no types available useunknown
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
🧠 Learnings (27)
📓 Common learnings
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12766
File: autogpt_platform/backend/backend/copilot/stream_registry.py:1175-1193
Timestamp: 2026-04-14T14:45:38.639Z
Learning: In `autogpt_platform/backend/backend/copilot/stream_registry.py`, `disconnect_all_listeners(session_id)` is intentionally pod-local (inspects in-memory `_listener_sessions`) and session-scoped (not subscriber-scoped). It cancels all listener tasks for the session on the current pod only. If the DELETE request hits a different pod, nothing is cancelled on that pod — the XREAD timeout (5 s block + status poll) bounds the worst-case release time. In the rare two-tabs-same-session case both listeners on the same pod would be torn down. A subscriber-scoped cross-pod fan-out (per-listener tokens + Redis pub/sub) is deferred as a follow-up. Do NOT re-flag this as a blocking issue; the limitation is explicitly documented in the function's docstring (PR `#12766`, commit 1f3ebafd5).
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:22.396Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12604
File: autogpt_platform/backend/backend/copilot/sdk/security_hooks.py:165-171
Timestamp: 2026-03-30T11:49:37.770Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/security_hooks.py`, the `web_search_count` and `total_tool_call_count` circuit-breaker counters in `create_security_hooks` are intentionally per-turn (closure-local), not per-session. Hooks are recreated per stream invocation in `service.py`, so counters reset each turn. This is an accepted v1 design: it caps a single runaway turn (incident d2f7cba3: 179 WebSearch calls, $20.66). True per-session persistence via Redis is deferred to a later iteration. Do not flag these as a per-session vs. per-turn mismatch bug.
📚 Learning: 2026-04-14T14:36:22.396Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:22.396Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-14T14:45:38.639Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12766
File: autogpt_platform/backend/backend/copilot/stream_registry.py:1175-1193
Timestamp: 2026-04-14T14:45:38.639Z
Learning: In `autogpt_platform/backend/backend/copilot/stream_registry.py`, `disconnect_all_listeners(session_id)` is intentionally pod-local (inspects in-memory `_listener_sessions`) and session-scoped (not subscriber-scoped). It cancels all listener tasks for the session on the current pod only. If the DELETE request hits a different pod, nothing is cancelled on that pod — the XREAD timeout (5 s block + status poll) bounds the worst-case release time. In the rare two-tabs-same-session case both listeners on the same pod would be torn down. A subscriber-scoped cross-pod fan-out (per-listener tokens + Redis pub/sub) is deferred as a follow-up. Do NOT re-flag this as a blocking issue; the limitation is explicitly documented in the function's docstring (PR `#12766`, commit 1f3ebafd5).
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-11T08:40:59.673Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 12328
File: autogpt_platform/frontend/src/app/(platform)/copilot/useLoadMoreMessages.ts:49-61
Timestamp: 2026-03-11T08:40:59.673Z
Learning: In `autogpt_platform/frontend/src/app/(platform)/copilot/useLoadMoreMessages.ts`, clearing `olderMessages` (and resetting `oldestSequence`/`hasMore`) when `initialOldestSequence` shifts on the same session is intentional. Pages already fetched were based on a now-stale cursor; retaining them risks sequence gaps or duplicates. `ScrollPreserver` keeps the currently visible viewport intact, so only unvisited older pages are dropped. This is a deliberate safe-refetch design tradeoff.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-24T02:23:31.305Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12526
File: autogpt_platform/frontend/src/app/(platform)/copilot/components/RateLimitResetDialog/RateLimitResetDialog.tsx:0-0
Timestamp: 2026-03-24T02:23:31.305Z
Learning: In the Copilot platform UI code, follow the established Orval hook `onError` error-handling convention: first explicitly detect/handle `ApiError`, then read `error.response?.detail` (if present) as the primary message; if not available, fall back to `error.message`; and finally fall back to a generic string message. This convention should be used for generated Orval hooks even if the custom Orval mutator already maps details into `ApiError.message`, to keep consistency across hooks/components (e.g., `useCronSchedulerDialog.ts`, `useRunGraph.ts`, and rate-limit/reset flows).
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-01T18:54:16.035Z
Learnt from: Bentlybro
Repo: Significant-Gravitas/AutoGPT PR: 12633
File: autogpt_platform/frontend/src/app/(platform)/library/components/AgentFilterMenu/AgentFilterMenu.tsx:3-10
Timestamp: 2026-04-01T18:54:16.035Z
Learning: In the frontend, the legacy Select component at `@/components/__legacy__/ui/select` is an intentional, codebase-wide visual-consistency pattern. During code reviews, do not flag or block PRs merely for continuing to use this legacy Select. If a migration to the newer design-system Select is desired, bundle it into a single dedicated cleanup/migration PR that updates all Select usages together (e.g., avoid piecemeal replacements).
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-07T09:24:16.582Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12686
File: autogpt_platform/frontend/src/app/(no-navbar)/onboarding/steps/__tests__/PainPointsStep.test.tsx:1-19
Timestamp: 2026-04-07T09:24:16.582Z
Learning: In Significant-Gravitas/AutoGPT’s `autogpt_platform/frontend` (Vite + `vitejs/plugin-react` with the automatic JSX transform), do not flag usages of React types/components (e.g., `React.ReactNode`) in `.ts`/`.tsx` files as missing `React` imports. Since the React namespace is made available by the project’s TS/Vite setup, an explicit `import React from 'react'` or `import type { ReactNode } ...` is not required; only treat it as missing if typechecking (e.g., `pnpm types`) would actually fail.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-02T05:43:49.128Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12640
File: autogpt_platform/frontend/src/app/(no-navbar)/onboarding/steps/WelcomeStep.tsx:13-13
Timestamp: 2026-04-02T05:43:49.128Z
Learning: Do not flag `import { Question } from "phosphor-icons/react"` as an invalid import. `Question` is a valid named export from `phosphor-icons/react` (as reflected in the package’s generated `.d.ts` files and re-exports via `dist/index.d.ts`), so it should be treated as a supported named export during code reviews.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-24T21:25:15.983Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12536
File: autogpt_platform/frontend/src/app/api/openapi.json:5770-5790
Timestamp: 2026-03-24T21:25:15.983Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12536`
File: autogpt_platform/frontend/src/app/api/openapi.json
Learning: The OpenAPI spec file is auto-generated; per established convention, endpoints generally declare only 200/201, 401, and 422 responses. Do not suggest adding explicit 403/404 response entries for single operations unless planning a repo-wide spec update. Prefer clarifying such behaviors in endpoint descriptions/docstrings instead of altering response maps.
Applied to files:
autogpt_platform/frontend/src/app/api/openapi.json
📚 Learning: 2026-03-17T06:48:26.471Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.
Applied to files:
autogpt_platform/frontend/src/app/api/openapi.jsonautogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-01T07:58:56.207Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:58:56.207Z
Learning: When a backend field represents sensitive data, use a secret type (e.g., Pydantic SecretStr with length constraints) so OpenAPI marks it as a password/writeOnly field. Apply this pattern to similar sensitive request fields across API schemas so generated TypeScript clients and docs treat them as secrets and do not mishandle sensitivity. Review all openapi.jsons where sensitive inputs are defined and replace plain strings with SecretStr-like semantics with appropriate minLength constraints.
Applied to files:
autogpt_platform/frontend/src/app/api/openapi.json
📚 Learning: 2026-04-14T06:39:49.111Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/frontend/src/app/api/openapi.json:12803-12806
Timestamp: 2026-04-14T06:39:49.111Z
Learning: In OpenAPI specs, ensure the schema/message length caps for the StreamChatRequest.message and QueuePendingMessageRequest.message fields are set to the intended values: StreamChatRequest.message maxLength must be 64000 and QueuePendingMessageRequest.message maxLength must be 32000. Keep QueuePendingMessageRequest.message consistent with PendingMessage.content, and ensure the pending (queue) ceiling never exceeds the stream ceiling because both ultimately feed the same LLM context window. Update any legacy smaller limits (e.g., 4000/16000) to these newer ceilings.
Applied to files:
autogpt_platform/frontend/src/app/api/openapi.json
📚 Learning: 2026-03-07T07:43:09.871Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 12328
File: autogpt_platform/frontend/src/app/api/openapi.json:1116-1118
Timestamp: 2026-03-07T07:43:09.871Z
Learning: For autogpt_platform/frontend/src/app/api/openapi.json, preserve the existing behavior: HTTPBearerJWT is declared at the router level with Depends(auth.get_user_id) returning None for unauthenticated users; treat as optional auth. Do not change per-operation security descriptions unless you plan a repo-wide OpenAPI update. If you change this file, prefer clarifying operation descriptions rather than altering security requirements.
Applied to files:
autogpt_platform/frontend/src/app/api/openapi.json
📚 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/useCopilotStream.ts
📚 Learning: 2026-03-17T06:18:51.570Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx:55-67
Timestamp: 2026-03-17T06:18:51.570Z
Learning: In `autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx`, an explicit `isBusy` guard on the retry handler (`handleRetry`) is not needed. Once `onSend` is invoked, the chat status immediately transitions to "submitted", which causes the `ErrorCard` (containing the retry button) to unmount before a second click can register, making double-send impossible by design.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-30T11:49:37.770Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12604
File: autogpt_platform/backend/backend/copilot/sdk/security_hooks.py:165-171
Timestamp: 2026-03-30T11:49:37.770Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/security_hooks.py`, the `web_search_count` and `total_tool_call_count` circuit-breaker counters in `create_security_hooks` are intentionally per-turn (closure-local), not per-session. Hooks are recreated per stream invocation in `service.py`, so counters reset each turn. This is an accepted v1 design: it caps a single runaway turn (incident d2f7cba3: 179 WebSearch calls, $20.66). True per-session persistence via Redis is deferred to a later iteration. Do not flag these as a per-session vs. per-turn mismatch bug.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-14T06:39:49.111Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/frontend/src/app/api/openapi.json:12803-12806
Timestamp: 2026-04-14T06:39:49.111Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
Intentional message length caps:
- StreamChatRequest.message maxLength = 64000.
- QueuePendingMessageRequest.message maxLength = 32000 (matches PendingMessage.content).
Rationale: both feed the same LLM context window; pending must not exceed stream, and larger ceilings replace legacy 4000/16000.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-13T13:10:33.180Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12764
File: autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentList/useLibraryAgentList.ts:264-294
Timestamp: 2026-04-13T13:10:33.180Z
Learning: In `autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentList/useLibraryAgentList.ts`, the `consecutiveEmptyPagesRef` and `prevFilteredLengthRef` refs used to track filtered-pagination exhaustion are intentional. The one-render lag in `filteredExhausted` (which reads `consecutiveEmptyPagesRef.current` synchronously) is by design — refs are preferred here to avoid triggering extra re-renders for internal fetch-state bookkeeping. Do not flag this as a stale-ref bug in future reviews.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-08T17:28:40.841Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:40.841Z
Learning: Applies to autogpt_platform/frontend/**/*.{ts,tsx} : No linter suppressors (`// ts-ignore`, `// eslint-disable`) — fix the actual issue
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-24T02:05:08.144Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12526
File: autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx:0-0
Timestamp: 2026-03-24T02:05:08.144Z
Learning: In `Significant-Gravitas/AutoGPT` (autogpt_platform frontend), when gating logic on a React Query result being available (e.g., `useGetV2GetCopilotUsage`), prefer destructuring `isSuccess` (e.g., `const { data, isSuccess: hasUsage } = useQuery(...)`) over checking `!isLoading`. `isLoading` can be `false` in error/idle states where `data` is still `undefined`, while `isSuccess` guarantees the query completed successfully and `data` is populated. This pattern was established in `CopilotPage.tsx` (PR `#12526`, commit e9dfd1f76).
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-03T11:14:45.569Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/baseline/service.py:0-0
Timestamp: 2026-04-03T11:14:45.569Z
Learning: In `autogpt_platform/backend/backend/copilot/baseline/service.py`, `transcript_builder.append_user(content=message)` is called unconditionally even when the message is a duplicate that was suppressed by the `is_new_message` guard. This is intentional: the downloaded transcript may be stale (uploaded before the previous attempt persisted the message), so always appending the current user turn prevents a malformed assistant-after-assistant transcript structure. The `is_user_message` flag is still checked (`if message and is_user_message:`), so assistant-role inputs are excluded. Do NOT flag this as a bug.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-09T09:07:11.551Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12720
File: autogpt_platform/backend/backend/copilot/tools/graphiti_delete.py:63-69
Timestamp: 2026-04-09T09:07:11.551Z
Learning: In Significant-Gravitas/AutoGPT, gating `graphiti_delete_user_data` (and similar Graphiti memory tools) on the `is_enabled_for_user` / `graphiti-memory` LaunchDarkly flag in the delete path is intentional and acceptable. The scenario where a user has existing Graphiti data but the flag is later disabled (preventing deletion) is not a concern for the team. Do not flag this pattern as an issue.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-19T11:25:27.842Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12471
File: autogpt_platform/frontend/src/app/(platform)/admin/users/useAdminUsersPage.ts:48-55
Timestamp: 2026-03-19T11:25:27.842Z
Learning: In `autogpt_platform/frontend/src/app/(platform)/admin/users/useAdminUsersPage.ts`, the debounced search pattern uses `useRef(debounce(...))` (lodash) rather than `useEffect`+`setTimeout`. The debounced callback atomically applies `setDebouncedSearch(value.trim())` and `setCurrentPage(1)`, so the page reset is deferred along with the filter change and never races ahead. The query is driven by `debouncedSearch` (not the raw `searchQuery`), so no stale-filter fetch occurs on the first keystroke. Do not flag this pattern as incorrect in future reviews.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-13T13:11:00.401Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12764
File: autogpt_platform/frontend/src/app/(platform)/copilot/components/EmptySession/EmptySession.tsx:41-42
Timestamp: 2026-04-13T13:11:00.401Z
Learning: In Significant-Gravitas/AutoGPT `autogpt_platform/frontend`, unconditional React Query hook calls (e.g. `usePulseChips()` in `EmptySession.tsx`) are intentional when the underlying data is expected to be cached from prior page visits. The team considers the fetch cost acceptable in these cases and does not require `enabled` gating purely for feature-flag-disabled paths. Do not flag unconditional query hooks as wasteful when caching makes the cost negligible.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-01T14:54:01.937Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12636
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-01T14:54:01.937Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), `claude_agent_max_transient_retries` (default=3) in `ChatConfig` counts **total attempts including the initial one**, not the number of extra retries. With the pre-incremented `transient_retries >= max_transient` guard in `service.py`, a value of 3 yields 3 total stream attempts (initial + 2 retries with exponential backoff: 1s, 2s). Do NOT flag this as an off-by-one — the `>=` check is intentional.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-03-10T08:39:22.025Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:22.025Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
📚 Learning: 2026-04-07T18:08:03.548Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12701
File: .claude/skills/orchestrate/scripts/verify-complete.sh:120-121
Timestamp: 2026-04-07T18:08:03.548Z
Learning: In Significant-Gravitas/AutoGPT, verify-complete.sh (`.claude/skills/orchestrate/scripts/verify-complete.sh`) uses `commits[-1].committedDate` (not `updatedAt`) to identify stale CHANGES_REQUESTED reviews. This is intentional: `updatedAt` changes on any PR activity (bot comments, label changes, description edits), which would falsely classify a reviewer's CHANGES_REQUESTED as stale — a silent false negative. The `committedDate` edge case (commit created locally before a review but pushed after) only causes a false positive (unnecessary re-brief), which is the safer failure mode. Do not suggest switching to `updatedAt` for this comparison.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
🔇 Additional comments (3)
autogpt_platform/frontend/src/app/api/openapi.json (1)
1609-1637: Looks good — endpoint schema is consistent and complete.
DELETE /api/chat/sessions/{session_id}/streamis correctly modeled with auth, requiredsession_id, and appropriate204/401/422responses for this generated spec.Based on learnings: The OpenAPI spec file is auto-generated; endpoints generally declare only 200/201, 401, and 422 responses, and per-operation 403/404 additions are not expected unless doing a repo-wide spec update.
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts (1)
159-167: Nice use of the generated disconnect client.Keeping the backend disconnect behind a tiny helper preserves the intended fire-and-forget behavior without adding another bespoke fetch path.
As per coding guidelines, use generated API hooks from
@/app/api/__generated__/endpoints/instead of deprecatedBackendAPIorsrc/lib/autogpt-server-api/*.autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts (1)
257-263: Good race fix.Keeping
sdkStop/resumeStreamin refs and using them from the reconnect, wake-resync, hydration, and session-switch paths removes the stale-closure edge cases this PR is targeting.Also applies to: 391-414, 438-453, 531-532
… codecov Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…connect-on-session-switch
…or is raised The cancelled counter was incrementing for both asyncio.CancelledError and asyncio.TimeoutError. Tasks that don't respond to cancellation within 5s now correctly report as not cancelled. Also adds a test covering the timeout path.
Issues attributed to commits in this pull requestThis pull request was merged and Sentry observed the following issues:
|
Summary
Fixes stream disconnection bugs where the UI shows "running" with no output when users switch between copilot chat sessions. The root cause is that the old SSE fetch is not aborted and backend XREAD listeners keep running until timeout when switching sessions.
Changes
Frontend (
useCopilotStream.ts,helpers.ts)sdkStop()on session switch to abort the in-flight SSE fetch from the old session's transportDELETEto new backend disconnect endpoint so server-side listeners release immediatelyresumeStreamandsdkStopin refs to fix stale closure bugs in:resumeStreamafter tab sleep)resumeStreamduring rapid session switches)Backend (
stream_registry.py,routes.py)disconnect_all_listeners(session_id)to stream registry — iterates active listener tasks, cancels any matching the sessionDELETE /sessions/{session_id}/streamendpoint — auth-protected, callsdisconnect_all_listeners, returns 204Why
Reported by multiple team members: when using Autopilot for anything serious, the frontend loses the SSE connection — particularly when switching between conversations. The backend completes fine (refreshing shows full output), but the UI gets stuck showing "running". This is the worst UX bug we have right now because real users will never know to refresh.
How to test
Test plan
pnpm lint,pnpm types,poetry run lintall pass🤖 Generated with Claude Code