Skip to content

fix(frontend): keep a sent message when the user switches sessions - #7834

Merged
mmabrouk merged 1 commit into
release/v0.125.2from
fix/frontend-lost-message-on-session-switch
Oct 26, 2026
Merged

mmabrouk merged 1 commit into
release/v0.125.2from
fix/frontend-lost-message-on-session-switch

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Context

A user types the first message in a new agent session and presses send. Starting the turn can take a while on a loaded box. If the user opens another session in the sidebar during that wait, the message is never sent. When they come back, the session and the message are gone.

The cause is in how the send builds its request. Before /invoke goes out, useServerSessionInputs.submit waits (up to 15 s) for the workflow's invocation URL, and buildAgentRequest reads it with store.get, which does not subscribe. While the chat screen is open, the screen keeps the workflow query subscribed. After a session switch, nothing holds it. A jotai atom nobody subscribes to does not take the data its query loads later, so if the workflow was still loading at the switch, every build returned null until the deadline failed the send with "The agent is not ready to accept input." The failure path then called dropUnacceptedLocalSession, which deleted the fresh session row, and tried to put the text back into a composer that no longer existed.

Once /invoke has left the browser, a switch is already safe: the fetch has no abort signal and nothing cancels the stream on unmount.

Changes

  1. The send no longer needs the screen. submit subscribes to workflowMolecule.selectors.invocationUrl(entityId) while the request builds and releases the subscription right after. A workflow that finishes loading off screen now reaches the builder, and the send goes out.
  2. A send that fails after the user left is kept. LiveConversation knows when it has unmounted. If a send fails while the screen is still open, nothing changes: the fresh session row is dropped and the text goes back to the composer. If it fails after the screen closed, the session row stays, and a new keepFailedSendAtom puts the message into pendingTasksAtom as a failed task (text, file parts, and the refusal's own reason when there is one). When the user comes back, the existing PendingTaskError card ("Message not sent", "Retry message") shows it. A Home task already in that slot is left to sendPendingTaskAtom, which already marks it failed.

To carry the refusal reason, onSendFailed now also receives the error that rejected the send: (message, error?) => void.

Tests

  • useServerSessionInputs.offScreen.test.ts: a send waits on a workflow that is still loading, the screen's subscription goes away and the hook unmounts, then the workflow loads. /invoke is called once. Without the fix it is never called.
  • liveConversationOffScreenFailure.render.test.tsx: a failure after unmount keeps the session and stores a failed task with its reason, and the task shows again on remount. A Home task in the slot is left alone. A failure while mounted still drops the session row and stores no task. The first two cases fail without the fix.
  • pnpm --filter @agenta/chat test:unit (1404 passed), pnpm --filter @agenta/mobile test:unit (675 passed), types:check for both, pnpm lint-fix in web/.
  • I confirmed the mechanism (an unsubscribed atomWithQuery never takes data that lands after its last subscriber left; holding a subscription fixes it) with a scratch vitest against the repo's installed jotai and jotai-tanstack-query.
  • Not verified live. I did not run this branch in a browser, because every dev stack on the box belongs to another session and the box is overloaded.

What to QA

  • Open a new agent session, type a message, send, and switch to another session at once (the effect is clearest on a slow stack or with network throttling). Wait 20 s and come back. The turn ran and the transcript shows it.
  • Make a send fail after you leave (for example, block the workflow revision request in the browser's devtools before you send). Come back. The session is still in the sidebar and shows "Message not sent" with "Retry message". Retry sends the saved text.
  • Regression: a send that fails while you stay on the screen still puts the text back in the composer, and a brand-new session whose first send fails still leaves the sidebar.

A first send waits for the workflow before it goes out. If the user opened
another session meanwhile, the workflow query that was still loading never
reached the unsubscribed atom the request builder reads, so the send timed
out and the failure path deleted the fresh session row.

Hold the workflow subscription while the request is built, so the send goes
out off screen. When a send still fails after the screen closed, keep the
session and store the message as a failed task that the chat screen offers
again with its retry.
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Failed messages are now preserved when you leave a conversation before the send completes, including attached files and any refusal details, so they can be retried when you return.
    • A failed send no longer overwrites a task that is already being sent.
    • Messages queued while workflow details load can now be sent successfully even if you leave the conversation before loading finishes.

Send handling now tracks screen presence and preserves failed sends as pending tasks when failure occurs after unmount. Request building also subscribes to the workflow invocation URL until building completes, including when the screen has unmounted.

Changes: Off-screen send handling

Layer / File(s) Summary
Workflow request building
web/packages/agenta-chat/src/hooks/useServerSessionInputs.ts, web/packages/agenta-chat/tests/unit/hooks/useServerSessionInputs.offScreen.test.ts
Request building subscribes to the current entity’s invocation URL and releases the subscription when building completes. A test covers a workflow loading after the hook unmounts and verifies the request URL, idempotency key, and queued result.
Send-failure callback data
web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts, web/packages/agenta-chat/src/hooks/useAgentConversation.ts
The failure callback now accepts a queued message and optional error. Admission rejection passes its caught error to the callback.
Retain failed sends after unmount
web/mobile/src/features/home/pendingTask.ts, web/mobile/src/features/chat/LiveConversation.tsx, web/mobile/tests/unit/liveConversationOffScreenFailure.render.test.tsx, web/mobile/tests/unit/liveConversationReadOnly.render.test.tsx
When failure occurs after unmount, LiveConversation stores the task’s text, file parts, agent ID, and available refusal reason. The pending-task atom leaves an occupied slot unchanged. Tests cover both mounted and unmounted failures and verify the retained reason on return.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentChatQueue
  participant AgentConversation
  participant LiveConversation
  participant PendingTaskAtom
  AgentChatQueue->>AgentConversation: Report rejected message and error
  AgentConversation->>LiveConversation: Call onSendFailed with message and error
  LiveConversation->>PendingTaskAtom: Store failed task when screen is unmounted
Loading
Merge Risk: Moderate · up to 0d036

The PR fixes loss of the first message when a user switches sessions. If several queued messages fail after the user leaves, only the first is kept for retry and the others are silently lost. Consider fixing this before merge, or explicitly accept it as a known limitation.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly summarizes the primary change: preserving sent messages when the user switches sessions.
Description check Passed The description directly explains the session-switching failure, the subscription fix, failure handling, tests, and QA scope.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 8…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 8 files.

Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@github-actions

Copy link
Copy Markdown
Contributor

Website preview

Preview URL: https://pr-7834-agenta-website-preview.mahmoud-637.workers.dev

Built from 0d036f91ecd0a2ca326b0ce5a8055e949f466194. This comment updates in place on every push.

@mmabrouk mmabrouk added lgtm This PR has been approved by a maintainer merge queue In the release merge queue; an agent is landing it labels Oct 26, 2026
@mmabrouk

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Actionable comments posted: 1


Review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between cc7d473 and 0d036f9.

Files selected for processing: 8
  • web/mobile/src/features/chat/LiveConversation.tsx
  • web/mobile/src/features/home/pendingTask.ts
  • web/mobile/tests/unit/liveConversationOffScreenFailure.render.test.tsx
  • web/mobile/tests/unit/liveConversationReadOnly.render.test.tsx
  • web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts
  • web/packages/agenta-chat/src/hooks/useAgentConversation.ts
  • web/packages/agenta-chat/src/hooks/useServerSessionInputs.ts
  • web/packages/agenta-chat/tests/unit/hooks/useServerSessionInputs.offScreen.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 4 remain after this review.

Comment thread web/mobile/src/features/home/pendingTask.ts
@mmabrouk
mmabrouk merged commit b3cd3bc into release/v0.125.2 Oct 26, 2026
110 of 116 checks passed
@mmabrouk mmabrouk removed the merge queue In the release merge queue; an agent is landing it label Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant