fix(sessions): scope default to server CWD, not $HOME (#32) (#33)

Recent sessions disappeared from the list when opencode serve was launched
outside $HOME (e.g. ~/workspace/opencode). The list path scoped to
server.home, but opencode-server resolves x-opencode-directory to a project
id by exact-match, not subtree prefix. $HOME mapped to the synthetic
'global' project, which never contained the user's workspace sessions.

Change the single-source-of-truth rule to return null (no scope header) when
the connection has no explicit directory. The server then uses its own CWD
project — the same project sessions are actually created in. Both list and
create paths still derive from sessionScopeDirectory, so #10's drift cure is
preserved.

Verified against 100.108.64.76:4096: no header returns the recent workspace
sessions (Opencode npm install, autopilot_exit, ...); header=$HOME returns
only the empty global project.

Closes #32
This commit is contained in:
Den
2026-06-21 22:01:05 -07:00
committed by GitHub
parent 0779a11284
commit 79d26f0fbd
2 changed files with 27 additions and 8 deletions

View File

@@ -3,10 +3,12 @@ import assert from "node:assert/strict"
import { sessionScopeDirectory } from "./sessionScope.ts"
// Bug #10 regression guard: list and create must resolve to the SAME scope.
// These cases pin the shared rule both call sites rely on.
// Bug #32 regression guard: the default scope must be "no header" (null), not
// the server's home directory. The home dir resolves to the "global" project on
// the opencode server and hides real workspace sessions.
test("no explicit directory + known home -> scope to home", () => {
assert.equal(sessionScopeDirectory(false, "/home/user"), "/home/user")
test("no explicit directory + known home -> default client (null), home ignored (#32)", () => {
assert.equal(sessionScopeDirectory(false, "/home/user"), null)
})
test("no explicit directory + unknown home -> default client (null)", () => {
@@ -20,7 +22,7 @@ test("explicit directory -> default client (null), home ignored", () => {
assert.equal(sessionScopeDirectory(true, null), null)
})
test("create and list resolve identically across all inputs (no drift)", () => {
test("create and list resolve identically across all inputs (no drift, #10)", () => {
for (const hasDir of [true, false]) {
for (const home of ["/home/user", null, undefined, ""]) {
const listScope = sessionScopeDirectory(hasDir, home)
@@ -29,3 +31,10 @@ test("create and list resolve identically across all inputs (no drift)", () => {
}
}
})
test("server directory != home: rule returns null so server uses its CWD project (#32)", () => {
// Reproduces the real-world setup: server launched in ~/workspace/opencode
// while $HOME is /home/azureuser. Old rule returned "/home/azureuser" (global
// project, empty). New rule returns null so server uses its CWD project.
assert.equal(sessionScopeDirectory(false, "/home/azureuser"), null)
})

View File

@@ -6,11 +6,21 @@
// a freshly created session was invisible. Both call sites now derive their client
// from this one function, so they cannot disagree again.
//
// Bug #32 ("recent sessions missing"): the previous rule scoped to the server's
// home directory when no explicit directory was set. opencode-server resolves the
// x-opencode-directory header to a project id by exact-match, NOT subtree prefix.
// $HOME typically maps to the synthetic "global" project, which never contains
// the user's real workspace sessions (those live under e.g. ~/workspace/foo, a
// different project id). The list looked empty even when sessions existed.
//
// Rule: when the active connection pins an explicit directory, use the default
// client (it is already scoped to that directory). Otherwise, if we know the
// server's home directory, scope to it. If home is unknown, fall back to default.
// client (it is already scoped to that directory). Otherwise, send no scope at
// all so the server uses its own CWD — i.e. the project where opencode serve was
// launched, which is the project the user actually works in. The `home` argument
// is accepted for backward compatibility but intentionally ignored.
export function sessionScopeDirectory(hasExplicitDirectory: boolean, home: string | null | undefined): string | null {
export function sessionScopeDirectory(hasExplicitDirectory: boolean, _home?: string | null | undefined): string | null {
void _home
if (hasExplicitDirectory) return null
return home ? home : null
return null
}