diff --git a/packages/opencode/src/question/index.ts b/packages/opencode/src/question/index.ts index 68997a9d71..2a158007ef 100644 --- a/packages/opencode/src/question/index.ts +++ b/packages/opencode/src/question/index.ts @@ -218,7 +218,15 @@ export const layer = Layer.effect( wait(request.id).pipe(Effect.orDie), Effect.gen(function* () { const info = yield* InstanceState.get(state) + const existing = info.pending.get(request.id) + if (!existing) return + yield* Cache.set(info.completed, request.id, { status: "rejected" }) info.pending.delete(request.id) + yield* bus.publish(Event.Rejected, { + sessionID: existing.info.sessionID, + requestID: existing.info.id, + }) + yield* Deferred.succeed(existing.deferred, { status: "rejected" }) }), ) if (result.status === "answered") return result.answers diff --git a/packages/opencode/test/question/question.test.ts b/packages/opencode/test/question/question.test.ts index 102c9366e0..eac3d81670 100644 --- a/packages/opencode/test/question/question.test.ts +++ b/packages/opencode/test/question/question.test.ts @@ -229,6 +229,32 @@ it.instance( { git: true }, ) +it.instance( + "wait - returns rejected when pending ask is interrupted", + () => + Effect.gen(function* () { + const fiber = yield* askEffect({ + sessionID: SessionID.make("ses_test"), + questions: [ + { + question: "What would you like to do?", + header: "Action", + options: [{ label: "Option 1", description: "First option" }], + }, + ], + }).pipe(Effect.forkScoped) + + const pending = yield* waitForPending(1) + const waiter = yield* waitEffect(pending[0].id).pipe(Effect.forkScoped) + + yield* Fiber.interrupt(fiber) + + expect(yield* Fiber.join(waiter)).toEqual({ status: "rejected" }) + expect(yield* waitEffect(pending[0].id)).toEqual({ status: "rejected" }) + }), + { git: true }, +) + it.instance( "reply - fails for unknown requestID", () => diff --git a/packages/opencode/test/server/httpapi-exercise/index.ts b/packages/opencode/test/server/httpapi-exercise/index.ts index f2b132cb73..c3e188d9d9 100644 --- a/packages/opencode/test/server/httpapi-exercise/index.ts +++ b/packages/opencode/test/server/httpapi-exercise/index.ts @@ -235,6 +235,34 @@ const scenarios: Scenario[] = [ })) .json(404, object, "status"), http.protected.get("/question", "question.list").json(200, array), + http.protected + .post("/question", "question.ask") + .mutating() + .at((ctx) => ({ + path: "/question", + headers: ctx.headers(), + body: { + sessionID: "ses_httpapi_question", + questions: [ + { + question: "Proceed?", + header: "Proceed", + options: [{ label: "Yes", description: "Continue" }], + }, + ], + }, + })) + .json(200, (body) => { + object(body) + check(typeof body.id === "string" && body.id.startsWith("que_"), "question ask should return request id") + }), + http.protected + .get("/question/{requestID}/wait", "question.wait.missing") + .at((ctx) => ({ + path: route("/question/{requestID}/wait", { requestID: "que_httpapi_wait" }), + headers: ctx.headers(), + })) + .json(404, object, "status"), http.protected .post("/question/{requestID}/reply", "question.reply.invalid") .at((ctx) => ({