fix(opencode): align v2 session endpoint errors

This commit is contained in:
Dax Raad
2026-05-25 12:21:06 -04:00
parent dcbe09bc9f
commit b819034c50
5 changed files with 20 additions and 67 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ const ListInputBase = {
workspaceID: WorkspaceV2.ID.pipe(Schema.optional),
search: Schema.String.pipe(Schema.optional),
limit: Schema.Int.pipe(Schema.optional),
order: Schema.Literal("asc").pipe(Schema.optional),
order: Schema.Literals(["asc", "desc"]).pipe(Schema.optional),
cursor: ListCursor.pipe(Schema.optional),
}
@@ -2,7 +2,7 @@ import { SessionID } from "@/session/schema"
import { SessionMessage } from "@opencode-ai/core/session/message"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "../../errors"
import { InvalidCursorError, SessionNotFoundError } from "../../errors"
import { V2Authorization } from "../../middleware/authorization"
import { WorkspaceRoutingQueryFields } from "../../middleware/workspace-routing"
@@ -36,7 +36,7 @@ export const MessageGroup = HttpApiGroup.make("v2.message")
next: Schema.String.pipe(Schema.optional),
}),
}).annotate({ identifier: "V2SessionMessagesResponse" }),
error: [InvalidCursorError, SessionNotFoundError, UnknownError],
error: [InvalidCursorError, SessionNotFoundError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.messages",
@@ -6,9 +6,7 @@ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/un
import {
InvalidCursorError,
InvalidRequestError,
ServiceUnavailableError,
SessionNotFoundError,
UnknownError,
} from "../../errors"
import { V2Authorization } from "../../middleware/authorization"
import { WorkspaceRoutingQuery, WorkspaceRoutingQueryFields } from "../../middleware/workspace-routing"
@@ -62,7 +60,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
params: { sessionID: SessionID },
query: WorkspaceRoutingQuery,
success: HttpApiSchema.NoContent,
error: [SessionNotFoundError, ServiceUnavailableError],
error: [SessionNotFoundError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.compact",
@@ -76,7 +74,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
params: { sessionID: SessionID },
query: WorkspaceRoutingQuery,
success: HttpApiSchema.NoContent,
error: [SessionNotFoundError, ServiceUnavailableError],
error: [SessionNotFoundError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.wait",
@@ -90,7 +88,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
params: { sessionID: SessionID },
query: WorkspaceRoutingQuery,
success: Schema.Array(SessionMessage.Message),
error: [SessionNotFoundError, UnknownError],
error: [SessionNotFoundError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.context",
@@ -4,7 +4,7 @@ import { Effect, Schema } from "effect"
import * as DateTime from "effect/DateTime"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api"
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "../../errors"
import { InvalidCursorError, SessionNotFoundError } from "../../errors"
const DefaultMessagesLimit = 50
@@ -58,20 +58,6 @@ export const messageHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.message
}),
),
),
Effect.catchTag("Session.MessageDecodeError", (error) => {
const ref = `err_${crypto.randomUUID().slice(0, 8)}`
return Effect.logError("failed to decode v2 session message").pipe(
Effect.annotateLogs({ ref, sessionID: error.sessionID, messageID: error.messageID }),
Effect.andThen(
Effect.fail(
new UnknownError({
message: "Unexpected server error. Check server logs for details.",
ref,
}),
),
),
)
}),
)
const first = messages[0]
const last = messages.at(-1)
@@ -1,15 +1,10 @@
import { WorkspaceV2 } from "@opencode-ai/core/workspace"
import { SessionV2 } from "@opencode-ai/core/session"
import { AbsolutePath } from "@opencode-ai/core/schema"
import { DateTime, Effect, Option, Schema } from "effect"
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api"
import {
InvalidCursorError,
InvalidRequestError,
ServiceUnavailableError,
SessionNotFoundError,
UnknownError,
} from "../../errors"
import { InvalidCursorError, InvalidRequestError, SessionNotFoundError } from "../../errors"
const DefaultSessionsLimit = 50
@@ -114,17 +109,21 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session
start: ctx.query.start,
search: ctx.query.search,
}
const sessions = yield* session.list({
const input = {
limit: ctx.query.limit ?? DefaultSessionsLimit,
order,
directory: filters.directory,
path: filters.path,
workspaceID: filters.workspaceID,
roots: filters.roots,
start: filters.start,
search: filters.search,
cursor: decoded ? { id: decoded.id, time: decoded.time, direction: decoded.direction } : undefined,
})
}
const sessions = yield* session.list(
filters.directory
? {
...input,
directory: AbsolutePath.make(filters.directory),
}
: input,
)
const first = sessions[0]
const last = sessions.at(-1)
return {
@@ -139,7 +138,7 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session
.handle(
"compact",
Effect.fn(function* (ctx) {
yield* session.compact(ctx.params.sessionID).pipe(
yield* session.compact({ sessionID: ctx.params.sessionID }).pipe(
Effect.catchTag("Session.NotFoundError", (error) =>
Effect.fail(
new SessionNotFoundError({
@@ -148,14 +147,6 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session
}),
),
),
Effect.catchTag("Session.OperationUnavailableError", (error) =>
Effect.fail(
new ServiceUnavailableError({
message: `V2 session ${error.operation} is not available yet`,
service: `v2.session.${error.operation}`,
}),
),
),
)
return HttpApiSchema.NoContent.make()
}),
@@ -172,14 +163,6 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session
}),
),
),
Effect.catchTag("Session.OperationUnavailableError", (error) =>
Effect.fail(
new ServiceUnavailableError({
message: `V2 session ${error.operation} is not available yet`,
service: `v2.session.${error.operation}`,
}),
),
),
)
return HttpApiSchema.NoContent.make()
}),
@@ -196,20 +179,6 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session
}),
),
),
Effect.catchTag("Session.MessageDecodeError", (error) => {
const ref = `err_${crypto.randomUUID().slice(0, 8)}`
return Effect.logError("failed to decode v2 session message").pipe(
Effect.annotateLogs({ ref, sessionID: error.sessionID, messageID: error.messageID }),
Effect.andThen(
Effect.fail(
new UnknownError({
message: "Unexpected server error. Check server logs for details.",
ref,
}),
),
),
)
}),
)
}),
)