Skip to content

fix(copilot): deduplicate SSE-replayed messages by content fingerprint - #12759

Merged
majdyz merged 7 commits into
devfrom
fix/sse-replay-deduplication
Apr 14, 2026
Merged

fix(copilot): deduplicate SSE-replayed messages by content fingerprint#12759
majdyz merged 7 commits into
devfrom
fix/sse-replay-deduplication

Conversation

@majdyz

@majdyz majdyz commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes duplicate message content shown in CoPilot during SSE reconnections (page visibility change, network hiccups, wake-resync)
  • The resume_session_stream backend always replays from "0-0" (beginning of Redis stream), and replayed UIMessage objects get new generated IDs from useChat, bypassing the old adjacent-only content dedup
  • Extends deduplicateMessages to track all seen role + preceding-user-context + content fingerprints globally, catching replayed messages regardless of different IDs or position in the list
  • Scopes fingerprints by preceding user message text to avoid false positives when the assistant legitimately gives the same answer to different prompts

Test plan

  • Verify new unit tests pass in CI (helpers.test.ts - 7 new dedup test cases)
  • Manual: start a long tool-use session, switch tabs, return - no duplicate content
  • Manual: refresh page during active session - content loads from DB without duplicates
  • Manual: ask the same question twice in different turns - both answers preserved

When the SSE connection reconnects, resume_session_stream replays from
"0-0" and the replayed UIMessage objects get new IDs from useChat,
bypassing the adjacent-only content dedup. Switch deduplicateMessages
to track all seen role+context+content fingerprints globally, scoped
by the preceding user message to avoid false positives when the
assistant legitimately gives identical answers to different prompts.
@majdyz
majdyz requested a review from a team as a code owner April 13, 2026 08:04
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Apr 13, 2026
@majdyz
majdyz requested review from 0ubbe and Bentlybro and removed request for a team April 13, 2026 08:04
@github-actions github-actions Bot added platform/frontend AutoGPT Platform - Front end size/l labels Apr 13, 2026
@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

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

Replaces last-assistant-only suppression with scoped deduplication keyed by the most recent user message and a content fingerprint; adds extensive tests and removes a stale in-progress assistant message before reconnect/resume in the SSE stream flow.

Changes

Cohort / File(s) Summary
Deduplication Implementation
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts
Reworks assistant deduplication to track lastUserMsgId + contentFingerprint keys (assistant:{userId}:{fingerprint}), compute fingerprints via JSON.stringify of msg.parts, ignore empty fingerprints, and stop resetting dedup state on non-assistant messages so identical assistant outputs across different turns are preserved while repeats within the same user-turn are filtered.
Deduplication Tests
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
Adds deduplicateMessages test suite and a deterministic makeMsgWithId helper; covers duplicate IDs, SSE-replay/ non-adjacent duplicate suppression, context-aware preservation, repeated-question cases, adjacent duplicates, empty/unique inputs, delimiter-collision avoidance, and dynamic-tool toolCallId deduplication.
Reconnect / Resume Fix
autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts
On reconnect timer flow, removes the last in-progress assistant UI message via setMessages before calling resumeStream() to avoid leaving a partially streamed assistant message when the backend replays.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

size/m

Suggested reviewers

  • Bentlybro
  • 0ubbe
  • kcze

Poem

🐇 I twitch my whiskers, sort the chat with glee,
Fold echoes away where one answer should be.
Same prompt, same turn—I'll hush the repeat,
New question, new hop—I let it all meet.
Carrot-coded tidy, neat as can be!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.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 describes the main change: deduplicating SSE-replayed messages using content fingerprints, which directly addresses the root cause of duplicate messages during reconnections.
Description check ✅ Passed The description is well-related to the changeset, explaining the root cause (SSE replay from 0-0), the solution (tracking fingerprints by role + user context + content), and including test plans for verification.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sse-replay-deduplication

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.

Comment thread autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts Outdated

@majdyz majdyz left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Review Summary

Overall: Clean, well-scoped fix. The change correctly addresses the root cause -- SSE replay from "0-0" produces messages with new IDs that bypass adjacent-only content dedup.

Correctness: The composite key assistant:{lastUserText}:{contentFingerprint} properly scopes dedup to the conversational context, preventing false positives when the assistant gives the same answer to different prompts. The Set-based global tracking catches replayed duplicates regardless of their position in the message list.

Testing: 7 new test cases cover the key scenarios: ID dedup, non-adjacent SSE replay dedup, same-answer-different-question preservation, adjacent dedup, empty list, unique passthrough, and toolCallId dedup. All 42 tests pass.

