You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In Maka Desktop, sending a new message while the current session turn is still running:
The chat view jitters (the live-turn projection flips between two turns).
The session reports an error (conversation error toast).
Root cause
Production Desktop still runs the embedded SessionManager path, which lacks the submit semantics established by #1357:
sessions:send → runtimeKernel.sendMessage → startTurn opens a new top-level turn unconditionally (no active-root guard).
ensureActive reuses the same backend generation; AiSdkBackend is a single mutable-state instance (aborted, currentTurnId, currentRunId, abortController, injectedSteeringMessages, ...). Two concurrent send() calls stomp these fields, so stop / respond / sandbox-boundary route to the wrong turn and one run fails → a streamed error event → session error.
The renderer arms liveTurn for the new turn (T_B), but every text_delta from the still-running T_A flips the projection back; persisted message order interleaves (T_B's user message lands before T_A's; assistant texts land in completion order), so full-list refreshes reorder content and scroll jumps.
CLI (#1087) and Desktop voice (sessions:steer) already route mid-turn text through the steering queue; only Desktop text input opens a parallel turn.
The final shape already exists
#1357 (merged) defines turn.message.submit: "An idle submission starts exactly one root Turn; active submissions become steering or follow-up entries." This bug is the embedded production path not yet applying that semantics. #853 (RFC) / #1357 (Message authority) / #1167 (M3 tracking) remain the long-term home; the M5 production cutover deletes the embedded path.
Desktop main sessions:send asks the embedded runtime steering queue first (runtimeKernel.steer — the embedded counterpart of feat(runtime-host): establish message authority foundation #1357's queue): { kind: 'queued' } injects into the running turn; { kind: 'fallback' } falls through to the existing new-turn sendMessage. Converge this decision in main so chat / bot / voice-fallback / goal entrypoints share one routing point (and return steered so the renderer skips new-turn bookkeeping: armTurnActive, optimistic status, refreshMessagesUntilTurn).
Renderer handleEvent learns steering_message → refreshMessages (currently falls into the default branch), so the injected text appears in the transcript (it is already persisted as a role: 'user' RuntimeEvent with steering: true).
Verification
Runtime repro (fake backend + MemoryAgentRunStore): two concurrent sendMessage calls on one session must not parallel-run or interleave message order.
Desktop E2E (fake backend): send while running → no second turn, steering text visible, no error.
Symptom
In Maka Desktop, sending a new message while the current session turn is still running:
Root cause
Production Desktop still runs the embedded SessionManager path, which lacks the submit semantics established by #1357:
sessions:send→runtimeKernel.sendMessage→startTurnopens a new top-level turn unconditionally (no active-root guard).ensureActivereuses the same backend generation;AiSdkBackendis a single mutable-state instance (aborted,currentTurnId,currentRunId,abortController,injectedSteeringMessages, ...). Two concurrentsend()calls stomp these fields, so stop / respond / sandbox-boundary route to the wrong turn and one run fails → a streamederrorevent → session error.liveTurnfor the new turn (T_B), but everytext_deltafrom the still-running T_A flips the projection back; persisted message order interleaves (T_B's user message lands before T_A's; assistant texts land in completion order), so full-list refreshes reorder content and scroll jumps.CLI (#1087) and Desktop voice (
sessions:steer) already route mid-turn text through the steering queue; only Desktop text input opens a parallel turn.The final shape already exists
#1357 (merged) defines
turn.message.submit: "An idle submission starts exactly one root Turn; active submissions become steering or follow-up entries." This bug is the embedded production path not yet applying that semantics. #853 (RFC) / #1357 (Message authority) / #1167 (M3 tracking) remain the long-term home; the M5 production cutover deletes the embedded path.Proposed fix (short term, aligns with #1357)
sessions:sendasks the embedded runtime steering queue first (runtimeKernel.steer— the embedded counterpart of feat(runtime-host): establish message authority foundation #1357's queue):{ kind: 'queued' }injects into the running turn;{ kind: 'fallback' }falls through to the existing new-turnsendMessage. Converge this decision in main so chat / bot / voice-fallback / goal entrypoints share one routing point (and returnsteeredso the renderer skips new-turn bookkeeping:armTurnActive, optimistic status,refreshMessagesUntilTurn).handleEventlearnssteering_message→refreshMessages(currently falls into the default branch), so the injected text appears in the transcript (it is already persisted as arole: 'user'RuntimeEvent withsteering: true).Verification
sendMessagecalls on one session must not parallel-run or interleave message order.