refactor(session): simplify pending withdrawal

This commit is contained in:
Kit Langton 2026-07-30 16:13:05 -04:00
commit d7ffc7fec1
12 changed files with 143 additions and 130 deletions

View file

@ -188,6 +188,12 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
index.set(item.id, messages.length)
messages.push(item)
},
reindex(messages: SessionMessageInfo[], index: Map<string, number>, start: number) {
for (let position = start; position < messages.length; position++) {
const item = messages[position]
if (item) index.set(item.id, position)
}
},
activeAssistant(messages: SessionMessageInfo[]) {
const item = messages.findLast((item) => item.type === "assistant" && !item.time.completed)
return item?.type === "assistant" ? item : undefined
@ -395,8 +401,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
existing.time.created = event.created
draft.splice(position, 1)
draft.push(existing)
index.clear()
draft.forEach((message, indexValue) => index.set(message.id, indexValue))
message.reindex(draft, index, position)
})
setStore(
"session",
@ -445,15 +450,16 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
const position = index.get(event.data.inputID)
if (position === undefined) return
draft.splice(position, 1)
index.clear()
draft.forEach((message, indexValue) => index.set(message.id, indexValue))
index.delete(event.data.inputID)
message.reindex(draft, index, position)
})
setStore(
"session",
"input",
event.data.sessionID,
(store.session.input[event.data.sessionID] ?? []).filter((id) => id !== event.data.inputID),
)
if (store.session.input[event.data.sessionID]?.includes(event.data.inputID))
setStore(
"session",
"input",
event.data.sessionID,
(store.session.input[event.data.sessionID] ?? []).filter((id) => id !== event.data.inputID),
)
break
}
case "session.instructions.updated":

View file

@ -46,7 +46,6 @@ export function DialogMessage(props: {
void undoMessage(client.api, {
sessionID: props.sessionID,
messageID: props.messageID,
pending: data.session.input.has(props.sessionID, props.messageID),
}).catch((error) => toast.show({ message: errorMessage(error), variant: "error", duration: 5000 }))
dialog.clear()
},

View file

@ -594,7 +594,6 @@ export function Session() {
void undoMessage(client.api, {
sessionID: route.sessionID,
messageID: message.id,
pending: data.session.input.has(route.sessionID, message.id),
}).catch((error) => toast.show({ message: errorMessage(error), variant: "error", duration: 5000 }))
prompt()?.set({
...projectedPromptInput(message),

View file

@ -2,10 +2,9 @@ import type { OpenCodeClient } from "@opencode-ai/client"
export function undoMessage(
client: OpenCodeClient,
input: { readonly sessionID: string; readonly messageID: string; readonly pending: boolean },
input: { readonly sessionID: string; readonly messageID: string },
) {
const revert = () => client.session.revert.stage(input).then(() => undefined)
if (!input.pending) return revert()
return client.session.pending
.withdraw({ sessionID: input.sessionID, inputID: input.messageID })
.then((withdrawn) => (withdrawn ? undefined : revert()))

View file

@ -3,10 +3,9 @@ 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 }) => {
{ withdrawn: true, expected: ["withdraw"] },
{ withdrawn: false, expected: ["withdraw", "revert"] },
])("routes undo for withdrawn=$withdrawn", async ({ withdrawn, expected }) => {
const calls: string[] = []
const client = OpenCode.make({
baseUrl: "http://localhost:3000",
@ -21,7 +20,7 @@ test.each([
),
})
await undoMessage(client, { sessionID: "ses_test", messageID: "msg_user", pending })
await undoMessage(client, { sessionID: "ses_test", messageID: "msg_user" })
expect(calls).toEqual([...expected])
})