One minor observation (posted inline): The content fingerprint approach relies on useCopilotStream removing the in-progress assistant message before resumeStream(). This coupling is currently correct but worth documenting.

No blockers found.

@codecov

codecov Bot commented Apr 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.23077% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.13%. Comparing base (b319c26) to head (0719c43).
⚠️ Report is 3 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev   #12759      +/-   ##
==========================================
- Coverage   63.14%   63.13%   -0.01%     
==========================================
  Files        1811     1811              
  Lines      130463   130470       +7     
  Branches    14260    14263       +3     
==========================================
- Hits        82376    82375       -1     
- Misses      45495    45502       +7     
- Partials     2592     2593       +1     
Flag Coverage Δ
platform-frontend 15.81% <69.23%> (+0.06%) ⬆️
platform-frontend-e2e 27.96% <0.00%> (-0.19%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Platform Backend 74.63% <ø> (ø)
Platform Frontend 23.77% <69.23%> (-0.02%) ⬇️
AutoGPT Libs ∅ <ø> (∅)
Classic AutoGPT 28.43% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts (1)

175-199: ⚠️ Potential issue | 🟡 Minor

Use collision-safe fingerprint serialization

Line 180, Line 191, and Line 196 build dedup keys with |/: delimiters. Since user/assistant text can contain those characters, distinct contexts can collapse to the same key and incorrectly drop valid assistant messages.

Proposed fix
-      lastUserText = msg.parts
-        .map((p) => ("text" in p ? p.text : ""))
-        .join("|");
+      lastUserText = JSON.stringify(
+        msg.parts.map((p) => ("text" in p ? p.text : "")),
+      );
@@
-      const contentFingerprint = msg.parts
-        .map(
-          (p) =>
-            ("text" in p && p.text) ||
-            ("toolCallId" in p && p.toolCallId) ||
-            "",
-        )
-        .join("|");
+      const contentFingerprint = JSON.stringify(
+        msg.parts.map(
+          (p) =>
+            ("text" in p && p.text) ||
+            ("toolCallId" in p && p.toolCallId) ||
+            "",
+        ),
+      );
@@
-        const contextKey = `assistant:${lastUserText}:${contentFingerprint}`;
+        const contextKey = JSON.stringify({
+          role: "assistant",
+          lastUserText,
+          contentFingerprint,
+        });
🤖 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
175 - 199, The current dedup key construction for assistant messages uses raw
concatenation with ":" and "|" (variables lastUserText, contentFingerprint)
which can collide if those characters appear in text; change the key
construction to a collision-safe serialization such as JSON.stringify or a
deterministic encode (e.g., base64/encodeURIComponent) of the components and
then use that serialized string as contextKey so
seenFingerprints.has(contextKey) / seenFingerprints.add(contextKey) operate on
unambiguous keys; update the places that build contextKey (and any places
relying on its shape) to use the new serialized form.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@autogpt_platform/frontend/src/app/`(platform)/copilot/helpers.ts:
- Around line 175-199: The current dedup key construction for assistant messages
uses raw concatenation with ":" and "|" (variables lastUserText,
contentFingerprint) which can collide if those characters appear in text; change
the key construction to a collision-safe serialization such as JSON.stringify or
a deterministic encode (e.g., base64/encodeURIComponent) of the components and
then use that serialized string as contextKey so
seenFingerprints.has(contextKey) / seenFingerprints.add(contextKey) operate on
unambiguous keys; update the places that build contextKey (and any places
relying on its shape) to use the new serialized form.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26a93d1f-4bb0-4068-88bc-c52669aceb61

📥 Commits

Reviewing files that changed from the base of the PR and between b319c26 and 7cadce4.

📒 Files selected for processing (2)
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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). (6)
  • GitHub Check: check API types
  • GitHub Check: integration_test
  • GitHub Check: Seer Code Review
  • GitHub Check: Analyze (python)
  • GitHub Check: Check PR Status
  • GitHub Check: end-to-end tests
🧰 Additional context used
📓 Path-based instructions (8)
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

Format frontend code using pnpm format

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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
No dark: Tailwind classes — the design system handles dark mode
Use Next.js <Link> for internal navigation — never raw <a> tags
No any types 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.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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 pattern use{Method}{Version}{OperationName}, and regenerate with pnpm 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 /components folder
Use function declarations for components and handlers, use arrow functions only for callbacks
Do not use useCallback or useMemo unless asked to optimise a given function

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

No barrel files or index.ts re-exports in the frontend

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

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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 .ts file.

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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/helpers.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts
autogpt_platform/frontend/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

autogpt_platform/frontend/**/*.{test,spec}.{ts,tsx}: Use Vitest + RTL + MSW for integration tests as the primary testing approach (~90%, page-level), use Playwright for E2E critical flows, and use Storybook for design system components
Run frontend integration tests with pnpm test:unit (Vitest + RTL + MSW)

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
🧠 Learnings (11)
📓 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: 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.
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: 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.
📚 Learning: 2026-04-08T17:28:55.665Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:55.665Z
Learning: Applies to autogpt_platform/frontend/src/tests/src/tests/**/*.spec.ts : Import `test` and `expect` from `./coverage-fixture` instead of `playwright/test` in E2E tests to auto-collect V8 coverage per test for Codecov reporting

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
📚 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/src/**/__tests__/**/*.test.{ts,tsx} : Use Orval-generated MSW handlers from `@/app/api/__generated__/endpoints/{tag}/{tag}.msw.ts` for API mocking

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
📚 Learning: 2026-04-08T17:27:45.725Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-08T17:27:45.725Z
Learning: Applies to autogpt_platform/frontend/**/*.{test,spec}.{ts,tsx} : Use Vitest + RTL + MSW for integration tests as the primary testing approach (~90%, page-level), use Playwright for E2E critical flows, and use Storybook for design system components

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts
📚 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/src/app/(platform)/**/__tests__/**/*.test.{ts,tsx} : Write integration tests in `__tests__/` next to `page.tsx` using Vitest + RTL + MSW for new pages/features

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.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.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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/helpers.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.ts
🔇 Additional comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.test.ts (1)

305-396: Strong coverage for SSE replay dedup behavior

The new suite covers the key replay/context/tool-call paths and meaningfully guards the deduplication regression.

Comment thread autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts Outdated
majdyz added 2 commits April 13, 2026 09:55
Using user message text as the context key caused the deduplicator to
drop the second assistant reply when a user asked the same question twice
in one session. Switching to user message ID (which is unique per turn)
fixes the false positive while still preventing SSE-replayed duplicates.

Adds a regression test covering the same-question-twice scenario.
A partial streaming message has a different fingerprint from its final
version, so it bypasses content dedup. Document that this is safe only
because the caller removes the in-progress message before resumeStream().

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts (1)

186-206: ⚠️ Potential issue | 🟡 Minor

Make the fingerprint key unambiguous.

join("|") can alias distinct assistant payloads, e.g. [text:"a|b"] and [text:"a", text:"b"]. In the same user turn, that can silently drop a legitimate assistant message as a replay. Use a structured payload for the key, then stringify/hash that structure.

🧩 One way to make the key collision-safe
-      const contentFingerprint = msg.parts
-        .map(
-          (p) =>
-            ("text" in p && p.text) ||
-            ("toolCallId" in p && p.toolCallId) ||
-            "",
-        )
-        .join("|");
+      const fingerprintParts = msg.parts.flatMap((part) => {
+        if ("text" in part && part.text) {
+          return [["text", part.text] as const];
+        }
+        if ("toolCallId" in part && part.toolCallId) {
+          return [["tool-call", part.toolCallId] as const];
+        }
+        return [];
+      });

-      if (contentFingerprint) {
+      if (fingerprintParts.length > 0) {
         // Scope to the preceding user message turn so that identical assistant
         // replies to *different* user prompts are preserved.
         // NOTE: A streaming (in-progress) assistant message has a partial
         // fingerprint that differs from its final form, so it would not be
         // caught by this dedup. This is safe because the caller removes the
         // in-progress assistant message before calling resumeStream() — see
         // useCopilotStream.ts. If that removal is ever refactored away,
         // partial streaming messages could bypass dedup.
-        const contextKey = `assistant:${lastUserMsgId}:${contentFingerprint}`;
+        const contextKey = JSON.stringify([
+          "assistant",
+          lastUserMsgId,
+          fingerprintParts,
+        ]);
         if (seenFingerprints.has(contextKey)) return false;
         seenFingerprints.add(contextKey);
       }
🤖 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
186 - 206, The current fingerprint built by contentFingerprint =
msg.parts.map(...).join("|") can collide (e.g., ["a|b"] vs ["a","b"]); instead
build an unambiguous structured key from msg.parts (e.g., map each part to an
object with explicit type fields like {kind: "text", value: p.text} or {kind:
"toolCallId", value: p.toolCallId}), then serialize that structure
(JSON.stringify or a stable serializer) or compute a hash of it and use that
serialized/hash value when forming contextKey
(`assistant:${lastUserMsgId}:${serializedParts}`) before checking/adding to
seenFingerprints; update references to contentFingerprint/contextKey accordingly
to ensure collision-safe deduping.
🧹 Nitpick comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts (1)

171-171: Rename lastUserMsgId to lastUserMsgID.

This new local introduces another Id spelling in frontend TS code. Renaming it here keeps the helper aligned with the repo’s acronym-casing rule.

♻️ Suggested rename
-  let lastUserMsgId = "";
+  let lastUserMsgID = "";
...
-      lastUserMsgId = msg.id;
+      lastUserMsgID = msg.id;
...
-        const contextKey = `assistant:${lastUserMsgId}:${contentFingerprint}`;
+        const contextKey = `assistant:${lastUserMsgID}:${contentFingerprint}`;

As per coding guidelines: autogpt_platform/frontend/**/*.{ts,tsx}: Fully capitalize acronyms in symbols, e.g. graphID, useBackendAPI.

Also applies to: 182-182, 204-204

🤖 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 at line 171,
Rename the local variable lastUserMsgId to lastUserMsgID to follow the project's
acronym-casing rule; update every occurrence in this helper (the variable
declaration and all uses/references where lastUserMsgId appears) including the
other instances in the same file (previously flagged at the other two
occurrences) so imports/uses remain consistent and TypeScript compiles.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@autogpt_platform/frontend/src/app/`(platform)/copilot/helpers.ts:
- Around line 186-206: The current fingerprint built by contentFingerprint =
msg.parts.map(...).join("|") can collide (e.g., ["a|b"] vs ["a","b"]); instead
build an unambiguous structured key from msg.parts (e.g., map each part to an
object with explicit type fields like {kind: "text", value: p.text} or {kind:
"toolCallId", value: p.toolCallId}), then serialize that structure
(JSON.stringify or a stable serializer) or compute a hash of it and use that
serialized/hash value when forming contextKey
(`assistant:${lastUserMsgId}:${serializedParts}`) before checking/adding to
seenFingerprints; update references to contentFingerprint/contextKey accordingly
to ensure collision-safe deduping.

---

Nitpick comments:
In `@autogpt_platform/frontend/src/app/`(platform)/copilot/helpers.ts:
- Line 171: Rename the local variable lastUserMsgId to lastUserMsgID to follow
the project's acronym-casing rule; update every occurrence in this helper (the
variable declaration and all uses/references where lastUserMsgId appears)
including the other instances in the same file (previously flagged at the other
two occurrences) so imports/uses remain consistent and TypeScript compiles.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d59819fb-493e-4316-96a6-1a118bc81cc4

