feat(agent): withhold ask_user_question from headless runs (no user to answer)
Part of recoupable/chat#1833. Base: main.
Why
ask_user_question has no server execute — only a streaming chat UI fulfills it. In headless/async runs (customer-prompt-task, /api/chat/runs) there is no user to answer, so it's a dead-end.
Verified on prod (chat 6d7ef610, Sonnet 5, Apache w/ YouTube): the agent scraped real data (1.54M subs, 1.11B views), correctly reasoned CPM isn't sourceable, labeled "Send honest report with real data only" as Recommended — and then called ask_user_question instead of sending. The run hung with nothing delivered. The tool's mere availability blocked the (correct, self-recommended) email.
Fix
buildAgentToolsgains aninteractiveflag (defaulttrue);ask_user_questionis only registered when interactive.- Threaded via
RunAgentWorkflowInput→runAgentStep→buildAgentTools. handleStartChatRun(headless/api/chat/runs) passesinteractive: false;handleChatWorkflowStream(interactive chat) keeps the default → unchanged.
With no ask-the-user escape, a headless agent must act — send an honest result or stop — rather than ask a question no one will answer. Pairs with the runAgentStep headless honest-send item.
Tests (TDD)
- RED→GREEN
buildAgentTools: omitsask_user_questionwheninteractive:false; keeps it by default / explicit true. lib/agent+app/lib/workflowssuites green (353); tsc adds no new errors (3 pre-existing baseline errors unchanged); eslint clean.
🤖 Generated with Claude Code
Summary by cubic
Prevent headless runs from hanging by omitting ask_user_question when no user is present. Adds an interactive flag so only interactive chat exposes the tool; headless APIs pass interactive: false.
- Bug Fixes
- Gate
ask_user_questionbehindinteractive(default true); omit in/api/chat/runsandcustomer-prompt-task. - Thread
interactiveviaRunAgentWorkflowInput→runAgentStep→buildAgentTools;handleStartChatRunsets false; interactive chat unchanged. - Added tests for both modes.
- Gate