Skip to content

Conversation

@jjjrmy
Copy link
Contributor

@jjjrmy jjjrmy commented Jan 28, 2026

Summary

  • Fix bug where clicking "Send Now" on a queued message during streaming would pop the item from the queue but silently fail to send it, losing the message entirely
  • Fix stale session ID race condition (SESSION_EXPIRED error) that occurred when aborting a stream and immediately starting a new message — the backend was resuming with an invalidated session ID saved by the cleanup handler
  • Fix arePropsEqual in ChatInputArea missing queueLength, firstQueueItemId, and onSendFromQueue, causing stale closures when queue state changed

Changes

active-chat.tsx

  • Add waitForStreamingReady() helper that subscribes to Zustand store instead of polling with setTimeout — includes a 30s safety timeout to prevent indefinite hangs
  • Wrap handleSendFromQueue in try-catch; on failure, requeue the item via prependItem so it is never lost
  • Wrap handleForceSend send call in try-catch; on failure, restore editor content via setValue so the user can retry
  • Replace setTimeout polling loops in both handleSendFromQueue and handleForceSend with the event-driven waitForStreamingReady
  • Remove dead fetch('/api/agents/chat') DELETE calls from handleStop and keyboard handler (no REST server exists in Electron)

ipc-chat-transport.ts

  • Add sessionInvalidated flag set on abort to prevent the transport from reading a stale sessionId from the last assistant message's metadata
  • Use typed AgentMessageMetadata cast instead of as any for metadata access
  • Remove fire-and-forget cancel.mutate() from abort handler — session cleanup is now handled server-side

claude.ts

  • Clear sessionId from DB on abort (sessionId: abortController.signal.aborted ? null : metadata.sessionId) in both the error catch block and normal save block
  • Stop saving stale sessionId in the cleanup handler (unsubscribe callback) — only clear streamId
  • Cancel mutation clears sessionId from DB when no active controller exists (handles the case where cleanup already removed from activeSessions)

chat-input-area.tsx

  • Add queueLength, firstQueueItemId, and onSendFromQueue to arePropsEqual to prevent stale closures

Test plan

  • Start a chat, send a message while AI is streaming, queue a second message
  • Click "Send Now" on the queued message — verify the stream stops and the queued message is sent
  • Verify no SESSION_EXPIRED error appears after the new message starts
  • Verify that if send fails, the queued message reappears in the queue
  • Verify force-send (Cmd+Enter while streaming) stops the stream and sends the new message
  • Verify normal (non-interrupted) chat flow still works and sessions resume correctly

@jjjrmy jjjrmy marked this pull request as draft January 28, 2026 02:23
@jjjrmy
Copy link
Contributor Author

jjjrmy commented Jan 28, 2026

Summary

  • Fixes AI losing all conversation context when a queued message interrupts a streaming response
  • The previous fix (6e246a0) cleared sessionId on abort to avoid resuming a dead session, but this caused the next message to start a completely fresh session with zero prior context since the Claude Code SDK relies solely on session ID-based resumption
  • The session file on disk (~/.claude/) is still valid after abort — the actual issue was resumeSessionAt pointing to an incomplete message UUID, not the session itself

Changes

  • Keep sessionId on abort instead of nulling it (both save paths in claude.ts)
  • Guard sdkMessageUuid so it's not set on aborted streams — prevents resumeSessionAt from targeting an incomplete message
  • Remove sessionInvalidated flag from client transport — no longer needed since server preserves the session
  • Remove cancel mutation fallback that cleared sessionId from DB when no active controller existed
  • Existing isSessionNotFound handler acts as safety net for genuinely stale sessions

Test plan

  • Start a multi-turn conversation so there's meaningful context
  • While the AI is streaming, type and send a new message (triggers queued message flow)
  • Verify the AI's response to the queued message still has context of the prior conversation
  • Verify normal (non-interrupted) conversations still work correctly
  • Verify stopping a response and then sending a new message also preserves context

@jjjrmy jjjrmy marked this pull request as ready for review January 28, 2026 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant