Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function createHarness() {
const onAccepted = vi.fn(() => {
order.push('accepted');
});
const router = { replace: vi.fn(() => order.push('replace')) };
const router = { dismissTo: vi.fn(() => order.push('dismiss')) };
const lock = createLock();
return { order, onAccepted, router, lock };
}
Expand All @@ -28,7 +28,7 @@ describe('exitRemoteSessionWithFeedback — happy path', () => {
vi.clearAllMocks();
});

it('shows "Session exited", calls onAccepted, and replaces with the sessions route', async () => {
it('shows "Session exited", calls onAccepted, and dismisses to the sessions route', async () => {
const { order, onAccepted, router, lock } = createHarness();
const exit = vi.fn(async () => {
order.push('exit');
Expand All @@ -41,9 +41,9 @@ describe('exitRemoteSessionWithFeedback — happy path', () => {
expect(toast.success).toHaveBeenCalledWith('Session exited');
expect(toast.error).not.toHaveBeenCalled();
expect(onAccepted).toHaveBeenCalledTimes(1);
expect(router.replace).toHaveBeenCalledTimes(1);
expect(router.replace).toHaveBeenCalledWith(SESSIONS_ROUTE);
expect(order).toEqual(['exit', 'accepted', 'replace']);
expect(router.dismissTo).toHaveBeenCalledTimes(1);
expect(router.dismissTo).toHaveBeenCalledWith(SESSIONS_ROUTE);
expect(order).toEqual(['exit', 'accepted', 'dismiss']);
expect(lock.current).toBe(false);
});
});
Expand Down Expand Up @@ -71,7 +71,7 @@ describe('exitRemoteSessionWithFeedback — retryable error', () => {
});
expect(toast.success).not.toHaveBeenCalled();
expect(onAccepted).not.toHaveBeenCalled();
expect(router.replace).not.toHaveBeenCalled();
expect(router.dismissTo).not.toHaveBeenCalled();
expect(lock.current).toBe(false);
});

Expand All @@ -97,7 +97,7 @@ describe('exitRemoteSessionWithFeedback — retryable error', () => {
});
expect(lock.current).toBe(false);
expect(onAccepted).not.toHaveBeenCalled();
expect(router.replace).not.toHaveBeenCalled();
expect(router.dismissTo).not.toHaveBeenCalled();
});

it('does not start a second exit mutation while a retry is pending', async () => {
Expand Down Expand Up @@ -137,7 +137,7 @@ describe('exitRemoteSessionWithFeedback — retryable error', () => {
expect(lock.current).toBe(true);
expect(exit).toHaveBeenCalledTimes(2);
expect(onAccepted).not.toHaveBeenCalled();
expect(router.replace).not.toHaveBeenCalled();
expect(router.dismissTo).not.toHaveBeenCalled();

exitPromiseRef.resolve?.();
await vi.waitFor(() => {
Expand Down Expand Up @@ -197,7 +197,7 @@ describe('exitRemoteSessionWithFeedback — non-retryable error', () => {
expect(options?.action).toBeUndefined();
expect(toast.success).not.toHaveBeenCalled();
expect(onAccepted).not.toHaveBeenCalled();
expect(router.replace).not.toHaveBeenCalled();
expect(router.dismissTo).not.toHaveBeenCalled();
expect(lock.current).toBe(false);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { toast } from 'sonner-native';

import { type AgentSessionRouterLike } from '@/components/agents/session-router-like';
import { type Href } from 'expo-router';
import { settleVoiceInputBeforeSubmit } from '@/lib/voice-input/voice-input-submit';

/** Structural subset of Expo Router's router used for post-exit navigation. */
type ExitRemoteSessionRouter = {
dismissTo: (href: Href) => void;
};

type ExitRemoteSessionWithFeedbackInput = {
exit: () => Promise<void>;
onAccepted: () => void;
router: AgentSessionRouterLike;
router: ExitRemoteSessionRouter;
/**
* The composer SubmitLock, exposed as a `{ current: boolean }` ref so the
* retry action can re-acquire the same admission gate the initial send
Expand Down Expand Up @@ -107,7 +112,10 @@ export async function exitRemoteSessionWithFeedback({

toast.success(SESSION_EXITED_MESSAGE);
onAccepted();
router.replace(SESSIONS_ROUTE);
// POP_TO pops the exited `agent-chat` route off the `(app)` stack and
// focuses the existing `(tabs)` entry, so back cannot return to the
// exited session.
router.dismissTo(SESSIONS_ROUTE);
};

await runExit();
Expand Down