Просмотр исходного кода

fix(httpapi): record deferred v2 prompts

Shoubhit Dash 2 месяцев назад
Родитель
Сommit
82071ff90c

+ 1 - 1
packages/opencode/src/server/routes/instance/httpapi/groups/v2/session.ts

@@ -72,7 +72,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
       OpenApi.annotations({
         identifier: "v2.session.prompt",
         summary: "Send v2 message",
-        description: "Create a v2 session message and queue it for the agent loop.",
+        description: "Create a deferred v2 user message. Immediate agent delivery is not available yet.",
       }),
     ),
   )

+ 44 - 1
packages/opencode/src/v2/session.ts

@@ -314,7 +314,50 @@ export const layer = Layer.effect(
       }),
       prompt: Effect.fn("V2Session.prompt")(function* (input) {
         yield* result.get(input.sessionID)
-        return yield* new OperationUnavailableError({ operation: "prompt" })
+        if (input.delivery !== "deferred") return yield* new OperationUnavailableError({ operation: "prompt" })
+        const event = yield* events.publish(
+          SessionEvent.Prompted,
+          {
+            sessionID: input.sessionID,
+            timestamp: DateTime.makeUnsafe(Date.now()),
+            prompt: input.prompt,
+          },
+          { id: input.id },
+        )
+        const message = new SessionMessage.User({
+          id: event.id,
+          type: "user",
+          metadata: event.metadata,
+          text: event.data.prompt.text,
+          files: event.data.prompt.files,
+          agents: event.data.prompt.agents,
+          references: event.data.prompt.references,
+          time: { created: event.data.timestamp },
+        })
+        // The bridge currently publishes the event but does not guarantee this request path has a projector attached.
+        Database.use((db) => {
+          const existing = db.select().from(SessionMessageTable).where(eq(SessionMessageTable.id, message.id)).get()
+          if (existing) return
+          db.insert(SessionMessageTable)
+            .values([
+              {
+                id: message.id,
+                session_id: input.sessionID,
+                type: message.type,
+                time_created: DateTime.toEpochMillis(message.time.created),
+                data: {
+                  metadata: message.metadata,
+                  text: message.text,
+                  files: message.files,
+                  agents: message.agents,
+                  references: message.references,
+                  time: { created: DateTime.toEpochMillis(message.time.created) },
+                } as NonNullable<(typeof SessionMessageTable.$inferInsert)["data"]>,
+              },
+            ])
+            .run()
+        })
+        return message
       }),
       shell: Effect.fn("V2Session.shell")(function* (_input) {}),
       skill: Effect.fn("V2Session.skill")(function* (_input) {}),

+ 31 - 0
packages/opencode/test/server/httpapi-session.test.ts

@@ -460,6 +460,37 @@ describe("session HttpApi", () => {
     { git: true, config: { formatter: false, lsp: false } },
   )
 
+  it.instance(
+    "records deferred v2 prompts as projected user messages",
+    () =>
+      Effect.gen(function* () {
+        const test = yield* TestInstance
+        const headers = { "x-opencode-directory": test.directory }
+        const session = yield* createSession({ title: "v2 deferred prompt" })
+
+        const prompt = yield* request(`/api/session/${session.id}/prompt`, {
+          method: "POST",
+          headers: { ...headers, "content-type": "application/json" },
+          body: JSON.stringify({ prompt: { text: "hello deferred" }, delivery: "deferred" }),
+        })
+        expect(prompt.status).toBe(200)
+        const promptBody = yield* responseJson(prompt)
+        expect(promptBody).toMatchObject({
+          type: "user",
+          text: "hello deferred",
+        })
+
+        const messages = yield* requestJson<{ items: SessionMessage.Message[] }>(`/api/session/${session.id}/message`, {
+          headers,
+        })
+        expect(messages.items).toMatchObject([{ type: "user", text: "hello deferred" }])
+
+        const context = yield* requestJson<SessionMessage.Message[]>(`/api/session/${session.id}/context`, { headers })
+        expect(context).toMatchObject([{ type: "user", text: "hello deferred" }])
+      }),
+    { git: true, config: { formatter: false, lsp: false } },
+  )
+
   it.instance(
     "returns v2 public unavailable errors for unfinished session mutations",
     () =>