fix(httpapi): expose unavailable v2 session mutations (#28624)

This commit is contained in:
Shoubhit Dash
2026-05-21 15:12:13 +05:30
committed by GitHub
parent 7d5e91b801
commit f5d20c580b
5 changed files with 96 additions and 10 deletions

View File

@@ -4,7 +4,7 @@ import { Prompt } from "@opencode-ai/core/session-prompt"
import { SessionV2 } from "@/v2/session"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"
import { InvalidCursorError, InvalidRequestError, SessionNotFoundError } from "../../errors"
import { InvalidCursorError, InvalidRequestError, ServiceUnavailableError, SessionNotFoundError } from "../../errors"
import { V2Authorization } from "../../middleware/authorization"
import { WorkspaceRoutingQuery, WorkspaceRoutingQueryFields } from "../../middleware/workspace-routing"
import { QueryBoolean } from "../query"
@@ -61,7 +61,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
delivery: SessionV2.Delivery.pipe(Schema.optional),
}),
success: SessionMessage.Message,
error: SessionNotFoundError,
error: [SessionNotFoundError, ServiceUnavailableError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.prompt",
@@ -75,7 +75,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
params: { sessionID: SessionID },
query: WorkspaceRoutingQuery,
success: HttpApiSchema.NoContent,
error: SessionNotFoundError,
error: [SessionNotFoundError, ServiceUnavailableError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.compact",
@@ -89,7 +89,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
params: { sessionID: SessionID },
query: WorkspaceRoutingQuery,
success: HttpApiSchema.NoContent,
error: SessionNotFoundError,
error: [SessionNotFoundError, ServiceUnavailableError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.wait",

View File

@@ -3,7 +3,7 @@ import { SessionV2 } from "@/v2/session"
import { DateTime, Effect, Option, Schema } from "effect"
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api"
import { InvalidCursorError, InvalidRequestError, SessionNotFoundError } from "../../errors"
import { InvalidCursorError, InvalidRequestError, ServiceUnavailableError, SessionNotFoundError } from "../../errors"
const DefaultSessionsLimit = 50
@@ -148,6 +148,14 @@ 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}`,
}),
),
),
)
}),
)
@@ -163,6 +171,14 @@ 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()
}),
@@ -179,6 +195,14 @@ 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()
}),

View File

@@ -65,6 +65,13 @@ export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Ses
sessionID: SessionID,
}) {}
export class OperationUnavailableError extends Schema.TaggedErrorClass<OperationUnavailableError>()(
"Session.OperationUnavailableError",
{
operation: Schema.Literals(["prompt", "compact", "wait"]),
},
) {}
export interface Interface {
readonly create: (input?: {
agent?: string
@@ -104,7 +111,7 @@ export interface Interface {
sessionID: SessionID
prompt: Prompt
delivery?: Delivery
}) => Effect.Effect<SessionMessage.User, NotFoundError>
}) => Effect.Effect<SessionMessage.User, NotFoundError | OperationUnavailableError>
readonly shell: (input: { id?: EventV2.ID; sessionID: SessionID; command: string }) => Effect.Effect<void, never>
readonly skill: (input: { id?: EventV2.ID; sessionID: SessionID; skill: string }) => Effect.Effect<void, never>
readonly subagent: (input: {
@@ -113,11 +120,11 @@ export interface Interface {
prompt: Prompt
agent: string
model?: ModelV2.Ref
}) => Effect.Effect<void, NotFoundError>
}) => Effect.Effect<void, NotFoundError | OperationUnavailableError>
readonly switchAgent: (input: { sessionID: SessionID; agent: string }) => Effect.Effect<void, never>
readonly switchModel: (input: { sessionID: SessionID; model: ModelV2.Ref }) => Effect.Effect<void, never>
readonly compact: (sessionID: SessionID) => Effect.Effect<void, NotFoundError>
readonly wait: (sessionID: SessionID) => Effect.Effect<void, NotFoundError>
readonly compact: (sessionID: SessionID) => Effect.Effect<void, NotFoundError | OperationUnavailableError>
readonly wait: (sessionID: SessionID) => Effect.Effect<void, NotFoundError | OperationUnavailableError>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Session") {}
@@ -292,7 +299,7 @@ export const layer = Layer.effect(
}),
prompt: Effect.fn("V2Session.prompt")(function* (input) {
yield* result.get(input.sessionID)
return {} as any
return yield* new OperationUnavailableError({ operation: "prompt" })
}),
shell: Effect.fn("V2Session.shell")(function* (_input) {}),
skill: Effect.fn("V2Session.skill")(function* (_input) {}),
@@ -333,9 +340,11 @@ export const layer = Layer.effect(
}),
compact: Effect.fn("V2Session.compact")(function* (sessionID) {
yield* result.get(sessionID)
return yield* new OperationUnavailableError({ operation: "compact" })
}),
wait: Effect.fn("V2Session.wait")(function* (sessionID) {
yield* result.get(sessionID)
return yield* new OperationUnavailableError({ operation: "wait" })
}),
})