📥 Commits

Reviewing files that changed from the base of the PR and between a17f05f and 6d0fb28.

📒 Files selected for processing (1)
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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). (2)
  • GitHub Check: Check PR Status
  • GitHub Check: end-to-end tests
🧰 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 development

Format frontend code using pnpm format

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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.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
No dark: Tailwind classes — the design system handles dark mode
Use Next.js <Link> for internal navigation — never raw <a> tags
No any types 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.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 pattern use{Method}{Version}{OperationName}, and regenerate with pnpm 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 /components folder
Use function declarations for components and handlers, use arrow functions only for callbacks
Do not use useCallback or useMemo unless asked to optimise a given function

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

No barrel files or index.ts re-exports in the frontend

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

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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 .ts file.

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.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/helpers.ts
🧠 Learnings (13)
📓 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: 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.
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.
📚 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/helpers.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.ts
📚 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/helpers.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/helpers.ts
📚 Learning: 2026-03-26T07:00:03.405Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12574
File: autogpt_platform/backend/backend/copilot/sdk/transcript.py:980-990
Timestamp: 2026-03-26T07:00:03.405Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/transcript.py`, `_rechain_tail` intentionally rewrites `parentUuid` for **all** tail entries (not just the first), because a single assistant turn can span multiple consecutive JSONL entries sharing the same `message.id` (e.g., a thinking entry + a tool_use entry). Their original `parentUuid` values may reference entries that were absorbed into the compressed prefix, so sequential rechaining of the entire tail is required to maintain a valid parent→child graph. The test `test_chains_multiple_tail_entries` validates this: the second tail entry's `parentUuid` is rewritten from its original value to the uuid of the first tail entry.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts
📚 Learning: 2026-04-09T08:47:23.320Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12720
File: autogpt_platform/backend/backend/copilot/graphiti/client.py:20-46
Timestamp: 2026-04-09T08:47:23.320Z
Learning: In Significant-Gravitas/AutoGPT, `user_id` values passed to `derive_group_id` in `autogpt_platform/backend/backend/copilot/graphiti/client.py` are always system-generated UUIDv4s (e.g. `883cc9da-fe37-4863-839b-acba022bf3ef`). The character set `[0-9a-f-]` is fully within `[a-zA-Z0-9_-]`, so the sanitization regex never strips any characters and no collision between two different user IDs is possible. Do not flag `derive_group_id` for collision-resistance issues.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts
📚 Learning: 2026-03-17T10:57:12.953Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts
📚 Learning: 2026-04-03T11:14:16.378Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/transcript_builder.py:30-34
Timestamp: 2026-04-03T11:14:16.378Z
Learning: In `autogpt_platform/backend/backend/copilot/transcript_builder.py` (and its re-export shim at `sdk/transcript_builder.py`), `TranscriptEntry.parentUuid` is typed `str` (not `str | None`) and root entries use `parentUuid=""` (empty string) to match the canonical `_messages_to_transcript` JSONL format. `_parse_entry`, `append_user`, and `append_assistant` all coerce `None` to `""`. Do NOT flag `parentUuid=""` as incorrect — it is the correct root marker. This was fixed in PR `#12623`, commit b753cb7d0b.

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.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.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.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.ts

