mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
fix(x/renderer): surface permission/ask-human cards and load errors
ChatSidebar assembles the active tab's view state from individual props, not from chatTabStates — and the four permission/ask-human maps were still fed from legacy standalone state that nothing populates under the sessions runtime. Pending permission requests (recorded correctly in the turn file) therefore never rendered, leaving the chat on the 'Thinking…' shimmer. - Feed pendingAskHumanRequests / allPermissionRequests / permissionResponses / autoPermissionDecisions from the sessions hook (legacy fallback until stage 7). - New optional isThinking prop on ChatSidebar: waiting on a permission/ask-human no longer shimmers (isProcessing still blocks the composer, per the state contract). - Session load failures are now visible: an error item renders in the chat instead of a silent blank, and the store logs load/reload failures to the console. Verified the full data path against the real on-disk session (the suspended list-downloads turn): conversation renders and the pending permission card map carries the request, under StrictMode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ec5a534465
commit
58b016ba99
3 changed files with 22 additions and 7 deletions
|
|
@ -2728,6 +2728,13 @@ function App() {
|
|||
handlePromptSubmitRef.current?.({ text: `${name} connected successfully.`, files: [] })
|
||||
}, [])
|
||||
|
||||
// A failed session load must be visible, not a blank chat.
|
||||
const sessionLoadErrorItems = React.useMemo<ConversationItem[]>(() => (
|
||||
sessionChat.error
|
||||
? [{ id: 'session-load-error', kind: 'error', message: `Failed to load chat: ${sessionChat.error}`, timestamp: 0 }]
|
||||
: []
|
||||
), [sessionChat.error])
|
||||
|
||||
// Active tab renders from the sessions hook; background tabs keep the
|
||||
// legacy cached view state until stage 7 retires it.
|
||||
const chatTabStatesForRender = React.useMemo(() => {
|
||||
|
|
@ -6303,7 +6310,7 @@ function App() {
|
|||
}}
|
||||
onOpenChatHistory={() => void navigateToView({ type: 'chat-history' })}
|
||||
onOpenFullScreen={toggleRightPaneMaximize}
|
||||
conversation={sessionChat.chatState?.conversation ?? conversation}
|
||||
conversation={sessionChat.chatState?.conversation ?? (sessionLoadErrorItems.length > 0 ? sessionLoadErrorItems : conversation)}
|
||||
currentAssistantMessage={sessionChat.chatState?.currentAssistantMessage ?? currentAssistantMessage}
|
||||
chatTabStates={chatTabStatesForRender}
|
||||
viewportAnchors={chatViewportAnchorByTab}
|
||||
|
|
@ -6338,10 +6345,11 @@ function App() {
|
|||
? { title: activeCodeSession.session.title }
|
||||
: null
|
||||
}
|
||||
pendingAskHumanRequests={pendingAskHumanRequests}
|
||||
allPermissionRequests={allPermissionRequests}
|
||||
permissionResponses={permissionResponses}
|
||||
autoPermissionDecisions={autoPermissionDecisions}
|
||||
pendingAskHumanRequests={sessionChat.chatState?.pendingAskHumanRequests ?? pendingAskHumanRequests}
|
||||
allPermissionRequests={sessionChat.chatState?.allPermissionRequests ?? allPermissionRequests}
|
||||
permissionResponses={sessionChat.chatState?.permissionResponses ?? permissionResponses}
|
||||
autoPermissionDecisions={sessionChat.chatState?.autoPermissionDecisions ?? autoPermissionDecisions}
|
||||
isThinking={sessionChat.chatState?.isThinking}
|
||||
onPermissionResponse={handlePermissionResponse}
|
||||
onAskHumanResponse={handleAskHumanResponse}
|
||||
isToolOpenForTab={isToolOpenForTab}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,10 @@ interface ChatSidebarProps {
|
|||
chatTabStates?: Record<string, ChatTabViewState>
|
||||
viewportAnchors?: Record<string, ChatViewportAnchorState>
|
||||
isProcessing: boolean
|
||||
// Actively working (sessions runtime). When provided, drives the shimmer
|
||||
// instead of isProcessing so waiting on a permission/ask-human doesn't
|
||||
// render a "Thinking…" under the card.
|
||||
isThinking?: boolean
|
||||
isStopping?: boolean
|
||||
onStop?: () => void
|
||||
onSubmit: (message: PromptInputMessage, mentions?: FileMention[], attachments?: StagedAttachment[], searchEnabled?: boolean, codeMode?: 'claude' | 'codex', permissionMode?: PermissionMode) => void
|
||||
|
|
@ -211,6 +215,7 @@ export function ChatSidebar({
|
|||
chatTabStates = {},
|
||||
viewportAnchors = {},
|
||||
isProcessing,
|
||||
isThinking,
|
||||
isStopping,
|
||||
onStop,
|
||||
onSubmit,
|
||||
|
|
@ -754,7 +759,7 @@ export function ChatSidebar({
|
|||
</Message>
|
||||
)}
|
||||
|
||||
{isActive && isProcessing && !tabState.currentAssistantMessage && (
|
||||
{isActive && (isThinking ?? isProcessing) && !tabState.currentAssistantMessage && (
|
||||
<Message from="assistant">
|
||||
<MessageContent>
|
||||
<Shimmer duration={1}>Thinking...</Shimmer>
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ export class SessionChatStore {
|
|||
this.emit()
|
||||
} catch (error) {
|
||||
if (generation !== this.generation) return
|
||||
console.error('[session-chat] failed to load session', sessionId, error)
|
||||
this.loading = false
|
||||
this.error = error instanceof Error ? error.message : String(error)
|
||||
this.emit()
|
||||
|
|
@ -164,8 +165,9 @@ export class SessionChatStore {
|
|||
}
|
||||
this.latestEvents = turn.events
|
||||
this.emit()
|
||||
} catch {
|
||||
} catch (error) {
|
||||
// The next snapshot-worthy event will retry.
|
||||
console.error('[session-chat] failed to reload turn', turnId, error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue