feat(server): add v2 session API endpoints (#31822)
This commit is contained in:
@@ -646,6 +646,7 @@ const scenarios: Scenario[] = [
|
|||||||
object(body)
|
object(body)
|
||||||
check(body.healthy === true, "v2 server should report healthy")
|
check(body.healthy === true, "v2 server should report healthy")
|
||||||
}),
|
}),
|
||||||
|
http.protected.get("/api/location", "v2.location.get").json(200, object),
|
||||||
http.protected.get("/api/agent", "v2.agent.list").json(200, locationData(array)),
|
http.protected.get("/api/agent", "v2.agent.list").json(200, locationData(array)),
|
||||||
http.protected.get("/api/model", "v2.model.list").json(200, locationData(array)),
|
http.protected.get("/api/model", "v2.model.list").json(200, locationData(array)),
|
||||||
http.protected.get("/api/provider", "v2.provider.list").json(200, locationData(array)),
|
http.protected.get("/api/provider", "v2.provider.list").json(200, locationData(array)),
|
||||||
@@ -698,6 +699,14 @@ const scenarios: Scenario[] = [
|
|||||||
headers: ctx.headers(),
|
headers: ctx.headers(),
|
||||||
}))
|
}))
|
||||||
.json(200, data(array)),
|
.json(200, data(array)),
|
||||||
|
http.protected
|
||||||
|
.get("/api/session/{sessionID}/question", "v2.session.question.list")
|
||||||
|
.seeded((ctx) => ctx.session({ title: "Question list owner" }))
|
||||||
|
.at((ctx) => ({
|
||||||
|
path: route("/api/session/{sessionID}/question", { sessionID: ctx.state.id }),
|
||||||
|
headers: ctx.headers(),
|
||||||
|
}))
|
||||||
|
.json(200, data(array)),
|
||||||
http.protected
|
http.protected
|
||||||
.post("/api/session/{sessionID}/permission/{requestID}/reply", "v2.session.permission.reply")
|
.post("/api/session/{sessionID}/permission/{requestID}/reply", "v2.session.permission.reply")
|
||||||
.seeded((ctx) => ctx.session({ title: "Permission owner" }))
|
.seeded((ctx) => ctx.session({ title: "Permission owner" }))
|
||||||
@@ -807,6 +816,22 @@ const scenarios: Scenario[] = [
|
|||||||
headers: ctx.headers(),
|
headers: ctx.headers(),
|
||||||
}))
|
}))
|
||||||
.status(400, undefined, "none"),
|
.status(400, undefined, "none"),
|
||||||
|
http.protected
|
||||||
|
.post("/api/session", "v2.session.create")
|
||||||
|
.at((ctx) => ({
|
||||||
|
path: "/api/session",
|
||||||
|
headers: { ...ctx.headers(), "content-type": "application/json" },
|
||||||
|
body: {},
|
||||||
|
}))
|
||||||
|
.json(200, data(object)),
|
||||||
|
http.protected
|
||||||
|
.get("/api/session/{sessionID}", "v2.session.get")
|
||||||
|
.seeded((ctx) => ctx.session({ title: "Session get" }))
|
||||||
|
.at((ctx) => ({
|
||||||
|
path: route("/api/session/{sessionID}", { sessionID: ctx.state.id }),
|
||||||
|
headers: ctx.headers(),
|
||||||
|
}))
|
||||||
|
.json(200, data(object)),
|
||||||
http.protected
|
http.protected
|
||||||
.get("/api/session/{sessionID}/context", "v2.session.context")
|
.get("/api/session/{sessionID}/context", "v2.session.context")
|
||||||
.at((ctx) => ({
|
.at((ctx) => ({
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ import type {
|
|||||||
GlobalUpgradeResponses,
|
GlobalUpgradeResponses,
|
||||||
InstanceDisposeErrors,
|
InstanceDisposeErrors,
|
||||||
InstanceDisposeResponses,
|
InstanceDisposeResponses,
|
||||||
|
LocationRef,
|
||||||
LspStatusErrors,
|
LspStatusErrors,
|
||||||
LspStatusResponses,
|
LspStatusResponses,
|
||||||
McpAddErrors,
|
McpAddErrors,
|
||||||
@@ -274,6 +275,8 @@ import type {
|
|||||||
V2FsReadResponses,
|
V2FsReadResponses,
|
||||||
V2HealthGetErrors,
|
V2HealthGetErrors,
|
||||||
V2HealthGetResponses,
|
V2HealthGetResponses,
|
||||||
|
V2LocationGetErrors,
|
||||||
|
V2LocationGetResponses,
|
||||||
V2ModelListErrors,
|
V2ModelListErrors,
|
||||||
V2ModelListResponses,
|
V2ModelListResponses,
|
||||||
V2PermissionRequestListErrors,
|
V2PermissionRequestListErrors,
|
||||||
@@ -294,6 +297,10 @@ import type {
|
|||||||
V2SessionCompactResponses,
|
V2SessionCompactResponses,
|
||||||
V2SessionContextErrors,
|
V2SessionContextErrors,
|
||||||
V2SessionContextResponses,
|
V2SessionContextResponses,
|
||||||
|
V2SessionCreateErrors,
|
||||||
|
V2SessionCreateResponses,
|
||||||
|
V2SessionGetErrors,
|
||||||
|
V2SessionGetResponses,
|
||||||
V2SessionListErrors,
|
V2SessionListErrors,
|
||||||
V2SessionListResponses,
|
V2SessionListResponses,
|
||||||
V2SessionMessagesErrors,
|
V2SessionMessagesErrors,
|
||||||
@@ -304,6 +311,8 @@ import type {
|
|||||||
V2SessionPermissionReplyResponses,
|
V2SessionPermissionReplyResponses,
|
||||||
V2SessionPromptErrors,
|
V2SessionPromptErrors,
|
||||||
V2SessionPromptResponses,
|
V2SessionPromptResponses,
|
||||||
|
V2SessionQuestionListErrors,
|
||||||
|
V2SessionQuestionListResponses,
|
||||||
V2SessionQuestionRejectErrors,
|
V2SessionQuestionRejectErrors,
|
||||||
V2SessionQuestionRejectResponses,
|
V2SessionQuestionRejectResponses,
|
||||||
V2SessionQuestionReplyErrors,
|
V2SessionQuestionReplyErrors,
|
||||||
@@ -5013,6 +5022,30 @@ export class Health extends HeyApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Location extends HeyApiClient {
|
||||||
|
/**
|
||||||
|
* Get location
|
||||||
|
*
|
||||||
|
* Resolve the requested location or the server default location.
|
||||||
|
*/
|
||||||
|
public get<ThrowOnError extends boolean = false>(
|
||||||
|
parameters?: {
|
||||||
|
location?: {
|
||||||
|
directory?: string
|
||||||
|
workspace?: string
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||||
|
return (options?.client ?? this.client).get<V2LocationGetResponses, V2LocationGetErrors, ThrowOnError>({
|
||||||
|
url: "/api/location",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Agent extends HeyApiClient {
|
export class Agent extends HeyApiClient {
|
||||||
/**
|
/**
|
||||||
* List agents
|
* List agents
|
||||||
@@ -5106,6 +5139,29 @@ export class Permission2 extends HeyApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Question2 extends HeyApiClient {
|
export class Question2 extends HeyApiClient {
|
||||||
|
/**
|
||||||
|
* List session question requests
|
||||||
|
*
|
||||||
|
* Retrieve pending question requests owned by a session.
|
||||||
|
*/
|
||||||
|
public list<ThrowOnError extends boolean = false>(
|
||||||
|
parameters: {
|
||||||
|
sessionID: string
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "sessionID" }] }])
|
||||||
|
return (options?.client ?? this.client).get<
|
||||||
|
V2SessionQuestionListResponses,
|
||||||
|
V2SessionQuestionListErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
url: "/api/session/{sessionID}/question",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reply to pending question request
|
* Reply to pending question request
|
||||||
*
|
*
|
||||||
@@ -5225,6 +5281,68 @@ export class Session3 extends HeyApiClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create session
|
||||||
|
*
|
||||||
|
* Create a session at the requested location.
|
||||||
|
*/
|
||||||
|
public create<ThrowOnError extends boolean = false>(
|
||||||
|
parameters?: {
|
||||||
|
id?: string
|
||||||
|
agent?: string
|
||||||
|
model?: {
|
||||||
|
id: string
|
||||||
|
providerID: string
|
||||||
|
variant?: string
|
||||||
|
}
|
||||||
|
location?: LocationRef
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams(
|
||||||
|
[parameters],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
args: [
|
||||||
|
{ in: "body", key: "id" },
|
||||||
|
{ in: "body", key: "agent" },
|
||||||
|
{ in: "body", key: "model" },
|
||||||
|
{ in: "body", key: "location" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return (options?.client ?? this.client).post<V2SessionCreateResponses, V2SessionCreateErrors, ThrowOnError>({
|
||||||
|
url: "/api/session",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
...params.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get session
|
||||||
|
*
|
||||||
|
* Retrieve a session by ID.
|
||||||
|
*/
|
||||||
|
public get<ThrowOnError extends boolean = false>(
|
||||||
|
parameters: {
|
||||||
|
sessionID: string
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "sessionID" }] }])
|
||||||
|
return (options?.client ?? this.client).get<V2SessionGetResponses, V2SessionGetErrors, ThrowOnError>({
|
||||||
|
url: "/api/session/{sessionID}",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send message
|
* Send message
|
||||||
*
|
*
|
||||||
@@ -5779,6 +5897,11 @@ export class V2 extends HeyApiClient {
|
|||||||
return (this._health ??= new Health({ client: this.client }))
|
return (this._health ??= new Health({ client: this.client }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _location?: Location
|
||||||
|
get location(): Location {
|
||||||
|
return (this._location ??= new Location({ client: this.client }))
|
||||||
|
}
|
||||||
|
|
||||||
private _agent?: Agent
|
private _agent?: Agent
|
||||||
get agent(): Agent {
|
get agent(): Agent {
|
||||||
return (this._agent ??= new Agent({ client: this.client }))
|
return (this._agent ??= new Agent({ client: this.client }))
|
||||||
|
|||||||
@@ -2737,18 +2737,18 @@ export type InvalidCursorError = {
|
|||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConflictError = {
|
|
||||||
_tag: "ConflictError"
|
|
||||||
message: string
|
|
||||||
resource?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SessionNotFoundError = {
|
export type SessionNotFoundError = {
|
||||||
_tag: "SessionNotFoundError"
|
_tag: "SessionNotFoundError"
|
||||||
sessionID: string
|
sessionID: string
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ConflictError = {
|
||||||
|
_tag: "ConflictError"
|
||||||
|
message: string
|
||||||
|
resource?: string
|
||||||
|
}
|
||||||
|
|
||||||
export type ServiceUnavailableError = {
|
export type ServiceUnavailableError = {
|
||||||
_tag: "ServiceUnavailableError"
|
_tag: "ServiceUnavailableError"
|
||||||
message: string
|
message: string
|
||||||
@@ -9453,6 +9453,40 @@ export type V2HealthGetResponses = {
|
|||||||
|
|
||||||
export type V2HealthGetResponse = V2HealthGetResponses[keyof V2HealthGetResponses]
|
export type V2HealthGetResponse = V2HealthGetResponses[keyof V2HealthGetResponses]
|
||||||
|
|
||||||
|
export type V2LocationGetData = {
|
||||||
|
body?: never
|
||||||
|
path?: never
|
||||||
|
query?: {
|
||||||
|
location?: {
|
||||||
|
directory?: string
|
||||||
|
workspace?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url: "/api/location"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2LocationGetErrors = {
|
||||||
|
/**
|
||||||
|
* InvalidRequestError
|
||||||
|
*/
|
||||||
|
400: InvalidRequestError
|
||||||
|
/**
|
||||||
|
* UnauthorizedError
|
||||||
|
*/
|
||||||
|
401: UnauthorizedError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2LocationGetError = V2LocationGetErrors[keyof V2LocationGetErrors]
|
||||||
|
|
||||||
|
export type V2LocationGetResponses = {
|
||||||
|
/**
|
||||||
|
* Location.Info
|
||||||
|
*/
|
||||||
|
200: LocationInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2LocationGetResponse = V2LocationGetResponses[keyof V2LocationGetResponses]
|
||||||
|
|
||||||
export type V2AgentListData = {
|
export type V2AgentListData = {
|
||||||
body?: never
|
body?: never
|
||||||
path?: never
|
path?: never
|
||||||
@@ -9531,6 +9565,83 @@ export type V2SessionListResponses = {
|
|||||||
|
|
||||||
export type V2SessionListResponse = V2SessionListResponses[keyof V2SessionListResponses]
|
export type V2SessionListResponse = V2SessionListResponses[keyof V2SessionListResponses]
|
||||||
|
|
||||||
|
export type V2SessionCreateData = {
|
||||||
|
body: {
|
||||||
|
id?: string
|
||||||
|
agent?: string
|
||||||
|
model?: {
|
||||||
|
id: string
|
||||||
|
providerID: string
|
||||||
|
variant?: string
|
||||||
|
}
|
||||||
|
location?: LocationRef
|
||||||
|
}
|
||||||
|
path?: never
|
||||||
|
query?: never
|
||||||
|
url: "/api/session"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionCreateErrors = {
|
||||||
|
/**
|
||||||
|
* InvalidRequestError
|
||||||
|
*/
|
||||||
|
400: InvalidRequestError
|
||||||
|
/**
|
||||||
|
* UnauthorizedError
|
||||||
|
*/
|
||||||
|
401: UnauthorizedError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionCreateError = V2SessionCreateErrors[keyof V2SessionCreateErrors]
|
||||||
|
|
||||||
|
export type V2SessionCreateResponses = {
|
||||||
|
/**
|
||||||
|
* Success
|
||||||
|
*/
|
||||||
|
200: {
|
||||||
|
data: SessionV2Info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionCreateResponse = V2SessionCreateResponses[keyof V2SessionCreateResponses]
|
||||||
|
|
||||||
|
export type V2SessionGetData = {
|
||||||
|
body?: never
|
||||||
|
path: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
query?: never
|
||||||
|
url: "/api/session/{sessionID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionGetErrors = {
|
||||||
|
/**
|
||||||
|
* InvalidRequestError
|
||||||
|
*/
|
||||||
|
400: InvalidRequestError
|
||||||
|
/**
|
||||||
|
* UnauthorizedError
|
||||||
|
*/
|
||||||
|
401: UnauthorizedError
|
||||||
|
/**
|
||||||
|
* SessionNotFoundError
|
||||||
|
*/
|
||||||
|
404: SessionNotFoundError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionGetError = V2SessionGetErrors[keyof V2SessionGetErrors]
|
||||||
|
|
||||||
|
export type V2SessionGetResponses = {
|
||||||
|
/**
|
||||||
|
* Success
|
||||||
|
*/
|
||||||
|
200: {
|
||||||
|
data: SessionV2Info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionGetResponse = V2SessionGetResponses[keyof V2SessionGetResponses]
|
||||||
|
|
||||||
export type V2SessionPromptData = {
|
export type V2SessionPromptData = {
|
||||||
body: {
|
body: {
|
||||||
id?: string
|
id?: string
|
||||||
@@ -10310,6 +10421,43 @@ export type V2QuestionRequestListResponses = {
|
|||||||
|
|
||||||
export type V2QuestionRequestListResponse = V2QuestionRequestListResponses[keyof V2QuestionRequestListResponses]
|
export type V2QuestionRequestListResponse = V2QuestionRequestListResponses[keyof V2QuestionRequestListResponses]
|
||||||
|
|
||||||
|
export type V2SessionQuestionListData = {
|
||||||
|
body?: never
|
||||||
|
path: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
query?: never
|
||||||
|
url: "/api/session/{sessionID}/question"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionQuestionListErrors = {
|
||||||
|
/**
|
||||||
|
* InvalidRequestError
|
||||||
|
*/
|
||||||
|
400: InvalidRequestError
|
||||||
|
/**
|
||||||
|
* UnauthorizedError
|
||||||
|
*/
|
||||||
|
401: UnauthorizedError
|
||||||
|
/**
|
||||||
|
* SessionNotFoundError
|
||||||
|
*/
|
||||||
|
404: SessionNotFoundError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionQuestionListError = V2SessionQuestionListErrors[keyof V2SessionQuestionListErrors]
|
||||||
|
|
||||||
|
export type V2SessionQuestionListResponses = {
|
||||||
|
/**
|
||||||
|
* Success
|
||||||
|
*/
|
||||||
|
200: {
|
||||||
|
data: Array<QuestionV2Request>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionQuestionListResponse = V2SessionQuestionListResponses[keyof V2SessionQuestionListResponses]
|
||||||
|
|
||||||
export type V2SessionQuestionReplyData = {
|
export type V2SessionQuestionReplyData = {
|
||||||
body: QuestionV2Reply
|
body: QuestionV2Reply
|
||||||
path: {
|
path: {
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ import { HealthGroup } from "./groups/health"
|
|||||||
import { QuestionGroup } from "./groups/question"
|
import { QuestionGroup } from "./groups/question"
|
||||||
import { ReferenceGroup } from "./groups/reference"
|
import { ReferenceGroup } from "./groups/reference"
|
||||||
import { Authorization } from "./middleware/authorization"
|
import { Authorization } from "./middleware/authorization"
|
||||||
|
import { LocationGroup } from "./groups/location"
|
||||||
|
|
||||||
export const Api = HttpApi.make("server")
|
export const Api = HttpApi.make("server")
|
||||||
.add(HealthGroup)
|
.add(HealthGroup)
|
||||||
|
.add(LocationGroup)
|
||||||
.add(AgentGroup)
|
.add(AgentGroup)
|
||||||
.add(SessionGroup)
|
.add(SessionGroup)
|
||||||
.add(MessageGroup)
|
.add(MessageGroup)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { AbsolutePath } from "@opencode-ai/core/schema"
|
|||||||
import { WorkspaceV2 } from "@opencode-ai/core/workspace"
|
import { WorkspaceV2 } from "@opencode-ai/core/workspace"
|
||||||
import { Effect, Layer, Schema } from "effect"
|
import { Effect, Layer, Schema } from "effect"
|
||||||
import { HttpServerRequest } from "effect/unstable/http"
|
import { HttpServerRequest } from "effect/unstable/http"
|
||||||
import { HttpApiMiddleware, OpenApi } from "effect/unstable/httpapi"
|
import { HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, OpenApi } from "effect/unstable/httpapi"
|
||||||
|
|
||||||
export const LocationQuery = Schema.Struct({
|
export const LocationQuery = Schema.Struct({
|
||||||
location: Schema.optional(
|
location: Schema.optional(
|
||||||
@@ -54,6 +54,23 @@ export class LocationMiddleware extends HttpApiMiddleware.Service<
|
|||||||
}
|
}
|
||||||
>()("@opencode/HttpApiLocation") {}
|
>()("@opencode/HttpApiLocation") {}
|
||||||
|
|
||||||
|
export const LocationGroup = HttpApiGroup.make("server.location")
|
||||||
|
.add(
|
||||||
|
HttpApiEndpoint.get("location.get", "/api/location", {
|
||||||
|
query: LocationQuery,
|
||||||
|
success: Location.Info,
|
||||||
|
})
|
||||||
|
.annotateMerge(locationQueryOpenApi)
|
||||||
|
.annotateMerge(
|
||||||
|
OpenApi.annotations({
|
||||||
|
identifier: "v2.location.get",
|
||||||
|
summary: "Get location",
|
||||||
|
description: "Resolve the requested location or the server default location.",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.middleware(LocationMiddleware)
|
||||||
|
|
||||||
function ref(request: HttpServerRequest.HttpServerRequest): Location.Ref {
|
function ref(request: HttpServerRequest.HttpServerRequest): Location.Ref {
|
||||||
const query = new URL(request.url, "http://localhost").searchParams
|
const query = new URL(request.url, "http://localhost").searchParams
|
||||||
const workspaceID = query.get("location[workspace]") || request.headers["x-opencode-workspace"]
|
const workspaceID = query.get("location[workspace]") || request.headers["x-opencode-workspace"]
|
||||||
|
|||||||
@@ -24,6 +24,21 @@ export const QuestionGroup = HttpApiGroup.make("server.question")
|
|||||||
)
|
)
|
||||||
.annotateMerge(OpenApi.annotations({ title: "questions", description: "Experimental question routes." }))
|
.annotateMerge(OpenApi.annotations({ title: "questions", description: "Experimental question routes." }))
|
||||||
.middleware(LocationMiddleware)
|
.middleware(LocationMiddleware)
|
||||||
|
.add(
|
||||||
|
HttpApiEndpoint.get("session.question.list", "/api/session/:sessionID/question", {
|
||||||
|
params: { sessionID: SessionV2.ID },
|
||||||
|
success: Schema.Struct({ data: Schema.Array(QuestionV2.Request) }),
|
||||||
|
error: SessionNotFoundError,
|
||||||
|
})
|
||||||
|
.middleware(SessionLocationMiddleware)
|
||||||
|
.annotateMerge(
|
||||||
|
OpenApi.annotations({
|
||||||
|
identifier: "v2.session.question.list",
|
||||||
|
summary: "List session question requests",
|
||||||
|
description: "Retrieve pending question requests owned by a session.",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
.add(
|
.add(
|
||||||
HttpApiEndpoint.post("session.question.reply", "/api/session/:sessionID/question/:requestID/reply", {
|
HttpApiEndpoint.post("session.question.reply", "/api/session/:sessionID/question/:requestID/reply", {
|
||||||
params: { sessionID: SessionV2.ID, requestID: QuestionV2.ID },
|
params: { sessionID: SessionV2.ID, requestID: QuestionV2.ID },
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import {
|
|||||||
UnknownError,
|
UnknownError,
|
||||||
} from "../errors"
|
} from "../errors"
|
||||||
import { SessionLocationMiddleware } from "../middleware/session-location"
|
import { SessionLocationMiddleware } from "../middleware/session-location"
|
||||||
|
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||||
|
import { ModelV2 } from "@opencode-ai/core/model"
|
||||||
|
import { Location } from "@opencode-ai/core/location"
|
||||||
|
|
||||||
const SessionsQueryFields = {
|
const SessionsQueryFields = {
|
||||||
workspace: WorkspaceV2.ID.pipe(Schema.optional),
|
workspace: WorkspaceV2.ID.pipe(Schema.optional),
|
||||||
@@ -105,6 +108,39 @@ export const SessionGroup = HttpApiGroup.make("server.session")
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.add(
|
||||||
|
HttpApiEndpoint.post("session.create", "/api/session", {
|
||||||
|
payload: Schema.Struct({
|
||||||
|
id: SessionV2.ID.pipe(Schema.optional),
|
||||||
|
agent: AgentV2.ID.pipe(Schema.optional),
|
||||||
|
model: ModelV2.Ref.pipe(Schema.optional),
|
||||||
|
location: Location.Ref.pipe(Schema.optional),
|
||||||
|
}),
|
||||||
|
success: Schema.Struct({ data: SessionV2.Info }),
|
||||||
|
})
|
||||||
|
.annotateMerge(
|
||||||
|
OpenApi.annotations({
|
||||||
|
identifier: "v2.session.create",
|
||||||
|
summary: "Create session",
|
||||||
|
description: "Create a session at the requested location.",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
HttpApiEndpoint.get("session.get", "/api/session/:sessionID", {
|
||||||
|
params: { sessionID: SessionV2.ID },
|
||||||
|
success: Schema.Struct({ data: SessionV2.Info }),
|
||||||
|
error: SessionNotFoundError,
|
||||||
|
})
|
||||||
|
.middleware(SessionLocationMiddleware)
|
||||||
|
.annotateMerge(
|
||||||
|
OpenApi.annotations({
|
||||||
|
identifier: "v2.session.get",
|
||||||
|
summary: "Get session",
|
||||||
|
description: "Retrieve a session by ID.",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
.add(
|
.add(
|
||||||
HttpApiEndpoint.post("session.prompt", "/api/session/:sessionID/prompt", {
|
HttpApiEndpoint.post("session.prompt", "/api/session/:sessionID/prompt", {
|
||||||
params: { sessionID: SessionV2.ID },
|
params: { sessionID: SessionV2.ID },
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ import { HealthHandler } from "./handlers/health"
|
|||||||
import { QuestionHandler } from "./handlers/question"
|
import { QuestionHandler } from "./handlers/question"
|
||||||
import { ReferenceHandler } from "./handlers/reference"
|
import { ReferenceHandler } from "./handlers/reference"
|
||||||
import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
|
import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
|
||||||
|
import { LocationHandler } from "./handlers/location"
|
||||||
|
|
||||||
export const handlers = Layer.mergeAll(
|
export const handlers = Layer.mergeAll(
|
||||||
HealthHandler,
|
HealthHandler,
|
||||||
|
LocationHandler,
|
||||||
AgentHandler,
|
AgentHandler,
|
||||||
SessionHandler,
|
SessionHandler,
|
||||||
MessageHandler,
|
MessageHandler,
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Location } from "@opencode-ai/core/location"
|
||||||
|
import { Effect } from "effect"
|
||||||
|
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||||
|
import { Api } from "../api"
|
||||||
|
|
||||||
|
export const LocationHandler = HttpApiBuilder.group(Api, "server.location", (handlers) =>
|
||||||
|
handlers.handle(
|
||||||
|
"location.get",
|
||||||
|
Effect.fn(function* () {
|
||||||
|
const location = yield* Location.Service
|
||||||
|
return new Location.Info({
|
||||||
|
directory: location.directory,
|
||||||
|
workspaceID: location.workspaceID,
|
||||||
|
project: location.project,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
@@ -29,6 +29,13 @@ export const QuestionHandler = HttpApiBuilder.group(Api, "server.question", (han
|
|||||||
return yield* response((yield* QuestionV2.Service).list())
|
return yield* response((yield* QuestionV2.Service).list())
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.handle(
|
||||||
|
"session.question.list",
|
||||||
|
Effect.fn(function* (ctx) {
|
||||||
|
const requests = yield* (yield* QuestionV2.Service).list()
|
||||||
|
return { data: requests.filter((request) => request.sessionID === ctx.params.sessionID) }
|
||||||
|
}),
|
||||||
|
)
|
||||||
.handle(
|
.handle(
|
||||||
"session.question.reply",
|
"session.question.reply",
|
||||||
Effect.fn(function* (ctx) {
|
Effect.fn(function* (ctx) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
SessionNotFoundError,
|
SessionNotFoundError,
|
||||||
UnknownError,
|
UnknownError,
|
||||||
} from "../errors"
|
} from "../errors"
|
||||||
|
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||||
|
|
||||||
const DefaultSessionsLimit = 50
|
const DefaultSessionsLimit = 50
|
||||||
|
|
||||||
@@ -61,6 +62,36 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.handle(
|
||||||
|
"session.create",
|
||||||
|
Effect.fn(function* (ctx) {
|
||||||
|
return {
|
||||||
|
data: yield* session.create({
|
||||||
|
id: ctx.payload.id,
|
||||||
|
agent: ctx.payload.agent,
|
||||||
|
model: ctx.payload.model,
|
||||||
|
location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) },
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.handle(
|
||||||
|
"session.get",
|
||||||
|
Effect.fn(function* (ctx) {
|
||||||
|
return {
|
||||||
|
data: yield* session.get(ctx.params.sessionID).pipe(
|
||||||
|
Effect.catchTag(
|
||||||
|
"Session.NotFoundError",
|
||||||
|
(error) =>
|
||||||
|
new SessionNotFoundError({
|
||||||
|
sessionID: error.sessionID,
|
||||||
|
message: `Session not found: ${error.sessionID}`,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
.handle(
|
.handle(
|
||||||
"session.prompt",
|
"session.prompt",
|
||||||
Effect.fn(function* (ctx) {
|
Effect.fn(function* (ctx) {
|
||||||
|
|||||||
Reference in New Issue
Block a user