fix(tui): restore queued compaction indicator (#36440)

This commit is contained in:
Kit Langton 2026-07-11 13:54:23 -04:00 committed by GitHub
commit 5945a8d429
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 181 additions and 1 deletions

View file

@ -63,6 +63,7 @@ type Data = {
status: Record<string, DataSessionStatus>
message: Record<string, SessionMessageInfo[]>
input: Record<string, string[]>
compaction: Record<string, string[]>
permission: Record<string, PermissionV2Request[]>
// Pending forms keyed by owner: a session ID or the temporary "global" elicitation sentinel.
form: Record<string, FormWithLocation[]>
@ -91,6 +92,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
status: {},
message: {},
input: {},
compaction: {},
permission: {},
form: {},
},
@ -112,6 +114,21 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
setStore("session", "status", sessionID, status)
}
function addCompaction(sessionID: string, inputID: string) {
if (store.session.compaction[sessionID]?.includes(inputID)) return
setStore("session", "compaction", sessionID, [...(store.session.compaction[sessionID] ?? []), inputID])
}
function removeCompaction(sessionID: string, inputID?: string) {
if (!inputID || !store.session.compaction[sessionID]?.includes(inputID)) return
setStore(
"session",
"compaction",
sessionID,
store.session.compaction[sessionID].filter((id) => id !== inputID),
)
}
const message = {
update(sessionID: string, fn: (messages: SessionMessageInfo[], index: Map<string, number>) => void) {
setStore(
@ -221,6 +238,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
delete draft.status[sessionID]
delete draft.message[sessionID]
delete draft.input[sessionID]
delete draft.compaction[sessionID]
delete draft.permission[sessionID]
delete draft.form[sessionID]
for (const [rootID, family] of Object.entries(draft.family)) {
@ -611,8 +629,10 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
setSessionStatus(event.data.sessionID, "running")
break
case "session.compaction.admitted":
addCompaction(event.data.sessionID, event.data.inputID)
break
case "session.compaction.started":
removeCompaction(event.data.sessionID, event.data.inputID)
message.update(event.data.sessionID, (draft, index) => {
message.append(draft, index, {
id: event.data.inputID ?? messageIDFromEvent(event.id),
@ -689,6 +709,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
})
break
case "session.compaction.failed":
removeCompaction(event.data.sessionID, event.data.inputID)
message.update(event.data.sessionID, (draft, index) => {
const position = draft.findLastIndex((item) => item.type === "compaction" && item.status === "running")
const current = draft[position]
@ -821,6 +842,24 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
return store.session.input[sessionID]?.includes(inputID) ?? false
},
},
compaction: {
list(sessionID: string) {
return store.session.compaction[sessionID] ?? []
},
async refresh(sessionID: string) {
if (!store.session.compaction[sessionID]) setStore("session", "compaction", sessionID, [])
setStore(
"session",
"compaction",
sessionID,
reconcile(
(await sdk.api.session.pending.list({ sessionID }))
.filter((item) => item.type === "compaction")
.map((item) => item.id),
),
)
},
},
async refresh(sessionID: string) {
setStore("session", "info", sessionID, await sdk.api.session.get({ sessionID }))
registerSession(sessionID)
@ -1098,9 +1137,14 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
sdk.event.listen(({ details }) => {
if (details.type === "server.connected") {
const messages = connected ? Object.keys(store.session.message) : []
const compactions = connected ? Object.keys(store.session.compaction) : []
connected = true
refreshActive()
void Promise.allSettled([bootstrap(), ...messages.map(result.session.message.refresh)])
void Promise.allSettled([
bootstrap(),
...messages.map(result.session.message.refresh),
...compactions.map(result.session.compaction.refresh),
])
return
}
handleEvent(details)

View file

@ -1059,6 +1059,9 @@ function SessionRowView(props: { row: SessionRow; message: (messageID: string) =
<Show when={props.message(row().messageID)}>{(message) => <SessionMessageView message={message()} />}</Show>
)}
</Match>
<Match when={props.row.type === "compaction-queued"}>
<CompactionQueued />
</Match>
<Match when={props.row.type === "part" ? props.row : undefined}>
{(row) => <SessionPartView partRef={row().ref} message={props.message} />}
</Match>
@ -1370,6 +1373,20 @@ function CompactionMessage(props: { message: Extract<SessionMessageInfo, { type:
)
}
function CompactionQueued() {
const { theme } = useTheme()
return (
<box flexDirection="row" alignItems="center">
<box border={["top"]} borderColor={theme.border} flexGrow={1} />
<box flexDirection="row" gap={1} paddingLeft={1} paddingRight={1}>
<text fg={theme.textMuted}></text>
<text fg={theme.textMuted}>Compaction queued</text>
</box>
<box border={["top"]} borderColor={theme.border} flexGrow={1} />
</box>
)
}
function statusLabel(status: "added" | "modified" | "deleted") {
if (status === "added") return "A"
if (status === "deleted") return "D"

View file

@ -10,6 +10,7 @@ export type PartRef = {
export type SessionRow =
| { type: "message"; messageID: string }
| { type: "compaction-queued"; inputID: string }
| { type: "part"; ref: PartRef }
| {
type: "group"
@ -31,6 +32,14 @@ export function createSessionRows(sessionID: Accessor<string>) {
const boundary = revertBoundary()
const rows = reduceSessionRows(boundary ? messages.filter((message) => message.id < boundary) : messages, inputs)
partitionPending(rows, pendingPermissions())
const position = rows.findIndex((row) => row.type === "message" && inputs.has(row.messageID))
rows.splice(
position === -1 ? rows.length : position,
0,
...data.session.compaction
.list(sessionID())
.map((inputID): SessionRow => ({ type: "compaction-queued", inputID })),
)
return rows
}
@ -54,6 +63,7 @@ export function createSessionRows(sessionID: Accessor<string>) {
createEffect(
on(sessionID, (id) => {
setRows(reconcile(reduce()))
void data.session.compaction.refresh(id).catch(() => undefined)
void data.session.message.refresh(id).then(
() => {
if (sessionID() !== id) return
@ -71,6 +81,13 @@ export function createSessionRows(sessionID: Accessor<string>) {
}),
)
createEffect(
on(
() => data.session.compaction.list(sessionID()).map((inputID) => inputID),
() => setRows(reconcile(reduce())),
),
)
createEffect(
on(
() =>