@@ -540,7 +540,10 @@ interface Props {
540540 composerPlaceholder ?: string ;
541541 onSubmitQuestionForm ?: QuestionFormSubmitHandler ;
542542 questionFormSubmitDisabled ?: boolean ;
543- onContinueRemainingTasks ?: ( assistantMessage : ChatMessage , todos : TodoItem [ ] ) => void ;
543+ onContinueRemainingTasks ?: (
544+ assistantMessage : ChatMessage ,
545+ todos : TodoItem [ ] ,
546+ ) => boolean | void | Promise < boolean | void > ;
544547 onAssistantFeedback ?: ( assistantMessage : ChatMessage , change : ChatMessageFeedbackChange ) => void ;
545548 // Client-side action for a brand-browser-assist od-card: open/focus the
546549 // Browser tab. Routed through the stable callbacks ref.
@@ -2685,6 +2688,7 @@ export function ChatPane({
26852688 < PinnedTodoSlot
26862689 messages = { displayMessages }
26872690 streaming = { streaming }
2691+ conversationId = { activeConversationId }
26882692 onContinueRemainingTasks = { onContinueRemainingTasks }
26892693 containerRef = { pinnedTodoRef }
26902694 />
@@ -3395,14 +3399,32 @@ function includeVirtualRowByKey<T extends { key: string }>(
33953399function PinnedTodoSlot ( {
33963400 messages,
33973401 streaming,
3402+ conversationId,
33983403 onContinueRemainingTasks,
33993404 containerRef,
34003405} : {
34013406 messages : ChatMessage [ ] ;
34023407 streaming : boolean ;
3403- onContinueRemainingTasks ?: ( assistantMessage : ChatMessage , todos : TodoItem [ ] ) => void ;
3408+ conversationId : string | null ;
3409+ onContinueRemainingTasks ?: (
3410+ assistantMessage : ChatMessage ,
3411+ todos : TodoItem [ ] ,
3412+ ) => boolean | void | Promise < boolean | void > ;
34043413 containerRef ?: MutableRefObject < HTMLDivElement | null > ;
34053414} ) {
3415+ const storageKey = `od:chat:continued-todo:${ conversationId ?? 'none' } ` ;
3416+ const [ dismissal , setDismissal ] = useState ( ( ) => ( {
3417+ storageKey,
3418+ snapshotKey : readContinuedTodoSnapshotKey ( storageKey ) ,
3419+ } ) ) ;
3420+ useEffect ( ( ) => {
3421+ setDismissal ( {
3422+ storageKey,
3423+ snapshotKey : readContinuedTodoSnapshotKey ( storageKey ) ,
3424+ } ) ;
3425+ } , [ storageKey ] ) ;
3426+ const dismissedSnapshotKey =
3427+ dismissal . storageKey === storageKey ? dismissal . snapshotKey : null ;
34063428 const input = latestTodoWriteInputForPinnedCard ( messages ) ;
34073429 if ( input == null ) return null ;
34083430
@@ -3413,6 +3435,19 @@ function PinnedTodoSlot({
34133435 ( event ) => event . kind === 'tool_use' && isTodoWriteToolName ( event . name ) ,
34143436 ) ,
34153437 ) ;
3438+ const ownerTodoEvent = owner ?. events
3439+ ? [ ...owner . events ] . reverse ( ) . find (
3440+ ( event ) => event . kind === 'tool_use' && isTodoWriteToolName ( event . name ) ,
3441+ )
3442+ : undefined ;
3443+ const snapshotKey =
3444+ owner &&
3445+ ownerTodoEvent &&
3446+ 'id' in ownerTodoEvent &&
3447+ typeof ownerTodoEvent . id === 'string'
3448+ ? `${ owner . id } :${ ownerTodoEvent . id } `
3449+ : null ;
3450+ if ( snapshotKey != null && snapshotKey === dismissedSnapshotKey ) return null ;
34163451 const unfinishedTodos = owner ? unfinishedTodosFromEvents ( owner . events ) : [ ] ;
34173452
34183453 return (
@@ -3425,15 +3460,41 @@ function PinnedTodoSlot({
34253460 runStreaming = { streaming }
34263461 runSucceeded = { ! streaming }
34273462 onContinue = {
3428- owner && unfinishedTodos . length > 0 && onContinueRemainingTasks
3429- ? ( ) => onContinueRemainingTasks ( owner , unfinishedTodos )
3463+ owner && snapshotKey && unfinishedTodos . length > 0 && onContinueRemainingTasks
3464+ ? ( ) => {
3465+ void Promise . resolve ( onContinueRemainingTasks ( owner , unfinishedTodos ) )
3466+ . then ( ( accepted ) => {
3467+ if ( accepted === false ) return ;
3468+ setDismissal ( { storageKey, snapshotKey } ) ;
3469+ writeContinuedTodoSnapshotKey ( storageKey , snapshotKey ) ;
3470+ } )
3471+ . catch ( ( ) => { } ) ;
3472+ }
34303473 : undefined
34313474 }
34323475 />
34333476 </ div >
34343477 ) ;
34353478}
34363479
3480+ function readContinuedTodoSnapshotKey ( storageKey : string ) : string | null {
3481+ if ( typeof window === 'undefined' ) return null ;
3482+ try {
3483+ return window . sessionStorage . getItem ( storageKey ) ;
3484+ } catch {
3485+ return null ;
3486+ }
3487+ }
3488+
3489+ function writeContinuedTodoSnapshotKey ( storageKey : string , snapshotKey : string ) : void {
3490+ if ( typeof window === 'undefined' ) return ;
3491+ try {
3492+ window . sessionStorage . setItem ( storageKey , snapshotKey ) ;
3493+ } catch {
3494+ // sessionStorage may be unavailable in sandboxed or privacy-restricted contexts.
3495+ }
3496+ }
3497+
34373498function QueuedSendStrip ( {
34383499 containerRef,
34393500 editingId,
0 commit comments