Skip to content

session/list doesn't conform to the ACP spec: empty params scopes to the launch project instead of all sessions, and cwd filter value is discarded #39579

Closed as not planned

Description

@LucaDiba

Summary

Per the ACP session/list spec, an empty params object should return "the first page of sessions," and the optional cwd parameter should filter to "sessions with a matching cwd." In practice, opencode's ACP session/list handler does neither: it silently scopes to the git-derived "project" of wherever the agent process was launched, and discards any cwd value you actually pass.

Repro

$ pwd
/path/to/some-git-repo   # a git repo opencode has never seen before
$ opencode acp
{"jsonrpc":"2.0","id":1,"method":"session/list","params":{}}
{"jsonrpc":"2.0","id":1,"result":{"sessions":[]}}
$ pwd
/home/someuser   # not a git repo
$ opencode acp
{"jsonrpc":"2.0","id":1,"method":"session/list","params":{}}
{"jsonrpc":"2.0","id":1,"result":{"sessions":[ ... dozens of sessions with unrelated cwds,
  e.g. various temp directories and other unrelated project paths ... ]}}

Same method, same empty params, wildly different results depending on the literal directory the agent process happens to be started from — and neither result is "all sessions."

Expected (per spec)

"All parameters are optional. A request with an empty params object returns the first page of sessions."
cwd: "Filter sessions by working directory. Must be an absolute path. Only sessions with a matching cwd are returned."

Actual / root cause

  1. Empty params is not unfiltered. Session.list always scopes to the current instance's project id, with no way to opt out:

    // packages/opencode/src/session/session.ts:548-555
    const list = Effect.fn("Session.list")(function* (input?: ListInput) {
      const ctx = yield* InstanceState.context
      return yield* listByProject(db, { projectID: ctx.project.id, ...input })
    })

    ctx.project.id comes from ProjectV2.resolve() (packages/core/src/project.ts:110-122), which walks up from the server's own launch directory looking for a git repo. No repo → everything collapses into the shared ID.global bucket (explaining why a non-git launch directory returns a grab-bag of unrelated sessions from every prior non-git invocation). A repo found → a brand-new project id scoped to that repo, which starts out empty even though other sessions exist elsewhere.

  2. cwd is treated as a boolean, not a filter value. Even when the ACP layer forwards params.cwd (packages/opencode/src/acp/service.ts:251), the underlying HTTP handler ignores the actual value:

    // packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:65
    const directory = ctx.query.directory ? yield* InstanceState.directory : undefined

    Any truthy directory/cwd is replaced with InstanceState.directory (the running instance's own directory) before the equality filter runs. So session/list { cwd: "/some/other/path" } never actually filters by /some/other/path — it filters by wherever the server itself started.

This looks like the REST endpoint was designed for opencode's own UI/CLI, where a server instance is pinned 1:1 to a workspace and "directory" is really just a "scope to my workspace" toggle — and the ACP bridge is reusing it as if it implemented the protocol's literal cwd filter, which it doesn't.

Suggested fix

  • Default (cwd omitted): return sessions across all known projects/directories, not just the launch instance's project.
  • When cwd is provided: filter by exact equality against SessionTable.directory/cwd, using the client-supplied value — not InstanceState.directory.

Activity

  1. github-actions commented on Jul 29, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If this is the same underlying issue, consider adding any new information (particularly the cwd filter value being discarded, which may not be fully covered in #33036) as a comment there instead.

  2. github-actions commented on Sep 28, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions