Repository navigation
[EPIC-X][LOU-X9] Built-in ask_question tool pauses durably until a human answers - #111
Merged
Merged
Conversation
…man answers
createAgent({ askQuestion: true }) (or askQuestionTool()) gives the agent
ask_question({ question, options?, allowFreeText? }). A call pauses through
the approval mechanism, so it is durable like any approval. The pending
record, the approval.requested event and the React pendingApproval carry
kind: 'question' and the question; agent.approvals.answer({ id, answer })
continues the run with { answer, option? }, declining yields a 'rejected'
tool error, the approve callback may return a string answer, and the hook
gets answer(text). An approved tool sees the decision's note as
ctx.approval.note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
# Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
askQuestionTool()(src/tools/built-in/askQuestion.ts). It is registered by the opt-increateAgent({ askQuestion: true }), which defaults to false. Its input is{ question: string; options?: string[]; allowFreeText?: boolean }and it setsneedsApproval: true, so a call pauses the run through the existing approval mechanism.PendingApprovalgains two optional fields:kind?: 'tool' | 'question'andquestion?: { text, options?, allowFreeText? }. New exports areApprovalKind,ApprovalQuestion,describeApproval()andASK_QUESTION_TOOL_NAME.createAgent()records anask_questionpause askind: 'question'.approval.requestedevent gains optionalkindandquestion. The React reducer'spendingApprovalexposes both fields too, anduseLoushyAgent()getsanswer(text).agent.approvals.answer({ id, answer })is a shorthand forresolve({ id, approved: true, note: answer }). The model then gets{ answer, option? }, whereoptionis the index of the matching option (case-insensitive). WithallowFreeText: false, an answer that is not one of the options reaches the model as a tool error.approved: falsegives the model akind: 'rejected'tool error that says "The user declined to answer the question".ApproveToolCallnow returnsboolean | string. A string approves the call with that string as its note, so code can answer questions.ctx.approval.note(new optionalToolExecutionContext.approval). This is howask_questionreads the answer, and it works the same way inside sub-agents.Why
Epic X matrix row "Agent asks the user a question" was ❌. eve ships a built-in
ask_questionthat pauses the session until a human answers. This change reuses the durable approval path (ApprovalStore, checkpoints with statusawaiting-approval,agent.resume), so questions survive sessions, stores and process restarts with no new persistence.Acceptance criteria
askQuestionTool()is exported and registered withcreateAgent({ askQuestion: true })(default false, documented). Its input is{ question, options?, allowFreeText? }.kind: 'question'. This change only adds fields to the record.resolve({ id, approved: true, note })and the newanswer({ id, answer })both continue the run with{ answer, option? }.approved: falsegives akind: 'rejected'tool error saying the user declined to answer.memoryStore()andSqliteStore(D30agent.resume).approval.requestedevent and the React reducer/hook exposekind: 'question'andpendingApproval.question. The hook hasanswer(text).approvecallback receives the question and may return a string answer (boolean | string).mockModelcover: ask, pause, answer, then the model sees the answer; decline; restart (memory and SQLite); options index;allowFreeText: false; hook reducerkind: 'question'; hookanswer();approvereturning a string; the session and stream event.How verified
npx tsc --noEmit: no errorsnpm run lint: 0 errors, 404 warnings, the same count as mainnpm run test:types: 30 passed, no type errorsnpx vitest run: 158 files passed; 2291 tests passed, 3 skippednpm run build && npm run build --workspace=packages/create-loushy-agent: both exit 0npm run docs:verify-snippets -- --skip-build: all 121 snippets type-check against src/, and 6 also run cleanlynpm run docs:llms:check: exit 0npm run test:coverage && npm run fallow: coverage 2291 passed; fallow exit 0 with "No issues found" and 0 above threshold. One existing clone warning remains between AnthropicProvider and OpenAIProvider; this PR does not touch those files.Files outside the listed scope, each a small addition:
src/types/tool.ts(ToolExecutionContext.approval) andsrc/execution/toolRunContext.ts, which pass the answer to the tool.src/execution/agentRun.ts, which builds theapproval.requestedevent.src/tools/built-in/index.ts, which exports the new tool.docs/tools.mdanddocs/api-overview.md.AgentExecutor.ts,toolCallExecution.ts,src/session/*,src/cli/*andapps/*are unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
Generated by Claude Code