diff --git a/packages/tui/src/app.tsx b/packages/tui/src/app.tsx
index 26ad36db955e..f98129c2d1e0 100644
--- a/packages/tui/src/app.tsx
+++ b/packages/tui/src/app.tsx
@@ -646,7 +646,10 @@ function App(props: { pair?: DialogPairCredentials }) {
run: () => {
route.navigate({
type: "home",
- location: route.data.type === "session" ? data.session.get(route.data.sessionID)?.location : undefined,
+ location:
+ route.data.type === "session"
+ ? (data.session.get(route.data.sessionID)?.location ?? location.ref)
+ : undefined,
})
dialog.clear()
},
diff --git a/packages/tui/src/component/dialog-open.tsx b/packages/tui/src/component/dialog-open.tsx
index 2ee57aa03bda..bfef6d54c784 100644
--- a/packages/tui/src/component/dialog-open.tsx
+++ b/packages/tui/src/component/dialog-open.tsx
@@ -84,6 +84,7 @@ export function DialogOpen() {
value: { type: "session", sessionID: session.id } as OpenTarget,
category: "Sessions",
footer: `${name ? `${Locale.truncate(name, 20)} ยท ` : ""}${timeAgo(session.time.updated)}`,
+ onSelect: () => location.set(session.location),
gutter: running
? () =>
: tabs.has(session.id)
diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx
index 79c36ee4c8fe..bffa7cf15c1f 100644
--- a/packages/tui/src/routes/session/index.tsx
+++ b/packages/tui/src/routes/session/index.tsx
@@ -143,8 +143,8 @@ export function Session() {
const promptRef = usePromptRef()
const session = createMemo(() => data.session.get(route.sessionID))
const messages = () => data.session.message.list(route.sessionID)
- const location = createMemo(() => session()?.location)
const currentLocation = useLocation()
+ const location = createMemo(() => session()?.location ?? currentLocation.ref)
createEffect(() => currentLocation.set(location()))
diff --git a/packages/tui/test/cli/tui/dialog-open.test.tsx b/packages/tui/test/cli/tui/dialog-open.test.tsx
new file mode 100644
index 000000000000..14ec4c7ec88d
--- /dev/null
+++ b/packages/tui/test/cli/tui/dialog-open.test.tsx
@@ -0,0 +1,106 @@
+/** @jsxImportSource @opentui/solid */
+import { expect, test } from "bun:test"
+import { testRender } from "@opentui/solid"
+import { mkdtempSync, rmSync } from "fs"
+import { tmpdir } from "os"
+import path from "path"
+import { onMount } from "solid-js"
+import { DialogOpen } from "../../../src/component/dialog-open"
+import { ConfigProvider } from "../../../src/config"
+import { ClientProvider } from "../../../src/context/client"
+import { DataProvider, useData } from "../../../src/context/data"
+import { Keymap } from "../../../src/context/keymap"
+import { LocationProvider, useLocation } from "../../../src/context/location"
+import { RouteProvider, useRoute } from "../../../src/context/route"
+import { TuiAppProvider } from "../../../src/context/runtime"
+import { SessionTabsProvider } from "../../../src/context/session-tabs"
+import { StorageProvider } from "../../../src/context/storage"
+import { ThemeProvider } from "../../../src/context/theme"
+import { DialogProvider, useDialog } from "../../../src/ui/dialog"
+import { ToastProvider } from "../../../src/ui/toast"
+import { createApi, createEventStream, createFetch, json } from "../../fixture/tui-client"
+import { TestTuiContexts } from "../../fixture/tui-environment"
+import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
+
+test("selecting an unhydrated session preserves its location", async () => {
+ const state = mkdtempSync(path.join(tmpdir(), "opencode-dialog-open-"))
+ const events = createEventStream()
+ const remote = { directory: "/tmp/opencode/remote", workspaceID: "ws_remote" }
+ const calls = createFetch((url) => {
+ if (url.pathname !== "/api/session") return
+ return json({
+ data: [
+ {
+ id: "ses_remote",
+ projectID: "proj_remote",
+ cost: 0,
+ tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
+ time: { created: 1, updated: 2 },
+ title: "Remote session",
+ location: remote,
+ },
+ ],
+ cursor: {},
+ })
+ }, events)
+ let route!: ReturnType
+ let location!: ReturnType
+ let data!: ReturnType
+
+ function Probe() {
+ const dialog = useDialog()
+ route = useRoute()
+ location = useLocation()
+ data = useData()
+ onMount(() => dialog.replace(() => ))
+ return null
+ }
+
+ const app = await testRender(
+ () => (
+
+
+
+
+
+
+
+
+
+
+
+ Promise.resolve({}) }}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ),
+ { width: 100, height: 30, kittyKeyboard: true },
+ )
+ app.renderer.start()
+
+ try {
+ await app.waitForFrame((frame) => frame.includes("Remote session"))
+ expect(data.session.get("ses_remote")).toBeUndefined()
+
+ app.mockInput.pressEnter()
+ await app.waitFor(() => route.data.type === "session")
+
+ expect(route.data).toEqual({ type: "session", sessionID: "ses_remote" })
+ expect(location.ref).toEqual(remote)
+ } finally {
+ app.renderer.destroy()
+ rmSync(state, { recursive: true, force: true })
+ }
+})