…lUIPart shape

Replace deprecated `type: "tool-invocation"` with `type: "dynamic-tool"` and
update state from old `"call"` to `"input-available"` in deduplicateMessages
tests to match the current ai SDK ToolUIPart type definitions.
@github-actions

github-actions Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

🔍 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.

🟡 Medium Risk — Some Line Overlap

These PRs have some overlapping changes:

Summary: 1 conflict(s), 1 medium risk, 0 low risk (out of 2 PRs with file overlap)


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

Comment thread autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts Outdated
Comment thread autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts Outdated
…re resumeStream

handleReconnect was the only resumeStream caller that did not strip the
stale in-progress assistant message first. On network disconnect the
partial assistant message stays in rawMessages; the backend replays from
0-0 and recreates the full turn, leaving the partial message stranded
alongside the replay.

- Add the same setMessages strip logic to handleReconnect (matching the
  wake-resync path at lines 368-374 and the hydration-effect path at
  lines 482-487).
- Update the helpers.ts comment to accurately state that all callers now
  strip the in-progress message before calling resumeStream().
- Replace join("|") with JSON.stringify() for contentFingerprint to
  eliminate separator-collision false positives (e.g. ["a|b","c"] and
  ["a","b|c"] previously produced identical fingerprints).
- Add a regression test for the separator-collision case.
Comment thread autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts

