Skip to content

Commit ec037bf

Browse files
sheehanmunimclaude
andcommitted
revert: drop upstream pingdotgg#12172 Pause session
Its sidebar "Paused" status matched every cleanly stopped session, so threads closed by the idle reaper, settling or an app restart all read as paused. The idle reaper and settle already free provider memory, so the manual action added little. Removes the menu item, status, docs, README row and guard line. This reverts commit 03eb9da. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 9f1a0c9 commit ec037bf

18 files changed

Lines changed: 6 additions & 340 deletions

‎README.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ This table lists only the differences. Once T3 Code ships a feature MT Code had
126126
| **Conversation import and forking** — import Claude Code / Codex history, fork a conversation into a new thread, self-hosted Android push | ✅ | ❌ |
127127
| **Scheduled sends** — queue a message for a later time, with recurrence | ✅ | ❌ |
128128
| **Processes dialog** — see and stop listeners agents left running | ✅ | ❌ |
129-
| **Pause session** — stop a thread's provider session to free memory without settling it | ✅ | ❌ |
130129
| **Script threads from the CLI** — `t3 thread start` and `t3 thread send` | ✅ | ❌ |
131130
| **Custom thread title instructions**, and drag to reorder or hide provider models | ✅ | ❌ |
132131
| **Hosted web app** — MT Code's own was retired; use T3 Connect or your own tunnel instead | ❌ | ✅ [app.t3.codes](https://app.t3.codes) |

