authorgravatar for chris@xenforo.comChris Deeming <chris@xenforo.com> 2026-08-14 12:35:10+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-14 13:35:10+02:00
log184d8ef33b8f42869fb84f66a33984185b81dc47
tree71b80a6f1b61967865671fadbb6d6f7f3985bd77
parent038560e58036d51b2576b3c2cd9170a194cefe9e
signature Signed by PGP key B5690EEEBB952194

fix(mobile): steer active turns by default (#6543)


7 files changed, 27 insertions(+), 27 deletions(-)

apps/mobile/src/features/threads/ThreadComposer.tsx+1-4
......@@ -105,7 +105,6 @@ export interface ThreadComposerProps {
105105 readonly selectedThread: OrchestrationThreadShell;
106106 readonly serverConfig: T3ServerConfig | null;
107107 readonly queueCount: number;
108 readonly activeThreadBusy: boolean;
109108 readonly environmentId: EnvironmentId;
110109 readonly projectCwd: string | null;
111110 readonly editorRef?: RefObject<ComposerEditorHandle | null>;
......@@ -327,9 +326,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
327326 props.selectedThread.session?.status === "starting";
328327
329328 const sendLabel =
330 props.connectionState !== "connected" || props.activeThreadBusy || props.queueCount > 0
331 ? "Queue"
332 : "Send";
329 props.connectionState !== "connected" || props.queueCount > 0 ? "Queue" : "Send";
333330 const currentModelSelection = props.selectedThread.modelSelection;
334331 const currentRuntimeMode = props.selectedThread.runtimeMode;
335332 const connectionStatus = composerConnectionStatus({
apps/mobile/src/features/threads/ThreadDetailScreen.tsx-2
......@@ -101,7 +101,6 @@ export interface ThreadDetailScreenProps {
101101 readonly threadSyncStatus?: EnvironmentThreadStatus;
102102 /** Non-null when older turns exist beyond the loaded window. */
103103 readonly loadEarlier?: { readonly loading: boolean; readonly onLoadEarlier: () => void } | null;
104 readonly activeThreadBusy: boolean;
105104 readonly environmentId: EnvironmentId;
106105 readonly projectWorkspaceRoot: string | null;
107106 readonly threadCwd: string | null;
......@@ -727,7 +726,6 @@ export const ThreadDetailScreen = memo(function ThreadDetailScreen(props: Thread
727726 selectedThread={props.selectedThread}
728727 serverConfig={props.serverConfig}
729728 queueCount={props.selectedThreadQueueCount}
730 activeThreadBusy={props.activeThreadBusy}
731729 environmentId={props.environmentId}
732730 projectCwd={props.projectWorkspaceRoot}
733731 bottomInset={composerBottomInset}
apps/mobile/src/features/threads/ThreadRouteScreen.tsx-1
......@@ -785,7 +785,6 @@ function ThreadRouteContent(
785785 connectionStateLabel={routeConnectionState}
786786 threadSyncStatus={selectedThreadDetailState.status}
787787 loadEarlier={loadEarlierTurns}
788 activeThreadBusy={composer.activeThreadBusy}
789788 environmentId={selectedThread.environmentId}
790789 projectWorkspaceRoot={selectedThreadProject?.workspaceRoot ?? null}
791790 threadCwd={selectedThreadCwd}
apps/mobile/src/state/thread-outbox-model.ts+1-1
......@@ -169,7 +169,7 @@ export function resolveThreadOutboxDeliveryAction(input: {
169169 if (!input.threadExists) {
170170 return input.shellStatus === "live" ? "remove" : "wait";
171171 }
172 return input.environmentConnected && !input.threadBusy ? "send" : "wait";
172 return input.environmentConnected ? "send" : "wait";
173173}
174174
175175/**
apps/mobile/src/state/thread-outbox.test.ts+21
......@@ -487,6 +487,27 @@ describe("thread outbox", () => {
487487 ).toBe("send");
488488 });
489489
490 it("sends existing-thread messages whenever connected so queued messages can steer", () => {
491 expect(
492 resolveThreadOutboxDeliveryAction({
493 isCreation: false,
494 threadExists: true,
495 shellStatus: "live",
496 environmentConnected: true,
497 threadBusy: true,
498 }),
499 ).toBe("send");
500 expect(
501 resolveThreadOutboxDeliveryAction({
502 isCreation: false,
503 threadExists: true,
504 shellStatus: "live",
505 environmentConnected: false,
506 threadBusy: true,
507 }),
508 ).toBe("wait");
509 });
510
490511 it("sends queued creations once connected and live, removing already-created ones", () => {
491512 expect(
492513 resolveThreadOutboxDeliveryAction({
apps/mobile/src/state/use-thread-composer-state.ts-5
......@@ -129,10 +129,6 @@ export function useThreadComposerState() {
129129 );
130130 }, [selectedThreadDetail, selectedThreadSessionActivity, selectedThreadShell]);
131131
132 const activeThreadBusy =
133 !!selectedThread &&
134 (selectedThread.session?.status === "running" || selectedThread.session?.status === "starting");
135
136132 const onSendMessage = useCallback(async () => {
137133 if (!selectedThreadShell) {
138134 return null;
......@@ -308,7 +304,6 @@ export function useThreadComposerState() {
308304 modelSelection,
309305 runtimeMode,
310306 interactionMode,
311 activeThreadBusy,
312307 onChangeDraftMessage,
313308 onPickDraftImages,
314309 onPasteIntoDraft,
apps/mobile/src/state/use-thread-outbox-drain.ts+4-14
......@@ -37,7 +37,7 @@ import {
3737 type QueuedThreadMessage,
3838 type ThreadOutboxCommandStage,
3939} from "./thread-outbox-model";
40import { environmentThreadShells, threadEnvironment } from "./threads";
40import { threadEnvironment } from "./threads";
4141import { useAtomCommand } from "./use-atom-command";
4242import {
4343 editingQueuedMessageIdsAtom,
......@@ -362,22 +362,12 @@ export function useThreadOutboxDrain(): void {
362362 return true;
363363 }
364364 // The guards evaluated before the confirmation await are stale by now:
365 // the thread may have gone busy, or the user may have opened this
366 // message in the editor. Re-read both and defer to the next drain pass
367 // (returning true skips the failure/backoff path) rather than sending
368 // a payload the user is editing or racing an active turn.
365 // the user may have opened this message in the editor. Re-read that
366 // guard and defer to the next drain pass (returning true skips the
367 // failure/backoff path) rather than sending a payload being edited.
369368 if (appAtomRegistry.get(editingQueuedMessageIdsAtom)[nextQueuedMessage.messageId]) {
370369 return true;
371370 }
372 const freshThread = findThread(
373 appAtomRegistry.get(environmentThreadShells.threadShellsAtom),
374 nextQueuedMessage,
375 );
376 const freshThreadBusy =
377 freshThread?.session?.status === "running" || freshThread?.session?.status === "starting";
378 if (deliveryAction === "send" && creation === undefined && freshThreadBusy) {
379 return true;
380 }
381371 return deliveryAction === "remove"
382372 ? removeQueuedMessage("[thread-outbox] failed to remove message for a missing thread")
383373 : creation !== undefined