@majdyz majdyz left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Review Summary

Overall: LGTM with minor nits. The core dedup logic is correct and well-tested. Previous blockers (same-question-twice false positive, handleReconnect missing strip, separator collision) were all addressed in the iteration. Two new minor comments added:

  1. 🟡 Fingerprint map robustness (helpers.ts:193): parts with falsy extracted values all map to "", which can conflate structurally different parts. Harmless today but brittle.
  2. 🔵 Untested branch (helpers.test.ts:435): the contentFingerprint !== "[]" early-return path has no dedicated test.

No blockers remain. The setMessages + resumeStream() ordering in handleReconnect is safe — verified that setMessages in the AI SDK mutates the internal store synchronously (chatRef.current.messages = ...), so resumeStream() sees the stripped message list immediately.

@majdyz

majdyz commented Apr 14, 2026

Copy link
Copy Markdown
Contributor Author

🧪 E2E Test Result — preview/all-active-prs

Result: PASS

After hard reload, session resumed without duplicating any messages. Message count matched before/after.

Screenshots

07-PR12759-before-reload.png
08-PR12759-after-reload.png

Follow the project's acronym-casing rule (autogpt_platform/frontend/**/*.{ts,tsx}:
fully capitalize acronyms in symbols). Flagged by coderabbitai as a nitpick.
Comment thread autogpt_platform/frontend/src/app/(platform)/copilot/helpers.ts
@majdyz
majdyz merged commit b2b6f75 into dev Apr 14, 2026
31 checks passed
@majdyz
majdyz deleted the fix/sse-replay-deduplication branch April 14, 2026 08:49
@github-project-automation github-project-automation Bot moved this to Done in Frontend Apr 14, 2026
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to ✅ Done in AutoGPT development kanban Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/frontend AutoGPT Platform - Front end size/l

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant