@@ -22,6 +22,8 @@ import { createSession, getSessionTasks, checkSessionExists } from "@/app/action
2222import { deriveSessionTitle , isPlaceholderSessionTitle } from "@/lib/sessionTitle" ;
2323import { normalizeSessionTimestamps } from "@/lib/sessionTimestamps" ;
2424import { getAgentWithResolvedKind , waitForSandboxAgentReady } from "@/app/actions/agents" ;
25+ import { getUiRuntimeConfig } from "@/app/actions/config" ;
26+ import { DEFAULT_STREAM_TIMEOUT_MS } from "@/lib/constants" ;
2527import { toast } from "sonner" ;
2628import { useRouter } from "next/navigation" ;
2729import { createMessageHandlers , extractMessagesFromTasks , extractApprovalMessagesFromTasks , extractTokenStatsFromTasks , createMessage , ADKMetadata , ProcessedToolCallData } from "@/lib/messageHandlers" ;
@@ -71,6 +73,22 @@ export default function ChatInterface({ selectedAgentName, selectedNamespace, se
7173 const pendingDecisionsRef = useRef < Record < string , ToolDecision > > ( { } ) ;
7274 /** Per-tool rejection reasons collected as the user rejects individual tools. */
7375 const pendingRejectionReasonsRef = useRef < Record < string , string > > ( { } ) ;
76+ // Stream inactivity timeout (ms), configurable via Helm (ui.streamTimeoutSeconds).
77+ const streamTimeoutMsRef = useRef < number > ( DEFAULT_STREAM_TIMEOUT_MS ) ;
78+
79+ useEffect ( ( ) => {
80+ let cancelled = false ;
81+ getUiRuntimeConfig ( )
82+ . then ( ( config ) => {
83+ if ( ! cancelled ) streamTimeoutMsRef . current = config . streamTimeoutMs ;
84+ } )
85+ . catch ( ( ) => {
86+ /* keep default on failure */
87+ } ) ;
88+ return ( ) => {
89+ cancelled = true ;
90+ } ;
91+ } , [ ] ) ;
7492
7593 const {
7694 isListening,
@@ -382,18 +400,24 @@ export default function ChatInterface({ selectedAgentName, selectedNamespace, se
382400 const consumeStream = async ( stream : AsyncIterable < unknown > ) => {
383401 let timeoutTimer : NodeJS . Timeout | null = null ;
384402 let streamActive = true ;
385- const STREAM_TIMEOUT_MS = 600000 ; // 10 minutes
403+
404+ const formatTimeout = ( ms : number ) : string => {
405+ const mins = ms / 60000 ;
406+ return mins >= 1 ? `${ Math . ceil ( mins ) } minutes` : `${ Math . round ( ms / 1000 ) } seconds` ;
407+ } ;
386408
387409 const startTimeout = ( ) => {
388410 if ( timeoutTimer ) clearTimeout ( timeoutTimer ) ;
411+ const streamTimeoutMs = streamTimeoutMsRef . current ;
389412 timeoutTimer = setTimeout ( ( ) => {
390413 if ( streamActive ) {
391- console . error ( "⏰ Stream timeout - no events received for 10 minutes" ) ;
392- toast . error ( "⏰ Stream timed out - no events received for 10 minutes" ) ;
414+ const label = formatTimeout ( streamTimeoutMs ) ;
415+ console . error ( `⏰ Stream timeout - no events received for ${ label } ` ) ;
416+ toast . error ( `⏰ Stream timed out - no events received for ${ label } ` ) ;
393417 streamActive = false ;
394418 abortControllerRef . current ?. abort ( ) ;
395419 }
396- } , STREAM_TIMEOUT_MS ) ;
420+ } , streamTimeoutMs ) ;
397421 } ;
398422 startTimeout ( ) ;
399423
0 commit comments