‎apps/mobile/src/features/home/HomeRouteScreen.tsx‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ export function HomeRouteScreen() {
4949
archiveThread,
5050
confirmDeleteThread,
5151
settleThread,
52-
pauseThread,
5352
snoozeThread,
5453
unsnoozeThread,
5554
pinThread,
@@ -217,7 +216,6 @@ export function HomeRouteScreen() {
217216
onArchiveThread={archiveThread}
218217
onDeleteThread={confirmDeleteThread}
219218
onSettleThread={settleThread}
220-
onPauseThread={pauseThread}
221219
onSnoozeThread={snoozeThread}
222220
onUnsnoozeThread={unsnoozeThread}
223221
onUnsettleThread={unsettleThread}

‎apps/mobile/src/features/home/HomeScreen.tsx‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ interface HomeScreenProps {
115115
readonly onDeleteThread: (thread: EnvironmentThreadShell) => void;
116116
/** Resolves true iff the settle was dispatched and succeeded. */
117117
readonly onSettleThread: (thread: EnvironmentThreadShell) => Promise<boolean>;
118-
readonly onPauseThread: (thread: EnvironmentThreadShell) => Promise<boolean>;
119118
readonly onSnoozeThread: (
120119
thread: EnvironmentThreadShell,
121120
snoozedUntil: string,
@@ -514,12 +513,6 @@ export function HomeScreen(props: HomeScreenProps) {
514513
// the partition works directly off live shells — no snapshot merging or
515514
// optimistic holds.
516515
const handleSettleThread = props.onSettleThread;
517-
const handlePauseThread = useCallback(
518-
(thread: EnvironmentThreadShell) => {
519-
void props.onPauseThread(thread);
520-
},
521-
[props.onPauseThread],
522-
);
523516
const handleSnoozeThread = useCallback(
524517
(thread: EnvironmentThreadShell, snoozedUntil: string) => {
525518
void props.onSnoozeThread(thread, snoozedUntil);
@@ -923,7 +916,6 @@ export function HomeScreen(props: HomeScreenProps) {
923916
onSnoozeThread={handleSnoozeThread}
924917
onUnsnoozeThread={handleUnsnoozeThread}
925918
onUnsettleThread={handleUnsettleThread}
926-
onPauseThread={handlePauseThread}
927919
onPinThread={handlePinThread}
928920
onUnpinThread={handleUnpinThread}
929921
onSetThreadAutoSettle={handleSetThreadAutoSettle}
@@ -935,7 +927,6 @@ export function HomeScreen(props: HomeScreenProps) {
935927
},
936928
[
937929
handleDeleteThread,
938-
handlePauseThread,
939930
activeReorderEnvironmentIds,
940931
threadMovePlanners,
941932
pendingOrder,

‎apps/mobile/src/features/home/useThreadListActions.ts‎

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import type { ThreadMoveDestination } from "../threads/threadOrder";
22
import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
3-
import {
4-
canPauseSession,
5-
canSnooze,
6-
effectiveSnoozed,
7-
} from "@t3tools/client-runtime/state/thread-settled";
3+
import { canSnooze, effectiveSnoozed } from "@t3tools/client-runtime/state/thread-settled";
84
import * as Cause from "effect/Cause";
95
import * as Haptics from "expo-haptics";
106
import { useCallback, useRef } from "react";
@@ -245,7 +241,6 @@ export function useThreadListActions(): {
245241
readonly archiveThread: (thread: EnvironmentThreadShell) => void;
246242
readonly confirmDeleteThread: (thread: EnvironmentThreadShell) => void;
247243
readonly settleThread: (thread: EnvironmentThreadShell) => Promise<boolean>;
248-
readonly pauseThread: (thread: EnvironmentThreadShell) => Promise<boolean>;
249244
readonly snoozeThread: (thread: EnvironmentThreadShell, snoozedUntil: string) => Promise<boolean>;
250245
readonly unsnoozeThread: (thread: EnvironmentThreadShell) => Promise<boolean>;
251246
readonly unsettleThread: (thread: EnvironmentThreadShell) => Promise<boolean>;
@@ -264,9 +259,6 @@ export function useThreadListActions(): {
264259
readonly regenerateThreadTitle: (thread: EnvironmentThreadShell) => Promise<boolean>;
265260
} {
266261
const executeAction = useThreadActionExecutor();
267-
const stopSessionMutation = useAtomCommand(threadEnvironment.stopSession, {
268-
reportFailure: false,
269-
});
270262
const snoozeMutation = useAtomCommand(threadEnvironment.snooze, { reportFailure: false });
271263
const unsnoozeMutation = useAtomCommand(threadEnvironment.unsnooze, { reportFailure: false });
272264
const pinMutation = useAtomCommand(threadEnvironment.pin, { reportFailure: false });
@@ -290,41 +282,6 @@ export function useThreadListActions(): {
290282
async (thread: EnvironmentThreadShell) => (await executeAction("settle", thread)) === true,
291283
[executeAction],
292284
);
293-
const pauseThread = useCallback(
294-
async (thread: EnvironmentThreadShell) => {
295-
// Same guard as web/archive: never yank a thread mid-turn. Interrupt
296-
// first, then pause. Resume is the next message (the server recreates
297-
// a stopped session on turn start).
298-
if (!canPauseSession(thread)) {
299-
Alert.alert(
300-
"Could not pause session",
301-
thread.session?.status === "running"
302-
? "This thread is working. Interrupt it first, then try again."
303-
: thread.session?.status === "starting"
304-
? "This thread is still starting. Try again once it's idle."
305-
: "This thread has no active session to pause.",
306-
);
307-
return false;
308-
}
309-
selectionHaptic();
310-
const result = await stopSessionMutation({
311-
environmentId: thread.environmentId,
312-
input: { threadId: thread.id },
313-
});
314-
if (result._tag === "Failure") {
315-
const error = Cause.squash(result.cause);
316-
Alert.alert(
317-
"Could not pause session",
318-
error instanceof Error && error.message.trim().length > 0
319-
? error.message
320-
: "The session could not be paused.",
321-
);
322-
return false;
323-
}
324-
return true;
325-
},
326-
[stopSessionMutation],
327-
);
328285
const snoozeThread = useCallback(
329286
async (thread: EnvironmentThreadShell, snoozedUntil: string) => {
330287
const key = scopedThreadKey(thread.environmentId, thread.id);
@@ -781,7 +738,6 @@ export function useThreadListActions(): {
781738
archiveThread,
782739
confirmDeleteThread,
783740
settleThread,
784-
pauseThread,
785741
snoozeThread,
786742
unsnoozeThread,
787743
unsettleThread,

‎apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ function ThreadNavigationSidebarPane(
167167
archiveThread,
168168
confirmDeleteThread,
169169
settleThread,
170-
pauseThread,
171170
snoozeThread,
172171
unsnoozeThread,
173172
unsettleThread,
@@ -972,7 +971,6 @@ function ThreadNavigationSidebarPane(
972971
onSnoozeThread={snoozeThread}
973972
onUnsnoozeThread={unsnoozeThread}
974973
onUnsettleThread={unsettleThread}
975-
onPauseThread={pauseThread}
976974
onPinThread={pinThread}
977975
onUnpinThread={unpinThread}
978976
onSetThreadAutoSettle={setThreadAutoSettle}

‎apps/mobile/src/features/threads/thread-list-v2-items.tsx‎

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import type {
1818
import type { EnvironmentThreadSearchMatch } from "@t3tools/client-runtime/state/thread-search";
1919
import type { EnvironmentMachineKind } from "@t3tools/contracts";
2020
import {
21-
canPauseSession,
2221
canSnooze,
2322
resolveSnoozePresets,
2423
threadUsageLimitResetsAt,
@@ -72,7 +71,6 @@ const STATUS_LABEL_BY_STATUS: Partial<
7271
input: { label: "Input", className: "text-foreground-secondary" },
7372
working: { label: "Working", className: "text-adaptive-sky-600-400" },
7473
failed: { label: "Failed", className: "text-danger-foreground" },
75-
paused: { label: "Paused", className: "text-foreground-secondary" },
7674
};
7775

7876
function threadTimeLabel(thread: EnvironmentThreadShell): string {
@@ -431,7 +429,6 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
431429
readonly onSnoozeThread: (thread: EnvironmentThreadShell, snoozedUntil: string) => void;
432430
readonly onUnsnoozeThread: (thread: EnvironmentThreadShell) => void;
433431
readonly onUnsettleThread: (thread: EnvironmentThreadShell) => void;
434-
readonly onPauseThread: (thread: EnvironmentThreadShell) => void;
435432
readonly onArchiveThread: (thread: EnvironmentThreadShell) => void;
436433
readonly onPinThread: (thread: EnvironmentThreadShell) => void;
437434
readonly onUnpinThread: (thread: EnvironmentThreadShell) => void;
@@ -478,7 +475,6 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
478475
onSnoozeThread,
479476
onUnsnoozeThread,
480477
onUnsettleThread,
481-
onPauseThread,
482478
onArchiveThread,
483479
onPinThread,
484480
onUnpinThread,
@@ -519,7 +515,6 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
519515
);
520516
const handleUnsnooze = useCallback(() => onUnsnoozeThread(thread), [onUnsnoozeThread, thread]);
521517
const handleUnsettle = useCallback(() => onUnsettleThread(thread), [onUnsettleThread, thread]);
522-
const handlePause = useCallback(() => onPauseThread(thread), [onPauseThread, thread]);
523518
const handlePin = useCallback(() => onPinThread(thread), [onPinThread, thread]);
524519
const handleUnpin = useCallback(() => onUnpinThread(thread), [onUnpinThread, thread]);
525520
const handleSetAutoSettle = useCallback(
@@ -648,16 +643,6 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
648643
],
649644
[props.titleRegenerationSupported, thread.titleRegeneration],
650645
);
651-
// Pause parks the provider session (saves RAM) while the thread stays
652-
// active; resume is the next message. Offered only for pausable cards —
653-
// already-paused and never-started threads show no item.
654-
const pauseMenuItems = useMemo<MenuAction[]>(
655-
() =>
656-
canPauseSession(thread) && !snoozedRow && !canUnsettle
657-
? [{ id: "pause", title: "Pause session", image: "pause" }]
658-
: [],
659-
[thread, snoozedRow, canUnsettle],
660-
);
661646
const snoozableCardMenuActions = useMemo<MenuAction[]>(
662647
() => [
663648
{ id: "settle", title: "Settle", image: "checkmark" },
@@ -667,24 +652,22 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
667652
image: "clock",
668653
subactions: snoozePresetActions,
669654
},
670-
...pauseMenuItems,
671655
...arrangementMenuItems,
672656
...titleMenuItems,
673657
...autoSettleMenuItems,
674658
{ id: "delete", title: "Delete", image: "trash", attributes: { destructive: true } },
675659
],
676-
[arrangementMenuItems, autoSettleMenuItems, pauseMenuItems, snoozePresetActions, titleMenuItems],
660+
[arrangementMenuItems, autoSettleMenuItems, snoozePresetActions, titleMenuItems],
677661
);
678662
const cardMenuActions = useMemo<MenuAction[]>(
679663
() => [
680664
CARD_MENU_ACTIONS[0]!,
681-
...pauseMenuItems,
682665
...arrangementMenuItems,
683666
...titleMenuItems,
684667
...autoSettleMenuItems,
685668
...CARD_MENU_ACTIONS.slice(1),
686669
],
687-
[arrangementMenuItems, autoSettleMenuItems, pauseMenuItems, titleMenuItems],
670+
[arrangementMenuItems, autoSettleMenuItems, titleMenuItems],
688671
);
689672
// Settled and snoozed rows keep the setting too, matching web where every
690673
// row shares one menu builder.
@@ -722,7 +705,6 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
722705
({ nativeEvent }: { readonly nativeEvent: { readonly event: string } }) => {
723706
if (nativeEvent.event === "new-thread-on-branch") onNewThreadOnBranch(thread);
724707
if (nativeEvent.event === "settle") handleSettle();
725-
if (nativeEvent.event === "pause") handlePause();
726708
if (nativeEvent.event === "unsettle") handleUnsettle();
727709
if (nativeEvent.event === "unsnooze") handleUnsnooze();
728710
if (nativeEvent.event === "pin") handlePin();
@@ -760,7 +742,6 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
760742
thread,
761743
handleArchive,
762744
handleDelete,
763-
handlePause,
764745
handleRegenerateTitle,
765746
handleRename,
766747
handleMoveDown,

‎apps/mobile/src/features/threads/threadListV2.test.ts‎

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -186,42 +186,6 @@ describe("resolveThreadListV2Status", () => {
186186
"ready",
187187
);
188188
});
189-
190-
it("resolves paused for a stopped session", () => {
191-
const thread = makeThread({
192-
id: ThreadId.make("t"),
193-
title: "t",
194-
session: {
195-
threadId: ThreadId.make("t"),
196-
status: "stopped",
197-
providerName: "Codex",
198-
providerInstanceId: ProviderInstanceId.make("codex"),
199-
runtimeMode: "full-access",
200-
activeTurnId: null,
201-
lastError: null,
202-
updatedAt: NOW,
203-
},
204-
});
205-
expect(resolveThreadListV2Status(thread)).toBe("paused");
206-
});
207-
208-
it("keeps a stopped session with a failure classified as failed", () => {
209-
const thread = makeThread({
210-
id: ThreadId.make("t"),
211-
title: "t",
212-
session: {
213-
threadId: ThreadId.make("t"),
214-
status: "stopped",
215-
providerName: "Codex",
216-
providerInstanceId: ProviderInstanceId.make("codex"),
217-
runtimeMode: "full-access",
218-
activeTurnId: null,
219-
lastError: "boom",
220-
updatedAt: NOW,
221-
},
222-
});
223-
expect(resolveThreadListV2Status(thread)).toBe("failed");
224-
});
225189
});
226190

227191
describe("queued messages keep a settled thread active", () => {

‎apps/mobile/src/features/threads/threadListV2.ts‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export { snoozeWakeLabel };
3434
* (approval), "in motion" (working), and "broken" (failed). Ready is the
3535
* unlabeled resting state.
3636
*/
37-
export type ThreadListV2Status = "approval" | "input" | "working" | "failed" | "paused" | "ready";
37+
export type ThreadListV2Status = "approval" | "input" | "working" | "failed" | "ready";
3838
export type ThreadListV2SwipeAction = "archive" | "settle" | "unsettle" | "snooze" | "unsnooze";
3939

4040
export function resolveThreadListV2SnoozeMenuSelection(input: {
@@ -148,11 +148,6 @@ export function resolveThreadListV2Status(
148148
if (thread.session?.status === "error") {
149149
return "failed";
150150
}
151-
if (thread.session != null && thread.session.status === "stopped") {
152-
// A stopped session carrying a failure still reads as failed, never as
153-
// the calm Paused state. Mirrors the web sidebar's Paused pill gating.
154-
return thread.session.lastError != null ? "failed" : "paused";
155-
}
156151
return "ready";
157152
}
158153

0 commit comments

Comments
 (0)