summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src/definition
diff options
context:
space:
mode:
authorShoubhit Dash <[email protected]>2026-04-15 04:45:25 +0530
committerDax Raad <[email protected]>2026-04-14 20:55:39 -0400
commitfba752a5016a93ad7ea54890cf444de02a89a0f8 (patch)
treef56a7f7705574fb418c7773e915dceafdcc51a1f /packages/server/src/definition
parent87b2a9d749ac39f47ea2d9d6806e32f224fe8ba9 (diff)
downloadopencode-fba752a5016a93ad7ea54890cf444de02a89a0f8.tar.gz
opencode-fba752a5016a93ad7ea54890cf444de02a89a0f8.zip
feat(server): extract question httpapi contract
Diffstat (limited to 'packages/server/src/definition')
-rw-r--r--packages/server/src/definition/api.ts16
-rw-r--r--packages/server/src/definition/index.ts1
-rw-r--r--packages/server/src/definition/question.ts94
3 files changed, 106 insertions, 5 deletions
diff --git a/packages/server/src/definition/api.ts b/packages/server/src/definition/api.ts
index 6eda4090e..e2f70196d 100644
--- a/packages/server/src/definition/api.ts
+++ b/packages/server/src/definition/api.ts
@@ -1,6 +1,12 @@
-import type { ServerApi } from "../types.js"
+import { HttpApi, OpenApi } from "effect/unstable/httpapi"
+import { questionApi } from "./question.js"
-export const api: ServerApi = {
- name: "opencode",
- groups: [],
-}
+export const api = HttpApi.make("opencode")
+ .addHttpApi(questionApi)
+ .annotateMerge(
+ OpenApi.annotations({
+ title: "opencode experimental HttpApi",
+ version: "0.0.1",
+ description: "Experimental HttpApi surface for selected instance routes.",
+ }),
+ )
diff --git a/packages/server/src/definition/index.ts b/packages/server/src/definition/index.ts
index 39cab2446..e9a52dc93 100644
--- a/packages/server/src/definition/index.ts
+++ b/packages/server/src/definition/index.ts
@@ -1 +1,2 @@
export { api } from "./api.js"
+export { questionApi, QuestionReply, QuestionRequest } from "./question.js"
diff --git a/packages/server/src/definition/question.ts b/packages/server/src/definition/question.ts
new file mode 100644
index 000000000..0d161e013
--- /dev/null
+++ b/packages/server/src/definition/question.ts
@@ -0,0 +1,94 @@
+import { Schema } from "effect"
+import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
+
+const root = "/experimental/httpapi/question"
+
+// Temporary transport-local schemas until canonical question schemas move into packages/core.
+export const QuestionID = Schema.String.annotate({ identifier: "QuestionID" })
+export const SessionID = Schema.String.annotate({ identifier: "SessionID" })
+export const MessageID = Schema.String.annotate({ identifier: "MessageID" })
+
+export class QuestionOption extends Schema.Class<QuestionOption>("QuestionOption")({
+ label: Schema.String.annotate({
+ description: "Display text (1-5 words, concise)",
+ }),
+ description: Schema.String.annotate({
+ description: "Explanation of choice",
+ }),
+}) {}
+
+const base = {
+ question: Schema.String.annotate({
+ description: "Complete question",
+ }),
+ header: Schema.String.annotate({
+ description: "Very short label (max 30 chars)",
+ }),
+ options: Schema.Array(QuestionOption).annotate({
+ description: "Available choices",
+ }),
+ multiple: Schema.optional(Schema.Boolean).annotate({
+ description: "Allow selecting multiple choices",
+ }),
+}
+
+export class QuestionInfo extends Schema.Class<QuestionInfo>("QuestionInfo")({
+ ...base,
+ custom: Schema.optional(Schema.Boolean).annotate({
+ description: "Allow typing a custom answer (default: true)",
+ }),
+}) {}
+
+export class QuestionTool extends Schema.Class<QuestionTool>("QuestionTool")({
+ messageID: MessageID,
+ callID: Schema.String,
+}) {}
+
+export class QuestionRequest extends Schema.Class<QuestionRequest>("QuestionRequest")({
+ id: QuestionID,
+ sessionID: SessionID,
+ questions: Schema.Array(QuestionInfo).annotate({
+ description: "Questions to ask",
+ }),
+ tool: Schema.optional(QuestionTool),
+}) {}
+
+export const QuestionAnswer = Schema.Array(Schema.String).annotate({ identifier: "QuestionAnswer" })
+
+export class QuestionReply extends Schema.Class<QuestionReply>("QuestionReply")({
+ answers: Schema.Array(QuestionAnswer).annotate({
+ description: "User answers in order of questions (each answer is an array of selected labels)",
+ }),
+}) {}
+
+export const questionApi = HttpApi.make("question").add(
+ HttpApiGroup.make("question")
+ .add(
+ HttpApiEndpoint.get("list", root, {
+ success: Schema.Array(QuestionRequest),
+ }).annotateMerge(
+ OpenApi.annotations({
+ identifier: "question.list",
+ summary: "List pending questions",
+ description: "Get all pending question requests across all sessions.",
+ }),
+ ),
+ HttpApiEndpoint.post("reply", `${root}/:requestID/reply`, {
+ params: { requestID: QuestionID },
+ payload: QuestionReply,
+ success: Schema.Boolean,
+ }).annotateMerge(
+ OpenApi.annotations({
+ identifier: "question.reply",
+ summary: "Reply to question request",
+ description: "Provide answers to a question request from the AI assistant.",
+ }),
+ ),
+ )
+ .annotateMerge(
+ OpenApi.annotations({
+ title: "question",
+ description: "Experimental HttpApi question routes.",
+ }),
+ ),
+)