feat(sessions): list and scope sessions per Portal instance#38
Open
Nowaker wants to merge 1 commit intohosenur:mainfrom
Open
feat(sessions): list and scope sessions per Portal instance#38Nowaker wants to merge 1 commit intohosenur:mainfrom
Nowaker wants to merge 1 commit intohosenur:mainfrom
Conversation
|
Someone is attempting to deploy a commit to the hosenur's projects Team on Vercel. A member of the Team first needs to authorize it. |
OpenCode's stable GET /session filters its results to the OpenCode
process's currently active project. As a result, Portal's session list
shows sessions from only one project at a time, even though the
underlying OpenCode database holds sessions for many. Users with many
projects experience a confusing flat-but-incomplete list.
After fixing the visibility, the opposite problem also matters: a
Portal instance started against a sub-tree (e.g.
`--directory ~/projects/work`) should not have to scroll through
sessions from unrelated trees. Each instance should focus on the
sub-tree it was started for.
This PR makes two coordinated changes to
`/api/opencode/[port]/sessions`:
1) List all sessions across all projects.
Switch from `client.session.list()` (which hits GET /session) to
GET /experimental/session, the same endpoint OpenCode's first-party
web UI uses. Falls back to the SDK call when the experimental
endpoint is not OK, so users on stale OpenCode binaries keep
working.
2) Scope the result to the Portal instance's --directory by default.
The instance's directory (from `~/.portal.json`) is read on each
request and used as a prefix filter. Two query overrides are
honored:
- ?scope=all — return everything (the unfiltered
behavior).
- ?directory=<path> — scope to an arbitrary path. Useful for
drilling into a specific project from a
Portal instance whose --directory is
broader.
Path matching is path-component-aware: `/foo/bar` matches
`/foo/bar` and `/foo/bar/...` but NOT `/foo/bar-baz`.
Implemented with `path.resolve` plus an explicit `/` boundary
check. Instances whose --directory is `/` are treated as unscoped.
Two small helpers are added next to the existing client cache in
`opencode-client.ts` (`getOpencodeBaseUrl`, `getInstanceDirectory`)
so handlers don't have to reimplement hostname resolution and
instance lookup from `~/.portal.json`.
Behavior change: a Portal instance with --directory ~/projects/foo
no longer shows sessions from other projects unless ?scope=all is
passed. This is intentional and matches user expectation when running
multiple Portal instances against different sub-trees.
The /experimental/* namespace is officially marked experimental
upstream. In practice it is reasonably stable since OpenCode's own UI
depends on it. The SDK fallback covers the rest.
2160b74 to
108ab66
Compare
hosenur
requested changes
Apr 26, 2026
Owner
hosenur
left a comment
There was a problem hiding this comment.
Blocking issue:
- apps/web/src/server/opencode/[port]/sessions.ts:36-47 fetches
/experimental/sessionwithout a limit/cursor, then applies the subtree filter locally. Upstream defaults this endpoint to 100 globally sorted rows, so once there are more than 100 newer sessions outside the selected instance directory, this route can return an empty or truncated list for the selected directory even though matching sessions exist. Please either page throughx-next-cursorbefore filtering, or otherwise request enough data from upstream in a way that preserves the intended scoped list.
Verified: bunx turbo run build --filter=app passes on the PR worktree. I also checked the installed @opencode-ai/sdk v2; it exposes client.experimental.session.list(), but upstream directory filtering is exact, so the local subtree filter still needs pagination if it fetches globally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
OpenCode's stable
GET /sessionendpoint filters its results to the OpenCode process's currently active project. As a consequence, Portal's session list shows sessions from only one project at a time, even though the underlying OpenCode database holds sessions for many.The flat-but-single-project list is confusing for users who maintain many projects: when you point Portal at a directory, you see your global sessions plus that directory's sessions, but every other project's sessions are silently hidden.
After fixing the visibility, the opposite problem also matters: a Portal instance that was started against a sub-tree (e.g.
--directory ~/projects/work) should not have to scroll through sessions from unrelated trees. Each instance should focus on the sub-tree it was started for.What this PR does
Two changes, one commit each.
1. List all sessions across all projects (commit 1)
OpenCode's first-party web UI uses
GET /experimental/session, which returns sessions across every project the OpenCode server knows about. Switch Portal's/api/opencode/[port]/sessionsroute to the same endpoint so Portal's session list mirrors what OpenCode itself shows.A small
getOpencodeBaseUrl(port)helper is added toopencode-client.tsso handlers that need a raw URL (rather than the SDK client) don't have to reimplement hostname resolution from~/.portal.json.The new code falls back to the SDK call (
client.session.list()) when/experimental/sessionis not OK (older OpenCode versions, future renames), so users on stale OpenCode binaries continue to see something rather than an empty list.2. Scope to the instance's
--directoryby default (commit 2)After (1) the session list is global. To keep multi-instance setups useful, results are filtered to sessions whose
directoryis at or under the Portal instance's--directory.Two query overrides are honored on
/api/opencode/[port]/sessions:?scope=all— return everything (the unfiltered behavior from commit 1).?directory=<path>— scope to an arbitrary path. Useful for drilling into a specific project from a Portal instance whose--directoryis broader.Path matching is path-component-aware:
/foo/barmatches/foo/barand/foo/bar/...but NOT/foo/bar-baz. Implemented withpath.resolveplus an explicit/boundary check.Instances whose
--directoryis/are treated as unscoped (no filter applied).A
getInstanceDirectory(port)helper is added next togetOpencodeBaseUrl.Compatibility
/experimental/*namespace is officially marked experimental upstream. In practice it is reasonably stable since OpenCode's own UI depends on it.--directory ~/projects/foono longer shows sessions from other projects unless?scope=allis passed. This is intentional and matches user expectation.Test plan
--directory ~/projectsand one at--directory ~/projects/foo, against the same OpenCode server.~/projectsshows all sessions under~/projects/...; instance at~/projects/fooshows only that sub-tree.?scope=allon either: full firehose.?directory=/some/path: results constrained to that path. Override works even when the path is outside the instance's--directory.~/projects/foo-baris NOT shown by an instance whose scope is~/projects/foo.All five verified manually with two Portal instances against a server holding 95 sessions across 16 projects: 89 (parent scope), 34 (sub-tree), 95 (
?scope=all), 13 (?directory=exact), 0 (sibling).Notes
apps/web/src/server/today; happy to add coverage if there is a preferred harness.bun.lockchanges frombun installwere deliberately not committed; this PR touches application code only.