refactor(core): simplify session pagination

This commit is contained in:
Dax Raad
2026-06-02 01:37:36 -04:00
parent 1b85d55a0e
commit 8d03f6c5be
7 changed files with 154 additions and 201 deletions

View File

@@ -25,7 +25,6 @@ import {
} from "../../src/server/routes/instance/httpapi/groups/session"
import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty"
import { MessagesQuery as V2MessagesQuery } from "../../src/server/routes/instance/httpapi/groups/v2/message"
import { SessionsQuery as V2SessionsQuery } from "../../src/server/routes/instance/httpapi/groups/v2/session"
import { QueryBoolean, QueryBooleanOpenApi } from "../../src/server/routes/instance/httpapi/groups/query"
import { resetDatabase } from "../fixture/db"
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
@@ -55,7 +54,6 @@ const openApiDriftRoutes = [
{ method: "get", path: ExperimentalPaths.session, query: ExperimentalSessionListQuery },
{ method: "get", path: ExperimentalPaths.tool, query: ToolListQuery },
{ method: "get", path: InstancePaths.vcsDiff, query: VcsDiffQuery },
{ method: "get", path: "/api/session", query: V2SessionsQuery },
{ method: "get", path: "/api/session/:sessionID/message", query: V2MessagesQuery },
] satisfies Array<{ method: Method; path: string; query: QuerySchema }>
@@ -72,8 +70,6 @@ const numericSdkQueryParams = [
name: "limit",
schema: { type: "integer", minimum: 0, maximum: Number.MAX_SAFE_INTEGER },
},
{ method: "get", path: "/api/session", name: "limit", schema: { type: "number" } },
{ method: "get", path: "/api/session", name: "start", schema: { type: "number" } },
{ method: "get", path: "/api/session/:sessionID/message", name: "limit", schema: { type: "number" } },
] satisfies Array<{ method: Method; path: string; name: string; schema: OpenApiSchema }>
@@ -81,7 +77,6 @@ const booleanSdkQueryParams = [
{ method: "get", path: ExperimentalPaths.session, name: "roots" },
{ method: "get", path: ExperimentalPaths.session, name: "archived" },
{ method: "get", path: SessionPaths.list, name: "roots" },
{ method: "get", path: "/api/session", name: "roots" },
] satisfies Array<{ method: Method; path: string; name: string }>
const queryParamPatterns = [
@@ -239,7 +234,7 @@ describe("httpapi query schema drift", () => {
],
},
path: "/fixture",
query: { fields: {} },
query: Schema.Struct({}),
}),
).toThrow("advertises query params not accepted by runtime schema")
}),

View File

@@ -444,17 +444,27 @@ describe("session HttpApi", () => {
yield* insertLegacyAssistantMessage(session.id, 1)
yield* insertLegacyAssistantMessage(session.id, 2)
const sessionPage = yield* request(`/api/session?limit=1`, { headers })
const sessionPage = yield* request(
`/api/session?${new URLSearchParams({
limit: "1",
order: "asc",
directory: test.directory,
search: "v2",
})}`,
{ headers },
)
const sessionCursor = (yield* json<{ cursor: { next?: string } }>(sessionPage)).cursor.next
expect(sessionCursor).toBeTruthy()
const cursorWithFilter = yield* request(`/api/session?cursor=${sessionCursor}&search=v2`, { headers })
expect(cursorWithFilter.status).toBe(400)
expect(yield* responseJson(cursorWithFilter)).toMatchObject({
_tag: "InvalidCursorError",
message: "Cursor cannot be combined with order or filters",
expect(JSON.parse(Buffer.from(sessionCursor!, "base64url").toString("utf8"))).toMatchObject({
order: "asc",
directory: test.directory,
search: "v2",
anchor: { id: session.id, direction: "next" },
})
const sessionNextPage = yield* request(`/api/session?cursor=${sessionCursor}`, { headers })
expect(sessionNextPage.status).toBe(200)
const invalidSessionCursor = yield* request(`/api/session?cursor=invalid`, { headers })
expect(invalidSessionCursor.status).toBe(400)
expect(yield* responseJson(invalidSessionCursor)).toMatchObject({
@@ -462,21 +472,11 @@ describe("session HttpApi", () => {
message: "Invalid cursor",
})
const mismatchedRouting = yield* request(`/api/session?cursor=${sessionCursor}&directory=/elsewhere`, {
headers,
})
expect(mismatchedRouting.status).toBe(400)
expect(yield* responseJson(mismatchedRouting)).toMatchObject({
_tag: "InvalidCursorError",
message: "Cursor does not match requested directory or workspace",
})
const invalidWorkspace = yield* request(`/api/session?workspace=bad`, { headers })
expect(invalidWorkspace.status).toBe(400)
expect(yield* responseJson(invalidWorkspace)).toMatchObject({
_tag: "InvalidRequestError",
message: "Invalid workspace query parameter",
field: "workspace",
kind: "Query",
})
const messagePage = yield* request(`/api/session/${session.id}/message?limit=1`, { headers })