fix(tui): undo pending session input

This commit is contained in:
Kit Langton 2026-07-30 16:01:44 -04:00
commit 330ab9acae
24 changed files with 493 additions and 71 deletions

View file

@ -853,6 +853,65 @@ test("completes exploration when a queued prompt is promoted", async () => {
}
})
test("removes optimistic input when it is withdrawn", async () => {
const events = createEventStream()
const sessionID = "session-withdrawal"
const calls = createFetch((url) => {
if (url.pathname === `/api/session/${sessionID}/message`) return json({ data: [], cursor: {} })
}, events)
let data!: ReturnType<typeof useData>
let client!: ReturnType<typeof useClient>
function Probe() {
data = useData()
client = useClient()
return <box />
}
const app = await testRender(() => (
<TestTuiContexts>
<ClientProvider api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
</DataProvider>
</ProjectProvider>
</ClientProvider>
</TestTuiContexts>
))
try {
await wait(() => client.connection.status() === "connected")
emitEvent(events, {
id: "evt_prompt_admitted",
created: 1,
type: "session.input.admitted",
durable: durable(sessionID, 1),
data: {
sessionID,
inputID: "message-user",
input: { type: "user", data: { text: "Never mind" }, delivery: "steer" },
},
})
await wait(() => data.session.message.get(sessionID, "message-user") !== undefined)
expect(data.session.input.has(sessionID, "message-user")).toBe(true)
emitEvent(events, {
id: "evt_prompt_withdrawn",
created: 2,
type: "session.input.withdrawn",
durable: durable(sessionID, 2),
data: { sessionID, inputID: "message-user" },
})
await wait(() => data.session.message.get(sessionID, "message-user") === undefined)
expect(data.session.input.has(sessionID, "message-user")).toBe(false)
expect(data.session.pending.list(sessionID)).toEqual([])
} finally {
app.renderer.destroy()
}
})
test("classifies live tool rows independently of their call ID", async () => {
const events = createEventStream()
const sessionID = "session-tool-call-id"

View file

@ -0,0 +1,27 @@
import { expect, test } from "bun:test"
import { OpenCode } from "@opencode-ai/client"
import { undoMessage } from "../../../src/routes/session/undo"
test.each([
{ pending: true, withdrawn: true, expected: ["withdraw"] },
{ pending: true, withdrawn: false, expected: ["withdraw", "revert"] },
{ pending: false, withdrawn: false, expected: ["revert"] },
])("routes undo for pending=$pending withdrawn=$withdrawn", async ({ pending, withdrawn, expected }) => {
const calls: string[] = []
const client = OpenCode.make({
baseUrl: "http://localhost:3000",
fetch: Object.assign(
async (input: URL | RequestInfo, init?: BunFetchRequestInit | RequestInit) => {
const request = input instanceof Request ? input : new Request(input, init)
const operation = request.url.endsWith("/withdraw") ? "withdraw" : "revert"
calls.push(operation)
return Response.json({ data: operation === "withdraw" ? withdrawn : { messageID: "msg_user" } })
},
{ preconnect: fetch.preconnect },
),
})
await undoMessage(client, { sessionID: "ses_test", messageID: "msg_user", pending })
expect(calls).toEqual([...expected])
})