Skip to content

feat(tools): add ask_user_question for mid-turn question prompting - #80

Merged
devproje merged 2 commits into
masterfrom
feat/ask-user-question
Sep 23, 2026
Merged

devproje merged 2 commits into
masterfrom
feat/ask-user-question

Conversation

@devproje

Copy link
Copy Markdown
Owner

What this changes

Every mid-turn round-trip so far was yes/no/deny dangerous-tool approval — there was no way for the model to ask the human an actual question and get an answer back. Adds ask_user_question, a PermissionSafe tool that blocks on the human's reply the same way tool approval already blocks, and returns the answer (a chosen option's text, or free-typed text) as the tool result.

It reuses approvalRouter's pending-channel map instead of adding a second one — one entry per session is safe because SessionLock already limits a session to one in-flight tool call at a time. wait() now takes the value to return on cancellation as a parameter ("deny" for approval, "" for a question) instead of hardcoding "deny", since an unanswered question isn't a denial.

modules/ask_question takes an injected AskFunc closure rather than importing core, mirroring web_search/web_fetch's WebBackendLookup pattern, since core already imports it and a direct import would cycle. The REPL client gains renderer.readLine (Enter submits, Backspace edits, Ctrl+C aborts) built on the same raw-byte primitive the existing y/a/N approval prompt uses, and renderer.ask, which accepts either a numbered option or free-typed text as the answer. The -p --format json/xml path and a nil AskFunc both fail fast instead of hanging, mirroring how approval_request is already auto-denied there.

How it was verified

  • make test-race passes
  • New behaviour has a test, or there is nothing to test

Added modules/ask_question/ask_question_test.go (missing-question error, nil-AskFunc error, answer round-trip) and server/sock/question_test.go (a full question_request/question round trip over a real websocket connection through the tool loop, mirroring the existing approval round-trip tests in server/sock/hil_test.go).

Checklist

  • Follows docs/CONVENTION.md: no comments, no :=, one var block per function in first-use order with err last, callees before callers, and main unconditionally last
  • New .go files carry the two-line SPDX header
  • Documentation updated if behaviour a user can see has changed (docs/ARCHITECTURE.md)
  • My contribution is licensed GPL-3.0-only, matching the project

Every mid-turn round-trip so far was yes/no/deny dangerous-tool
approval; there was no way for the model to ask the human an actual
question and get an answer back. Add ask_user_question, a
PermissionSafe tool that blocks on the human's reply the same way
tool approval already blocks, and returns the answer (a chosen
option's text, or free-typed text) as the tool result.

It reuses approvalRouter's pending-channel map instead of adding a
second one — one entry per session is safe because SessionLock already
limits a session to one in-flight tool call at a time. wait() now
takes the value to return on cancellation as a parameter ("deny" for
approval, "" for a question) instead of hardcoding "deny", since an
unanswered question isn't a denial.

modules/ask_question takes an injected AskFunc closure rather than
importing core, mirroring web_search/web_fetch's WebBackendLookup
pattern, since core already imports it and a direct import would
cycle. The REPL client gains renderer.readLine (Enter submits,
Backspace edits, Ctrl+C aborts) built on the same raw-byte primitive
the existing y/a/N approval prompt uses, and renderer.ask, which
accepts either a numbered option or free-typed text as the answer.
The -p --format json/xml path and a nil AskFunc both fail fast instead
of hanging, mirroring how approval_request is already auto-denied
there.
…tch in USAGE.md

The ARCHITECTURE.md paragraph added for ask_user_question conflated
two separate fallbacks into one: a nil AskFunc (which errors
immediately) and renderer.collect's auto-reply for -p --format
json/xml (which is a live /ws round trip that succeeds with an empty
answer, not an error). Split them apart and named the one production
call site that actually passes a nil ask (core/context.go, sizing a
context window, never running a turn).

USAGE.md's tool list never mentioned web_search/web_fetch (missed
when they were added) or ask_user_question, and its "works without a
cwd" list was stale against what buildTools actually gates on root !=
"" (only bash_exec and the file tools).
@devproje
devproje merged commit 8f8d09d into master Sep 23, 2026
3 checks passed
@devproje
devproje deleted the feat/ask-user-question branch September 23, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant