feat(tui): queue pre-session input, auto-flush when session lands
The TUI is fully interactive from the first frame but `session.create` (agent + tools + MCP) takes ~2s. Plain-text messages typed before the session is live used to fail with "session not ready yet"; slash and shell commands worked but agent prompts were dropped. Now: - `dispatchSubmission` enqueues plain text when `sid` is null (slash/shell still short-circuit first) - `useMainApp` tracks sid transitions and kicks off one `sendQueued()` when the session first becomes ready; subsequent queued messages drain on `message.complete` as before - Fixed pre-existing double-Enter bug that dequeued without sid check User flow: type `hello` → shows in `queuedDisplay` preview → 2s later agent wakes → message auto-sends → reply streams. Zero wasted input.
This commit is contained in:
@@ -372,6 +372,28 @@ export function useMainApp(gw: GatewayClient) {
|
|||||||
sys
|
sys
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Flush any pre-session queued input once the session lands.
|
||||||
|
// Message.complete already drains subsequent items; this only kicks off the first.
|
||||||
|
const prevSidRef = useRef<null | string>(null)
|
||||||
|
useEffect(() => {
|
||||||
|
const prev = prevSidRef.current
|
||||||
|
prevSidRef.current = ui.sid
|
||||||
|
|
||||||
|
if (prev !== null || !ui.sid || ui.busy) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (composerRefs.queueEditRef.current !== null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = composerActions.dequeue()
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
sendQueued(next)
|
||||||
|
}
|
||||||
|
}, [ui.sid, ui.busy, composerActions, composerRefs, sendQueued])
|
||||||
|
|
||||||
const { pagerPageSize } = useInputHandlers({
|
const { pagerPageSize } = useInputHandlers({
|
||||||
actions: {
|
actions: {
|
||||||
answerClarify,
|
answerClarify,
|
||||||
|
|||||||
@@ -183,12 +183,7 @@ export function useSubmission(opts: UseSubmissionOptions) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const live = getUiState()
|
// Slash + shell run regardless of session state (each handles its own sid needs).
|
||||||
|
|
||||||
if (!live.sid) {
|
|
||||||
return sys('session not ready yet')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (looksLikeSlashCommand(full)) {
|
if (looksLikeSlashCommand(full)) {
|
||||||
appendMessage({ kind: 'slash', role: 'system', text: full })
|
appendMessage({ kind: 'slash', role: 'system', text: full })
|
||||||
composerActions.pushHistory(full)
|
composerActions.pushHistory(full)
|
||||||
@@ -204,6 +199,17 @@ export function useSubmission(opts: UseSubmissionOptions) {
|
|||||||
return shellExec(full.slice(1).trim())
|
return shellExec(full.slice(1).trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const live = getUiState()
|
||||||
|
|
||||||
|
// No session yet — queue the text and let the ready-flush effect send it.
|
||||||
|
if (!live.sid) {
|
||||||
|
composerActions.pushHistory(full)
|
||||||
|
composerActions.enqueue(full)
|
||||||
|
composerActions.clearIn()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const editIdx = composerRefs.queueEditRef.current
|
const editIdx = composerRefs.queueEditRef.current
|
||||||
composerActions.clearIn()
|
composerActions.clearIn()
|
||||||
|
|
||||||
@@ -269,10 +275,10 @@ export function useSubmission(opts: UseSubmissionOptions) {
|
|||||||
return turnController.interruptTurn({ appendMessage, gw, sid: live.sid, sys })
|
return turnController.interruptTurn({ appendMessage, gw, sid: live.sid, sys })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doubleTap && composerRefs.queueRef.current.length) {
|
if (doubleTap && live.sid && composerRefs.queueRef.current.length) {
|
||||||
const next = composerActions.dequeue()
|
const next = composerActions.dequeue()
|
||||||
|
|
||||||
if (next && live.sid) {
|
if (next) {
|
||||||
composerActions.setQueueEdit(null)
|
composerActions.setQueueEdit(null)
|
||||||
dispatchSubmission(next)
|
dispatchSubmission(next)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user