From daef31e98a8b8a98eb37901f5cee38feec78fb67 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 29 May 2026 12:15:52 +0200 Subject: [PATCH] refactor(cli): remove SessionPrompt promise facade --- .../src/kilo-sessions/remote-sender.ts | 9 +- packages/opencode/src/session/prompt.ts | 8 - .../test/kilocode/plan-followup.test.ts | 11 +- .../kilocode/session-prompt-queue.test.ts | 384 ++++++++++-------- .../kilocode/sessions/remote-sender.test.ts | 55 ++- script/check-opencode-promise-facades.ts | 1 - 6 files changed, 259 insertions(+), 209 deletions(-) diff --git a/packages/opencode/src/kilo-sessions/remote-sender.ts b/packages/opencode/src/kilo-sessions/remote-sender.ts index 38a70975b3f..9666789233b 100644 --- a/packages/opencode/src/kilo-sessions/remote-sender.ts +++ b/packages/opencode/src/kilo-sessions/remote-sender.ts @@ -76,6 +76,7 @@ export namespace RemoteSender { readonly list: () => Promise> readonly reply: (input: Permission.ReplyInput) => Promise } + prompt?: (input: SessionPrompt.PromptInput) => Promise } export type Sender = { @@ -97,6 +98,12 @@ export namespace RemoteSender { return AppRuntime.runPromise(Permission.Service.use((svc) => svc.reply(input))) }, } + const prompt = + options.prompt ?? + (async (input: SessionPrompt.PromptInput) => { + const { AppRuntime } = await import("@/effect/app-runtime") + return AppRuntime.runPromise(SessionPrompt.Service.use((svc) => svc.prompt(input))) + }) const sub = options.subscribe ?? @@ -294,7 +301,7 @@ export namespace RemoteSender { return } dispatchLongRunning(msg, directoryFor(input.data.sessionID), async () => { - await SessionPrompt.prompt(input.data as SessionPrompt.PromptInput) + await prompt(input.data as SessionPrompt.PromptInput) }) return } diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 17748fa93f7..97d74a2b4aa 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -13,7 +13,6 @@ import { SessionID, MessageID, PartID } from "./schema" import { MessageV2 } from "./message-v2" import * as Log from "@opencode-ai/core/util/log" import { SessionRevert } from "./revert" -import { makeRuntime } from "@/effect/run-service" // kilocode_change import * as Session from "./session" import { Agent } from "../agent/agent" import { Provider } from "@/provider/provider" @@ -2149,11 +2148,4 @@ const argsRegex = /(?:\[Image\s+\d+\]|"[^"]*"|'[^']*'|[^\s"']+)/gi const placeholderRegex = /\$(\d+)/g const quoteTrimRegex = /^["']|["']$/g -// kilocode_change start - legacy promise helpers for Kilo callsites -const { runPromise } = makeRuntime(Service, defaultLayer) -export const prompt = (input: PromptInput) => runPromise((svc) => svc.prompt(input)) -export const loop = (input: LoopInput) => runPromise((svc) => svc.loop(input)) -export const cancel = (sessionID: SessionID) => runPromise((svc) => svc.cancel(sessionID)) -// kilocode_change end - export * as SessionPrompt from "./prompt" diff --git a/packages/opencode/test/kilocode/plan-followup.test.ts b/packages/opencode/test/kilocode/plan-followup.test.ts index d65dd8fd9bb..ea6f55eacb0 100644 --- a/packages/opencode/test/kilocode/plan-followup.test.ts +++ b/packages/opencode/test/kilocode/plan-followup.test.ts @@ -1,4 +1,5 @@ import { describe, expect, spyOn, test } from "bun:test" +import { Effect } from "effect" import { Agent } from "../../src/agent/agent" import { Bus } from "../../src/bus" import { TuiEvent } from "../../src/cli/cmd/tui/event" @@ -1211,10 +1212,16 @@ describe("plan follow-up", () => { expect(followup).toBeDefined() if (!followup) return - expect(states.some((x) => x.sessionID === followup && x.type === "busy")).toBe(true) + const sid = followup + expect(states.some((x) => x.sessionID === sid && x.type === "busy")).toBe(true) const { SessionPrompt } = await import("../../src/session/prompt") - await SessionPrompt.cancel(followup) + await Effect.runPromise( + SessionPrompt.Service.use((svc) => svc.cancel(sid)).pipe( + Effect.provide(SessionPrompt.defaultLayer), + Effect.scoped, + ), + ) deferred.resolve("## Discoveries\n\nexample") await expect(pending).resolves.toBe("break") diff --git a/packages/opencode/test/kilocode/session-prompt-queue.test.ts b/packages/opencode/test/kilocode/session-prompt-queue.test.ts index c07663aff97..9c7b419851a 100644 --- a/packages/opencode/test/kilocode/session-prompt-queue.test.ts +++ b/packages/opencode/test/kilocode/session-prompt-queue.test.ts @@ -13,7 +13,7 @@ import { SessionCompaction } from "../../src/session/compaction" import { SessionPrompt } from "../../src/session/prompt" import { MessageID, SessionID } from "../../src/session/schema" import * as Log from "@opencode-ai/core/util/log" -import { tmpdir } from "../fixture/fixture" +import { provideInstance, tmpdir } from "../fixture/fixture" Log.init({ print: false }) @@ -60,10 +60,20 @@ function reply(input: { text: string; ready?: () => void; wait?: Promise>, text: string) { +function hasText(msg: MessageV2.WithParts, text: string) { return msg.parts.some((part) => part.type === "text" && part.text.includes(text)) } +function scoped(dir: string, fn: (prompt: SessionPrompt.Interface) => Promise) { + return Effect.runPromise( + SessionPrompt.Service.use((prompt) => Effect.promise(() => fn(prompt))).pipe( + Effect.provide(SessionPrompt.defaultLayer), + provideInstance(dir), + Effect.scoped, + ), + ) +} + // Find the last non-system message in an OpenAI-compatible request body. Kept // tolerant: we only care about role invariants, not the exact content shape, // because providers may serialize `content` as a string or as a parts array. @@ -414,77 +424,82 @@ describe("session prompt queue", () => { await WithInstance.provide({ directory: tmp.path, - fn: async () => { - const session = await Session.create({ title: "Queued prompt regression" }) - const first = SessionPrompt.prompt({ - sessionID: session.id, - agent: "code", - parts: [{ type: "text", text: "first prompt" }], - }) + fn: async () => + scoped(tmp.path, async (prompt) => { + const session = await Session.create({ title: "Queued prompt regression" }) + const first = Effect.runPromise( + prompt.prompt({ + sessionID: session.id, + agent: "code", + parts: [{ type: "text", text: "first prompt" }], + }), + ) - await ready.promise + await ready.promise - const second = SessionPrompt.prompt({ - sessionID: session.id, - agent: "code", - parts: [{ type: "text", text: "second prompt" }], - }) + const second = Effect.runPromise( + prompt.prompt({ + sessionID: session.id, + agent: "code", + parts: [{ type: "text", text: "second prompt" }], + }), + ) - const one = await first - await injected.promise - const two = await second + const one = await first + await injected.promise + const two = await second - expect(calls).toHaveLength(2) + expect(calls).toHaveLength(2) - // The in-flight stream must complete; no aborted error on msg1's reply. - expect(one.info.role).toBe("assistant") - if (one.info.role === "assistant") expect(one.info.error).toBeUndefined() - expect(hasText(one, "first reply")).toBe(true) - expect(hasText(two, "second reply")).toBe(true) + // The in-flight stream must complete; no aborted error on msg1's reply. + expect(one.info.role).toBe("assistant") + if (one.info.role === "assistant") expect(one.info.error).toBeUndefined() + expect(hasText(one, "first reply")).toBe(true) + expect(hasText(two, "second reply")).toBe(true) - const msgs = await Session.messages({ sessionID: session.id }) - const users = msgs.filter((msg) => msg.info.role === "user") - const assistants = msgs.filter((msg) => msg.info.role === "assistant") - const prompts = users.flatMap((msg) => - msg.parts.filter((part) => part.type === "text").map((part) => part.text), - ) - const text = assistants.flatMap((msg) => - msg.parts.filter((part) => part.type === "text").map((part) => part.text), - ) - expect(users).toHaveLength(2) - expect(assistants).toHaveLength(2) - expect(prompts).toContain("first prompt") - expect(prompts).toContain("second prompt") - expect(text).toContain("first reply") - expect(text).toContain("second reply") + const msgs = await Session.messages({ sessionID: session.id }) + const users = msgs.filter((msg) => msg.info.role === "user") + const assistants = msgs.filter((msg) => msg.info.role === "assistant") + const prompts = users.flatMap((msg) => + msg.parts.filter((part) => part.type === "text").map((part) => part.text), + ) + const text = assistants.flatMap((msg) => + msg.parts.filter((part) => part.type === "text").map((part) => part.text), + ) + expect(users).toHaveLength(2) + expect(assistants).toHaveLength(2) + expect(prompts).toContain("first prompt") + expect(prompts).toContain("second prompt") + expect(text).toContain("first reply") + expect(text).toContain("second reply") - const firstUser = users.find((msg) => hasText(msg, "first prompt")) - const secondUser = users.find((msg) => hasText(msg, "second prompt")) - const firstReply = assistants.find((msg) => hasText(msg, "first reply")) - const secondReply = assistants.find((msg) => hasText(msg, "second reply")) - if ( - firstUser?.info.role !== "user" || - secondUser?.info.role !== "user" || - firstReply?.info.role !== "assistant" || - secondReply?.info.role !== "assistant" - ) { - throw new Error("missing expected messages") - } - expect(firstReply.info.parentID).toBe(firstUser.info.id) - expect(secondReply.info.parentID).toBe(secondUser.info.id) + const firstUser = users.find((msg) => hasText(msg, "first prompt")) + const secondUser = users.find((msg) => hasText(msg, "second prompt")) + const firstReply = assistants.find((msg) => hasText(msg, "first reply")) + const secondReply = assistants.find((msg) => hasText(msg, "second reply")) + if ( + firstUser?.info.role !== "user" || + secondUser?.info.role !== "user" || + firstReply?.info.role !== "assistant" || + secondReply?.info.role !== "assistant" + ) { + throw new Error("missing expected messages") + } + expect(firstReply.info.parentID).toBe(firstUser.info.id) + expect(secondReply.info.parentID).toBe(secondUser.info.id) - // Regression for #9492: the second LLM request must end with the - // queued user prompt, not an assistant tail from the prior turn. - // Anthropic's API rejects requests whose final message is assistant - // (prefill), and scope() is supposed to partition the queued target - // turn to the end before the model request is built. - expect(bodies).toHaveLength(2) - const second2 = bodies[1] - expect(JSON.stringify(second2)).toContain("second prompt") - const tail = lastConversational(second2) - expect(tail?.role).toBe("user") - expect(JSON.stringify(tail?.content)).toContain("second prompt") - }, + // Regression for #9492: the second LLM request must end with the + // queued user prompt, not an assistant tail from the prior turn. + // Anthropic's API rejects requests whose final message is assistant + // (prefill), and scope() is supposed to partition the queued target + // turn to the end before the model request is built. + expect(bodies).toHaveLength(2) + const second2 = bodies[1] + expect(JSON.stringify(second2)).toContain("second prompt") + const tail = lastConversational(second2) + expect(tail?.role).toBe("user") + expect(JSON.stringify(tail?.content)).toContain("second prompt") + }), }) } finally { server.stop(true) @@ -531,52 +546,59 @@ describe("session prompt queue", () => { await WithInstance.provide({ directory: tmp.path, - fn: async () => { - const session = await Session.create({ title: "Queued cancel regression" }) - const first = SessionPrompt.prompt({ - sessionID: session.id, - agent: "code", - parts: [{ type: "text", text: "first prompt" }], - }) - await ready.promise + fn: async () => + scoped(tmp.path, async (prompt) => { + const session = await Session.create({ title: "Queued cancel regression" }) + const first = Effect.runPromise( + prompt.prompt({ + sessionID: session.id, + agent: "code", + parts: [{ type: "text", text: "first prompt" }], + }), + ) + await ready.promise - const second = SessionPrompt.prompt({ - sessionID: session.id, - agent: "code", - parts: [{ type: "text", text: "second prompt" }], - }) - const third = SessionPrompt.prompt({ - sessionID: session.id, - agent: "code", - parts: [{ type: "text", text: "third prompt" }], - }) + const second = Effect.runPromise( + prompt.prompt({ + sessionID: session.id, + agent: "code", + parts: [{ type: "text", text: "second prompt" }], + }), + ) + const third = Effect.runPromise( + prompt.prompt({ + sessionID: session.id, + agent: "code", + parts: [{ type: "text", text: "third prompt" }], + }), + ) - // Let msg2/msg3's enqueue capture the current version before cancel bumps it. - await Bun.sleep(20) - expect(calls).toHaveLength(1) + // Let msg2/msg3's enqueue capture the current version before cancel bumps it. + await Bun.sleep(20) + expect(calls).toHaveLength(1) - await SessionPrompt.cancel(session.id) - await Promise.all([first, second, third]) + await Effect.runPromise(prompt.cancel(session.id)) + await Promise.all([first, second, third]) - // The queued prompts must never reach the LLM once cancel flushes the queue. - expect(calls).toHaveLength(1) - const msgs = await Session.messages({ sessionID: session.id }) - const assistants = msgs.filter((msg) => msg.info.role === "assistant") - expect(assistants).toHaveLength(1) - expect(msgs.filter((msg) => msg.info.role === "user")).toHaveLength(3) + // The queued prompts must never reach the LLM once cancel flushes the queue. + expect(calls).toHaveLength(1) + const msgs = await Session.messages({ sessionID: session.id }) + const assistants = msgs.filter((msg) => msg.info.role === "assistant") + expect(assistants).toHaveLength(1) + expect(msgs.filter((msg) => msg.info.role === "user")).toHaveLength(3) - // Internal state should have no lingering tail/version/target entries after the last release. - const ids = await Effect.runPromise( - KiloSessionPromptQueue.enqueue( - session.id, - MessageID.make("message_probe"), - Effect.succeed(KiloSessionPromptQueue.scope(session.id, []).map((item) => item.info.id)), - Effect.succeed([]), - ), - ) - expect(ids).toEqual([]) - expect(KiloSessionPromptQueue.hasFollowup(session.id)).toBe(false) - }, + // Internal state should have no lingering tail/version/target entries after the last release. + const ids = await Effect.runPromise( + KiloSessionPromptQueue.enqueue( + session.id, + MessageID.make("message_probe"), + Effect.succeed(KiloSessionPromptQueue.scope(session.id, []).map((item) => item.info.id)), + Effect.succeed([]), + ), + ) + expect(ids).toEqual([]) + expect(KiloSessionPromptQueue.hasFollowup(session.id)).toBe(false) + }), }) } finally { server.stop(true) @@ -590,41 +612,44 @@ describe("session prompt queue", () => { await WithInstance.provide({ directory: tmp.path, - fn: async () => { - const session = await Session.create({ title: "Suggestion unblock regression" }) - const offShown = Bus.subscribe(Suggestion.Event.Shown, (event) => { - if (event.properties.sessionID === session.id) shown.resolve() - }) - const offDismissed = Bus.subscribe(Suggestion.Event.Dismissed, (event) => { - if (event.properties.sessionID === session.id) dismissed.resolve() - }) - - try { - const base = Suggestion.show({ - sessionID: session.id, - text: "Run review?", - actions: [{ label: "Review", prompt: "/local-review-uncommitted" }], - }).catch((err) => { - if (err instanceof Suggestion.DismissedError) return "dismissed" - throw err + fn: async () => + scoped(tmp.path, async (prompt) => { + const session = await Session.create({ title: "Suggestion unblock regression" }) + const offShown = Bus.subscribe(Suggestion.Event.Shown, (event) => { + if (event.properties.sessionID === session.id) shown.resolve() + }) + const offDismissed = Bus.subscribe(Suggestion.Event.Dismissed, (event) => { + if (event.properties.sessionID === session.id) dismissed.resolve() }) - await shown.promise - await SessionPrompt.prompt({ - sessionID: session.id, - agent: "code", - parts: [{ type: "text", text: "replacement prompt" }], - noReply: true, - }) - await dismissed.promise + try { + const base = Suggestion.show({ + sessionID: session.id, + text: "Run review?", + actions: [{ label: "Review", prompt: "/local-review-uncommitted" }], + }).catch((err) => { + if (err instanceof Suggestion.DismissedError) return "dismissed" + throw err + }) - expect(await base).toBe("dismissed") - expect(await Suggestion.list()).toEqual([]) - } finally { - offShown() - offDismissed() - } - }, + await shown.promise + await Effect.runPromise( + prompt.prompt({ + sessionID: session.id, + agent: "code", + parts: [{ type: "text", text: "replacement prompt" }], + noReply: true, + }), + ) + await dismissed.promise + + expect(await base).toBe("dismissed") + expect(await Suggestion.list()).toEqual([]) + } finally { + offShown() + offDismissed() + } + }), }) }) @@ -635,49 +660,52 @@ describe("session prompt queue", () => { await WithInstance.provide({ directory: tmp.path, - fn: async () => { - const session = await Session.create({ title: "Question unblock regression" }) - const offAsked = Bus.subscribe(Question.Event.Asked, (event) => { - if (event.properties.sessionID === session.id) asked.resolve() - }) - const offRejected = Bus.subscribe(Question.Event.Rejected, (event) => { - if (event.properties.sessionID === session.id) rejected.resolve() - }) - - try { - const pending = Question.ask({ - sessionID: session.id, - questions: [ - { - header: "Continue?", - question: "Should I continue?", - options: [ - { label: "Yes", description: "Go ahead" }, - { label: "No", description: "Stop" }, - ], - }, - ], - }).catch((err) => { - if (err instanceof Question.RejectedError) return "rejected" - throw err + fn: async () => + scoped(tmp.path, async (prompt) => { + const session = await Session.create({ title: "Question unblock regression" }) + const offAsked = Bus.subscribe(Question.Event.Asked, (event) => { + if (event.properties.sessionID === session.id) asked.resolve() + }) + const offRejected = Bus.subscribe(Question.Event.Rejected, (event) => { + if (event.properties.sessionID === session.id) rejected.resolve() }) - await asked.promise - await SessionPrompt.prompt({ - sessionID: session.id, - agent: "code", - parts: [{ type: "text", text: "replacement prompt" }], - noReply: true, - }) - await rejected.promise + try { + const pending = Question.ask({ + sessionID: session.id, + questions: [ + { + header: "Continue?", + question: "Should I continue?", + options: [ + { label: "Yes", description: "Go ahead" }, + { label: "No", description: "Stop" }, + ], + }, + ], + }).catch((err) => { + if (err instanceof Question.RejectedError) return "rejected" + throw err + }) - expect(await pending).toBe("rejected") - expect(await Question.list()).toEqual([]) - } finally { - offAsked() - offRejected() - } - }, + await asked.promise + await Effect.runPromise( + prompt.prompt({ + sessionID: session.id, + agent: "code", + parts: [{ type: "text", text: "replacement prompt" }], + noReply: true, + }), + ) + await rejected.promise + + expect(await pending).toBe("rejected") + expect(await Question.list()).toEqual([]) + } finally { + offAsked() + offRejected() + } + }), }) }) diff --git a/packages/opencode/test/kilocode/sessions/remote-sender.test.ts b/packages/opencode/test/kilocode/sessions/remote-sender.test.ts index 734d974dc5a..bc7210a8f74 100644 --- a/packages/opencode/test/kilocode/sessions/remote-sender.test.ts +++ b/packages/opencode/test/kilocode/sessions/remote-sender.test.ts @@ -4,10 +4,12 @@ import { Effect } from "effect" import { RemoteSender } from "../../../src/kilo-sessions/remote-sender" import type { RemoteWS } from "../../../src/kilo-sessions/remote-ws" import type { RemoteProtocol } from "../../../src/kilo-sessions/remote-protocol" -import { SessionPrompt } from "../../../src/session/prompt" +import type { SessionPrompt } from "../../../src/session/prompt" import { Question } from "../../../src/question" import { Permission } from "../../../src/permission" import { PermissionID } from "../../../src/permission/schema" +import { ModelID, ProviderID } from "../../../src/provider/schema" +import { SessionID } from "../../../src/session/schema" import { Suggestion } from "../../../src/kilocode/suggestion" // kilocode_change function fakeConn() { @@ -55,6 +57,12 @@ function permissions(items: Permission.Request[] = []) { } } +function prompts(calls: SessionPrompt.PromptInput[]) { + return async (input: SessionPrompt.PromptInput) => { + calls.push(input) + } +} + // kilocode_change start afterEach(() => { mock.restore() @@ -321,13 +329,14 @@ describe("RemoteSender", () => { // kilocode_change start test("send_message normalizes string model without prefix", async () => { const { conn, sent } = fakeConn() - const prompt = spyOn(SessionPrompt, "prompt").mockResolvedValue({} as never) + const calls: SessionPrompt.PromptInput[] = [] const sender = RemoteSender.create({ conn, directory: "/tmp/test", log: nolog, subscribe: fakeBus().subscribe, provide: async (input: { directory: string; init?: Effect.Effect; fn: () => R }) => input.fn(), + prompt: prompts(calls), }) sender.handle({ @@ -344,22 +353,25 @@ describe("RemoteSender", () => { await new Promise((r) => setTimeout(r, 0)) expect(sent[0]).toEqual({ type: "response", id: "req_model_string", result: {} }) - expect(prompt).toHaveBeenCalledWith({ - sessionID: "ses_x", - parts: [{ type: "text", text: "hello" }], - model: { providerID: "kilo", modelID: "anthropic/claude-sonnet-4-20250514" }, - }) + expect(calls).toEqual([ + { + sessionID: SessionID.make("ses_x"), + parts: [{ type: "text", text: "hello" }], + model: { providerID: ProviderID.make("kilo"), modelID: ModelID.make("anthropic/claude-sonnet-4-20250514") }, + }, + ]) }) test("send_message keeps kilocode-prefixed model unchanged before internal conversion", async () => { const { conn } = fakeConn() - const prompt = spyOn(SessionPrompt, "prompt").mockResolvedValue({} as never) + const calls: SessionPrompt.PromptInput[] = [] const sender = RemoteSender.create({ conn, directory: "/tmp/test", log: nolog, subscribe: fakeBus().subscribe, provide: async (input: { directory: string; init?: Effect.Effect; fn: () => R }) => input.fn(), + prompt: prompts(calls), }) sender.handle({ @@ -375,11 +387,13 @@ describe("RemoteSender", () => { await new Promise((r) => setTimeout(r, 0)) - expect(prompt).toHaveBeenCalledWith({ - sessionID: "ses_x", - parts: [{ type: "text", text: "hello" }], - model: { providerID: "kilo", modelID: "gpt-5-mini" }, - }) + expect(calls).toEqual([ + { + sessionID: SessionID.make("ses_x"), + parts: [{ type: "text", text: "hello" }], + model: { providerID: ProviderID.make("kilo"), modelID: ModelID.make("gpt-5-mini") }, + }, + ]) }) test("send_message rejects structured model on remote path", () => { @@ -411,13 +425,14 @@ describe("RemoteSender", () => { test("send_message does not special-case kilo-prefixed model", async () => { const { conn, sent } = fakeConn() - const prompt = spyOn(SessionPrompt, "prompt").mockResolvedValue({} as never) + const calls: SessionPrompt.PromptInput[] = [] const sender = RemoteSender.create({ conn, directory: "/tmp/test", log: nolog, subscribe: fakeBus().subscribe, provide: async (input: { directory: string; init?: Effect.Effect; fn: () => R }) => input.fn(), + prompt: prompts(calls), }) sender.handle({ @@ -434,11 +449,13 @@ describe("RemoteSender", () => { await new Promise((r) => setTimeout(r, 0)) expect(sent[0]).toEqual({ type: "response", id: "req_model_kilo", result: {} }) - expect(prompt).toHaveBeenCalledWith({ - sessionID: "ses_x", - parts: [{ type: "text", text: "hello" }], - model: { providerID: "kilo", modelID: "kilo/gpt-5-mini" }, - }) + expect(calls).toEqual([ + { + sessionID: SessionID.make("ses_x"), + parts: [{ type: "text", text: "hello" }], + model: { providerID: ProviderID.make("kilo"), modelID: ModelID.make("kilo/gpt-5-mini") }, + }, + ]) }) // kilocode_change end diff --git a/script/check-opencode-promise-facades.ts b/script/check-opencode-promise-facades.ts index 2e0b10778ad..49181f613f4 100644 --- a/script/check-opencode-promise-facades.ts +++ b/script/check-opencode-promise-facades.ts @@ -25,7 +25,6 @@ const allow: Record = { "installation/index.ts": "existing installation facade outside #10655", "question/index.ts": "transitional facade deferred for upstream reconciliation in #10655", "session/compaction.ts": "existing compaction facade outside #10655", - "session/prompt.ts": "transitional facade tracked by #10655", "session/session.ts": "transitional facade tracked by #10655", "sync/index.ts": "sync event runtime boundary", }