@@ -1712,6 +1712,35 @@ Return ONLY JSON per schema.`,
17121712
17131713 const trimmedContent = contentText . trim ( )
17141714
1715+ // IMPORTANT: For non-actionable requests (no relevant tools OR no tools at all),
1716+ // accept any non-empty text response as complete. This prevents infinite loops
1717+ // for simple Q&A like "hi" where the LLM just responds without tool calls.
1718+ // We use a lower threshold here (any non-empty response) because short greetings
1719+ // like "Hi." or "Hello." are valid complete responses to simple greetings.
1720+ const noToolsAvailable = ! availableTools || availableTools . length === 0
1721+ const hasAnyResponse = trimmedContent . length > 0 && ! isToolCallPlaceholder ( contentText )
1722+ if ( ( noToolsAvailable || ! isActionableRequest ) && hasAnyResponse && llmResponse . needsMoreWork !== true ) {
1723+ if ( isDebugLLM ( ) ) {
1724+ logLLM ( "Non-actionable request with substantive response - accepting as complete" , {
1725+ noToolsAvailable,
1726+ isActionableRequest,
1727+ responseLength : trimmedContent . length ,
1728+ responsePreview : trimmedContent . substring ( 0 , 100 ) ,
1729+ } )
1730+ }
1731+ finalContent = contentText
1732+ addMessage ( "assistant" , contentText )
1733+ emit ( {
1734+ currentIteration : iteration ,
1735+ maxIterations,
1736+ steps : progressSteps . slice ( - 3 ) ,
1737+ isComplete : true ,
1738+ finalContent,
1739+ conversationHistory : formatConversationForProgress ( conversationHistory ) ,
1740+ } )
1741+ break
1742+ }
1743+
17151744 // When agent claims it's done (needsMoreWork !== true) and has tool results + substantive response,
17161745 // determine if the task is complete
17171746 if ( hasToolResultsInCurrentTurn && hasSubstantiveResponse && llmResponse . needsMoreWork !== true ) {
0 commit comments