Compare commits

...

1 Commits

Author SHA1 Message Date
James Long 8ff12590a3 fix(tui): preserve first prompt in fresh sessions 2026-07-09 15:45:28 +00:00
5 changed files with 78 additions and 2 deletions
@@ -1169,6 +1169,7 @@ export function Prompt(props: PromptProps) {
route.navigate({
type: "session",
sessionID,
fresh: true,
})
}, 50)
}
+1
View File
@@ -11,6 +11,7 @@ export type HomeRoute = {
export type SessionRoute = {
type: "session"
sessionID: string
fresh?: boolean
prompt?: PromptInfo
}
+4 -1
View File
@@ -237,7 +237,10 @@ export function Session() {
const toast = useToast()
const sdk = useSDK()
const editor = useEditorContext()
const rows = createSessionRows(() => route.sessionID)
const rows = createSessionRows(
() => route.sessionID,
() => route.fresh !== true,
)
createEffect(
on(descendantSessionIDs, (sessionIDs) => {
+2 -1
View File
@@ -20,7 +20,7 @@ export type SessionRow =
}
| { type: "assistant-footer"; messageID: string }
export function createSessionRows(sessionID: Accessor<string>) {
export function createSessionRows(sessionID: Accessor<string>, refresh: Accessor<boolean> = () => true) {
const data = useData()
const [rows, setRows] = createStore<SessionRow[]>([])
const revertBoundary = () => data.session.get(sessionID())?.revert?.messageID
@@ -54,6 +54,7 @@ export function createSessionRows(sessionID: Accessor<string>) {
createEffect(
on(sessionID, (id) => {
setRows(reconcile(reduce()))
if (!refresh()) return
void data.session.message.refresh(id).then(
() => {
if (sessionID() !== id) return
+70
View File
@@ -2066,6 +2066,76 @@ test("renders admitted prompts immediately and tracks them until promoted", asyn
}
})
test("does not refresh a fresh session timeline", async () => {
const events = createEventStream()
const sessionID = "session-refresh-race"
const messageID = "msg-refresh-race"
let refreshes = 0
const calls = createFetch((url) => {
if (url.pathname === `/api/session/${sessionID}/message`) {
refreshes++
return json({ data: [], cursor: {} })
}
}, events)
let data!: ReturnType<typeof useData>
let rows!: ReturnType<typeof createSessionRows>
function Probe() {
data = useData()
rows = createSessionRows(
() => sessionID,
() => false,
)
return <box />
}
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
</DataProvider>
</ProjectProvider>
</SDKProvider>
</TestTuiContexts>
))
try {
emitEvent(events, {
id: "evt-refresh-race-admitted",
created: 1,
type: "session.input.admitted",
durable: durable(sessionID),
data: {
sessionID,
inputID: messageID,
input: { type: "user", data: { text: "do not lose me" }, delivery: "steer" },
},
})
await wait(() => data.session.message.get(sessionID, messageID)?.type === "user")
emitEvent(events, {
id: "evt-refresh-race-promoted",
created: 2,
type: "session.input.promoted",
durable: durable(sessionID, 1),
data: { sessionID, inputID: messageID },
})
await wait(() => data.session.input.list(sessionID).length === 0)
expect(refreshes).toBe(0)
expect(rows).toContainEqual({ type: "message", messageID })
expect(data.session.message.get(sessionID, messageID)).toMatchObject({
id: messageID,
type: "user",
text: "do not lose me",
})
} finally {
app.renderer.destroy()
}
})
test("projects live instruction updates with their message ID", async () => {
const events = createEventStream()
const calls = createFetch(undefined, events)