feat(agent): withhold ask_user_question from headless runs (no user to answer)

Merged
SW
4mo ago
+33-3

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

  • buildAgentTools gains an interactive flag (default true); ask_user_question is only registered when interactive.
  • Threaded via RunAgentWorkflowInput → runAgentStep → buildAgentTools.
  • handleStartChatRun (headless /api/chat/runs) passes interactive: 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: omits ask_user_question when interactive:false; keeps it by default / explicit true.
  • lib/agent + app/lib/workflows suites 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_question behind interactive (default true); omit in /api/chat/runs and customer-prompt-task.
    • Thread interactive via RunAgentWorkflowInput → runAgentStep → buildAgentTools; handleStartChatRun sets false; interactive chat unchanged.
    • Added tests for both modes.