diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts index bc26a9e597..e1e93b7d7c 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts @@ -10,7 +10,6 @@ import { SessionSummary } from "@/session/summary" import { Todo } from "@/session/todo" import { MessageID, PartID, SessionID } from "@/session/schema" import { Snapshot } from "@/snapshot" -import { NonNegativeInt } from "@/util/schema" import { Schema, SchemaGetter, Struct } from "effect" import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi" import { Authorization } from "../middleware/authorization" @@ -45,7 +44,7 @@ export const UpdatePayload = Schema.Struct({ permission: Schema.optional(Permission.Ruleset), time: Schema.optional( Schema.Struct({ - archived: Schema.optional(NonNegativeInt), + archived: Schema.optional(Schema.Number), }), ), }) diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts index 1be5dfffd4..a3db293ef3 100644 --- a/packages/opencode/src/session/session.ts +++ b/packages/opencode/src/session/session.ts @@ -146,7 +146,7 @@ const Time = Schema.Struct({ created: NonNegativeInt, updated: NonNegativeInt, compacting: optionalOmitUndefined(NonNegativeInt), - archived: optionalOmitUndefined(NonNegativeInt), + archived: optionalOmitUndefined(Schema.Number), }) const Revert = Schema.Struct({ @@ -215,7 +215,7 @@ export const SetTitleInput = Schema.Struct({ sessionID: SessionID, title: Schema ) export const SetArchivedInput = Schema.Struct({ sessionID: SessionID, - time: Schema.optional(NonNegativeInt), + time: Schema.optional(Schema.Number), }).pipe(withStatics((s) => ({ zod: zod(s) }))) export const SetPermissionInput = Schema.Struct({ sessionID: SessionID, @@ -244,7 +244,7 @@ const UpdatedTime = Schema.Struct({ created: Schema.optional(Schema.NullOr(NonNegativeInt)), updated: Schema.optional(Schema.NullOr(NonNegativeInt)), compacting: Schema.optional(Schema.NullOr(NonNegativeInt)), - archived: Schema.optional(Schema.NullOr(NonNegativeInt)), + archived: Schema.optional(Schema.NullOr(Schema.Number)), }) const UpdatedInfo = Schema.Struct({ diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index 593f9765c7..8e1987cedf 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -18,9 +18,9 @@ void Log.init({ print: false }) const original = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI -function app() { - Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true - return Server.Default().app +function app(experimental = true) { + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = experimental + return experimental ? Server.Default().app : Server.Legacy().app } function runSession(fx: Effect.Effect) { @@ -76,6 +76,10 @@ function request(path: string, init?: RequestInit) { return Effect.promise(async () => app().request(path, init)) } +function requestWithBackend(experimental: boolean, path: string, init?: RequestInit) { + return Effect.promise(async () => app(experimental).request(path, init)) +} + function json(response: Response) { return Effect.promise(async () => { if (response.status !== 200) throw new Error(await response.text()) @@ -217,6 +221,34 @@ describe("session HttpApi", () => { ), ) + it.live( + "matches legacy archived timestamp validation", + withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + Effect.gen(function* () { + const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" } + const legacy = yield* createSession(tmp.path, { title: "legacy" }) + const effect = yield* createSession(tmp.path, { title: "effect" }) + const body = JSON.stringify({ time: { archived: -1 } }) + + const legacyResponse = yield* requestWithBackend(false, pathFor(SessionPaths.update, { sessionID: legacy.id }), { + method: "PATCH", + headers, + body, + }) + expect(legacyResponse.status).toBe(200) + expect((yield* json(legacyResponse)).time.archived).toBe(-1) + + const effectResponse = yield* requestWithBackend(true, pathFor(SessionPaths.update, { sessionID: effect.id }), { + method: "PATCH", + headers, + body, + }) + expect(effectResponse.status).toBe(legacyResponse.status) + expect((yield* json(effectResponse)).time.archived).toBe(-1) + }), + ), + ) + it.live( "serves message mutation routes through Hono bridge", withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) =>