Repository navigation
fix: wait for an in-progress reply before sending after a reload - #59
Conversation
|
Needs a rebase before merge. It conflicts with #54 in |
jerelvelarde
left a comment
There was a problem hiding this comment.
Value: recovering an active reply across reload and queueing follow-up messages is useful. Six queue/SDK tests passed; no new security blocker identified. Please integrate current main before approval: chat.tsx, conversation-run.ts and SDK tests have substantive conflicts with the merged history-error handling and Jev runQueued wiring. Preserve those behaviors alongside reconnect/queue behavior and rerun the combined regressions.
3825375 to
1614381
Compare
|
Rebased onto main with #54's history-error handling and the Jev
|
jerelvelarde
left a comment
There was a problem hiding this comment.
Value: reconnect now holds queued turns until an in-progress reply completes. Template fit: current-main integration preserves history-error filtering, replay counters, Jev choice completion, and Android keyboard behavior; the description accurately states live-testing limits. Security: no introduced security issue found in the changed queue/SDK/UI paths. Verification: 19 focused tests, mobile typecheck, and formatting pass. Executing the actual runQueued handler confirms a held choice stays pending and resolves exactly once after resend. All seven CI checks pass.
What changed
Reloading the app while a reply is still running, then sending a message, fails with:
A Retry response button appears next to it, and retrying would ask for the earlier reply again.
The cause:
hydrate()marks the conversation loaded as soon as replayed messages arrive, whileconnectAgentis still attached to the running reply. The next turn is therefore sent while the server still holds the thread lock. CopilotKit reports the refusal asagent_thread_locked, then again asagent_run_failed.Now:
connectAgentreturns. In connect mode it returns once the thread is idle, so after a reload the earlier reply finishes streaming in first, and queued messages then send automatically. The history still shows immediately, and the composer stays usable.agent_thread_locked(for example, another device is running the thread), the message goes back first in the queue and the queue is put on hold, instead of showing the raw lock error. Send queued messages resumes it. The server refused the turn before running it, so no work was done.runQueueddoes not resolve or forget it).runConversationTurnkeeps fix: keep failed turns in saved history from breaking the chat #54's ignore list and reports the first, specific failure;ConversationTurnErrorcarries its code.Interaction with #54
#54 ignores replayed
RUN_ERRORevents (agent_run_error_event) while history loads. BecauseconnectAgentnow also waits for a reply that was running before the reload, that window covers the rest of that reply: if it fails during the wait, its error is not shown as a banner, the same limitation #54 documents, now for longer. The failed turn stays in the transcript, and turns sent after the wait report errors as usual.Verification
tests/conversation-sdk.test.ts(realCopilotKitCorewhere possible), together with fix: keep failed turns in saved history from breaking the chat #54's tests:AgentThreadLockedErrorrejects with codeagent_thread_locked;apps/mobile/test/conversation-queue.test.ts: a refused message is restored first in line, once.pnpm typecheck,biome cion the changed files andpnpm build:webpass.pnpm test: 426 passed, 1 failed, 3 skipped. The failure isworker recovery preserves downloads when metadata cannot be inspected, which needs symlink permission on Windows and fails on main the same way.Integration limits
Tested on web. The change is in shared chat state handling, and native layout is unchanged.