11import { clearAgentZeroProcessingIndicator } from '@libs/actions/Report' ;
22import { applyPendingConciergeAction , clearPendingFollowupList , discardPendingConciergeAction , hidePendingFollowupList } from '@libs/actions/Report/SuggestedFollowup' ;
33import AgentZeroOptimisticStore , { MAX_AGE_MS } from '@libs/AgentZeroOptimisticStore' ;
4- import { getOptimisticRevealDurationMS , MIN_TRICKLE_TOKEN_COUNT , TICK_INTERVAL_MS , TRICKLE_HARD_CAP_MS } from '@libs/ConciergeRevealUtils' ;
4+ import { ACCELERATED_REMAINING_MS , getOptimisticRevealDurationMS , MIN_TRICKLE_TOKEN_COUNT , TICK_INTERVAL_MS , TRICKLE_HARD_CAP_MS } from '@libs/ConciergeRevealUtils' ;
55import Log from '@libs/Log' ;
66import { rand64 } from '@libs/NumberUtils' ;
77import type { ConciergeDraftEvent } from '@libs/Pusher/types' ;
@@ -46,6 +46,7 @@ function usePendingConciergeResponse(reportID: string | undefined) {
4646 const { dispatchLocalDraftEvent} = useConciergeDraftActions ( ) ;
4747
4848 const tokens = tokenizeForReveal ( fullHtml ) ;
49+ const accelerateRef = useRef < ( ( nowMs : number ) => void ) | null > ( null ) ;
4950
5051 // Captured into a ref so the trickle effect can re-run only on the IDs that
5152 // identify a distinct Concierge reply. Composer typing, unrelated Onyx emits,
@@ -58,6 +59,16 @@ function usePendingConciergeResponse(reportID: string | undefined) {
5859 trickleInputsRef . current = { pendingResponse, fullHtml, tokens, dispatchLocalDraftEvent, persistedAction} ;
5960 } ) ;
6061
62+ // Reconciliation: when the canonical reportComment lands in REPORT_ACTIONS
63+ // mid-trickle, fire the running loop's accelerator so the remaining reveal
64+ // finishes in ~1.5s instead of snapping the synthetic bubble closed.
65+ useEffect ( ( ) => {
66+ if ( ! persistedAction || ! accelerateRef . current ) {
67+ return ;
68+ }
69+ accelerateRef . current ( Date . now ( ) ) ;
70+ } , [ persistedAction ] ) ;
71+
6172 const lastOnlineTransitionAtRef = useRef < number > ( 0 ) ;
6273 const wasOfflineRef = useRef < boolean > ( isOffline ) ;
6374 useEffect ( ( ) => {
@@ -146,10 +157,16 @@ function usePendingConciergeResponse(reportID: string | undefined) {
146157 let sequence = 0 ;
147158 let intervalID : ReturnType < typeof setInterval > | null = null ;
148159 let trickleStart = 0 ;
149- const effectiveDuration = getOptimisticRevealDurationMS ( snapshotTokens . length ) ;
160+ // The accelerator recomputes effectiveDuration when the canonical reply lands
161+ // so the tail finishes quickly.
162+ let effectiveDuration = getOptimisticRevealDurationMS ( snapshotTokens . length ) ;
150163 let lastStage = 0 ;
151164 let cancelled = false ;
152165 const clampProgress = ( elapsedMs : number ) => Math . max ( 0 , Math . min ( 1 , elapsedMs / effectiveDuration ) ) ;
166+ // Snapshot of trickle progress at the moment the canonical reportComment
167+ // arrives. Presence (`arrival !== undefined`) doubles as the
168+ // "acceleration fired" check that selects the completion reason below.
169+ let arrival : { progress : number ; elapsedMs : number } | undefined ;
153170
154171 const dispatch = ( status : ConciergeDraftEvent [ 'status' ] , finalRenderedHTML : string ) => {
155172 if ( cancelled ) {
@@ -175,24 +192,46 @@ function usePendingConciergeResponse(reportID: string | undefined) {
175192 intervalID = null ;
176193 }
177194 const totalElapsedMs = trickleStart === 0 ? 0 : Date . now ( ) - trickleStart ;
178- const reason : 'natural' | 'stale_cap' = totalElapsedMs >= TRICKLE_HARD_CAP_MS ? 'stale_cap' : 'natural' ;
195+ let reason : 'natural' | 'accelerated' | 'stale_cap' = 'natural' ;
196+ if ( arrival ) {
197+ reason = 'accelerated' ;
198+ } else if ( totalElapsedMs >= TRICKLE_HARD_CAP_MS ) {
199+ reason = 'stale_cap' ;
200+ }
179201 Log . info ( '[ConciergeTrickle] complete' , false , {
180202 reportActionID,
181203 reason,
182204 tokenCount : snapshotTokens . length ,
183205 durationMs : effectiveDuration ,
184206 totalElapsedMs,
207+ arrivedAtProgress : arrival ?. progress ,
208+ arrivedAtElapsedMs : arrival ?. elapsedMs ,
185209 } ) ;
186210 dispatch ( 'completed' , snapshotTokens . at ( - 1 ) ?? snapshotHtml ) ;
187211 // Don't reapply our older optimistic when the canonical is already there —
188- // Reapplying it would overwrite server-added follow-up buttons and deep-link Pressables.
189- if ( trickleInputsRef . current . persistedAction ) {
212+ // it would clobber server-added markup (follow-up buttons, deep-link
213+ // Pressables). `arrival` covers the accelerator path; the live ref read
214+ // catches arrivals during the pre-trickle setTimeout where the accelerator
215+ // no-ops on null intervalID.
216+ if ( arrival || trickleInputsRef . current . persistedAction ) {
190217 discardPendingConciergeAction ( reportID ) ;
191218 } else {
192219 applyPendingConciergeAction ( reportID , reportAction ) ;
193220 }
194221 } ;
195222
223+ accelerateRef . current = ( nowMs : number ) => {
224+ if ( ! intervalID || trickleStart === 0 ) {
225+ return ;
226+ }
227+ const elapsed = nowMs - trickleStart ;
228+ // Compressing effectiveDuration is what makes progress hit 1 within
229+ // ACCELERATED_REMAINING_MS — the next tick observes progress >= 1
230+ // and runs completeAndApply via the normal path.
231+ arrival = { progress : clampProgress ( elapsed ) , elapsedMs : elapsed } ;
232+ effectiveDuration = elapsed + ACCELERATED_REMAINING_MS ;
233+ } ;
234+
196235 const startTrickle = ( ) => {
197236 if ( cancelled ) {
198237 return ;
@@ -242,6 +281,7 @@ function usePendingConciergeResponse(reportID: string | undefined) {
242281 if ( intervalID ) {
243282 clearInterval ( intervalID ) ;
244283 }
284+ accelerateRef . current = null ;
245285 } ;
246286 } , [ reportID , reportActionID ] ) ;
247287}
0 commit comments