diff --git a/packages/opencode/src/server/routes/session.ts b/packages/opencode/src/server/routes/session.ts index d14005572b..029b53518c 100644 --- a/packages/opencode/src/server/routes/session.ts +++ b/packages/opencode/src/server/routes/session.ts @@ -612,15 +612,14 @@ export const SessionRoutes = lazy(() => return c.json([]) } - const limit = Math.min(query.limit, 500) const page = await MessageV2.page({ sessionID, - limit, + limit: query.limit, before: query.before, }) if (page.cursor) { const url = new URL(c.req.url) - url.searchParams.set("limit", limit.toString()) + url.searchParams.set("limit", query.limit.toString()) url.searchParams.set("before", page.cursor) c.header("Access-Control-Expose-Headers", "Link, X-Next-Cursor") c.header("Link", `<${url.toString()}>; rel=\"next\"`) diff --git a/packages/opencode/test/server/session-messages.test.ts b/packages/opencode/test/server/session-messages.test.ts index 843ee4825a..4b075d14b6 100644 --- a/packages/opencode/test/server/session-messages.test.ts +++ b/packages/opencode/test/server/session-messages.test.ts @@ -10,9 +10,8 @@ import { Log } from "../../src/util/log" const root = path.join(__dirname, "../..") Log.init({ print: false }) -async function fill(sessionID: string, count: number) { +async function fill(sessionID: string, count: number, time = (i: number) => Date.now() + i) { const ids: string[] = [] - const base = Date.now() for (let i = 0; i < count; i++) { const id = Identifier.ascending("message") ids.push(id) @@ -20,7 +19,7 @@ async function fill(sessionID: string, count: number) { id, sessionID, role: "user", - time: { created: base + i }, + time: { created: time(i) }, agent: "test", model: { providerID: "test", modelID: "test" }, tools: {}, @@ -99,4 +98,22 @@ describe("session messages endpoint", () => { }, }) }) + + test("does not truncate large legacy limit requests", async () => { + await Instance.provide({ + directory: root, + fn: async () => { + const session = await Session.create({}) + await fill(session.id, 520) + const app = Server.Default() + + const res = await app.request(`/session/${session.id}/message?limit=510`) + expect(res.status).toBe(200) + const body = (await res.json()) as MessageV2.WithParts[] + expect(body).toHaveLength(510) + + await Session.remove(session.id) + }, + }) + }) }) diff --git a/packages/opencode/test/session/messages-pagination.test.ts b/packages/opencode/test/session/messages-pagination.test.ts index 41cd933972..a91dc309bc 100644 --- a/packages/opencode/test/session/messages-pagination.test.ts +++ b/packages/opencode/test/session/messages-pagination.test.ts @@ -9,9 +9,8 @@ import { Log } from "../../src/util/log" const root = path.join(__dirname, "../..") Log.init({ print: false }) -async function fill(sessionID: string, count: number) { +async function fill(sessionID: string, count: number, time = (i: number) => Date.now() + i) { const ids: string[] = [] - const base = Date.now() for (let i = 0; i < count; i++) { const id = Identifier.ascending("message") ids.push(id) @@ -19,7 +18,7 @@ async function fill(sessionID: string, count: number) { id, sessionID, role: "user", - time: { created: base + i }, + time: { created: time(i) }, agent: "test", model: { providerID: "test", modelID: "test" }, tools: {}, @@ -80,6 +79,24 @@ describe("session message pagination", () => { }) }) + test("accepts cursors generated from fractional timestamps", async () => { + await Instance.provide({ + directory: root, + fn: async () => { + const session = await Session.create({}) + const ids = await fill(session.id, 4, (i) => 1000.5 + i) + + const a = await MessageV2.page({ sessionID: session.id, limit: 2 }) + const b = await MessageV2.page({ sessionID: session.id, limit: 2, before: a.cursor! }) + + expect(a.items.map((item) => item.info.id)).toEqual(ids.slice(-2)) + expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2)) + + await Session.remove(session.id) + }, + }) + }) + test("scopes get by session id", async () => { await Instance.provide({ directory: root,