feat(runtime): add AskUserQuestion for in-turn bounded choices #825

Description

@Astro-Han

Problem

Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

Decided v1 contract

AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
  • The client always adds an Other choice with free-text input. The model does not provide is_other.
  • One option is selected per question. Multi-select is out of scope.
  • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
  • The tool returns and persists one normal JSON tool result:
{answers: [{question: string,answer: string|null},],}
  • permissionRequired: false.
  • Available in explore, ask, execute, and bypass.
  • Registered only for interactive root agents in desktop and TUI.
  • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

Runtime design

Reuse the existing await machinery; do not build a second waiting pipeline.

  1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
  2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
  3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
  4. Add a distinct user_question_request session event.
  5. Route the UI answer back as a distinct UI → runtime response command.
  6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
  7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
  8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

Interaction design

Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

PR1 — permission composer takeover

  • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
  • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
  • Keep the action summary, risk, allow/deny actions, and Stop visible.
  • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
  • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
  • Preserve the existing per-session FIFO and remember-for-turn semantics.

PR2 — AskUserQuestion runtime + TUI

  • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
  • Register the tool only in the interactive TUI root-agent toolset.
  • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
  • Keep desktop unregistered in this slice.
  • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

PR3 — AskUserQuestion desktop

  • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
  • Reuse PR1's composer-takeover container.
  • Show one question at a time with option descriptions and client-owned Other input.
  • Use explicit Previous / Next controls; selecting an option does not auto-advance.
  • Show progress (2 / 3), allow review, and submit all answers once on the last question.
  • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
  • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

Acceptance criteria

  • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
  • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
  • The answer is persisted exactly once as the tool result and survives history replay.
  • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
  • Parallel permission/question requests are queued without overwrite or competing focus.
  • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
  • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

Out of scope

Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
       blocks
      (function() {
      function addCopyButtons() {
      document.querySelectorAll('pre code').forEach(function(codeBlock) {
      if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
      codeBlock.parentElement.setAttribute('data-copy-added', 'true');
      var btn = document.createElement('button');
      btn.textContent = 'Copy';
      btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
      btn.onmouseover = function() { this.style.opacity = '1'; };
      btn.onmouseout = function() { this.style.opacity = '0.7'; };
      btn.onclick = function() {
      navigator.clipboard.writeText(codeBlock.textContent).then(function() {
      btn.textContent = 'Copied!';
      setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
      });
      };
      codeBlock.parentElement.style.position = 'relative';
      codeBlock.parentElement.appendChild(btn);
      });
      }
      addCopyButtons();
      // Re-run on dynamic content
      var observer = new MutationObserver(addCopyButtons);
      observer.observe(document.body, { childList: true, subtree: true });
      })();
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      feat(runtime): add AskUserQuestion for in-turn bounded choices #825

      Description

      @Astro-Han

      Problem

      Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

      Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

      Decided v1 contract

      AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
      • The client always adds an Other choice with free-text input. The model does not provide is_other.
      • One option is selected per question. Multi-select is out of scope.
      • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
      • The tool returns and persists one normal JSON tool result:
      {answers: [{question: string,answer: string|null},],}
      • permissionRequired: false.
      • Available in explore, ask, execute, and bypass.
      • Registered only for interactive root agents in desktop and TUI.
      • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

      There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

      Runtime design

      Reuse the existing await machinery; do not build a second waiting pipeline.

      1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
      2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
      3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
      4. Add a distinct user_question_request session event.
      5. Route the UI answer back as a distinct UI → runtime response command.
      6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
      7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
      8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

      The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

      Interaction design

      Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

      PR1 — permission composer takeover

      • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
      • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
      • Keep the action summary, risk, allow/deny actions, and Stop visible.
      • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
      • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
      • Preserve the existing per-session FIFO and remember-for-turn semantics.

      PR2 — AskUserQuestion runtime + TUI

      • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
      • Register the tool only in the interactive TUI root-agent toolset.
      • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
      • Keep desktop unregistered in this slice.
      • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

      PR3 — AskUserQuestion desktop

      • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
      • Reuse PR1's composer-takeover container.
      • Show one question at a time with option descriptions and client-owned Other input.
      • Use explicit Previous / Next controls; selecting an option does not auto-advance.
      • Show progress (2 / 3), allow review, and submit all answers once on the last question.
      • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
      • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

      Acceptance criteria

      • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
      • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
      • The answer is persisted exactly once as the tool result and survives history replay.
      • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
      • Parallel permission/question requests are queued without overwrite or competing focus.
      • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
      • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

      Out of scope

      Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          feat(runtime): add AskUserQuestion for in-turn bounded choices #825

          Description

          @Astro-Han

          Problem

          Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

          Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

          Decided v1 contract

          AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
          • The client always adds an Other choice with free-text input. The model does not provide is_other.
          • One option is selected per question. Multi-select is out of scope.
          • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
          • The tool returns and persists one normal JSON tool result:
          {answers: [{question: string,answer: string|null},],}
          • permissionRequired: false.
          • Available in explore, ask, execute, and bypass.
          • Registered only for interactive root agents in desktop and TUI.
          • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

          There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

          Runtime design

          Reuse the existing await machinery; do not build a second waiting pipeline.

          1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
          2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
          3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
          4. Add a distinct user_question_request session event.
          5. Route the UI answer back as a distinct UI → runtime response command.
          6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
          7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
          8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

          The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

          Interaction design

          Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

          PR1 — permission composer takeover

          • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
          • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
          • Keep the action summary, risk, allow/deny actions, and Stop visible.
          • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
          • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
          • Preserve the existing per-session FIFO and remember-for-turn semantics.

          PR2 — AskUserQuestion runtime + TUI

          • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
          • Register the tool only in the interactive TUI root-agent toolset.
          • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
          • Keep desktop unregistered in this slice.
          • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

          PR3 — AskUserQuestion desktop

          • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
          • Reuse PR1's composer-takeover container.
          • Show one question at a time with option descriptions and client-owned Other input.
          • Use explicit Previous / Next controls; selecting an option does not auto-advance.
          • Show progress (2 / 3), allow review, and submit all answers once on the last question.
          • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
          • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

          Acceptance criteria

          • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
          • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
          • The answer is persisted exactly once as the tool result and survives history replay.
          • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
          • Parallel permission/question requests are queued without overwrite or competing focus.
          • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
          • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

          Out of scope

          Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              feat(runtime): add AskUserQuestion for in-turn bounded choices #825

              Description

              @Astro-Han

              Problem

              Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

              Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

              Decided v1 contract

              AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
              • The client always adds an Other choice with free-text input. The model does not provide is_other.
              • One option is selected per question. Multi-select is out of scope.
              • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
              • The tool returns and persists one normal JSON tool result:
              {answers: [{question: string,answer: string|null},],}
              • permissionRequired: false.
              • Available in explore, ask, execute, and bypass.
              • Registered only for interactive root agents in desktop and TUI.
              • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

              There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

              Runtime design

              Reuse the existing await machinery; do not build a second waiting pipeline.

              1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
              2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
              3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
              4. Add a distinct user_question_request session event.
              5. Route the UI answer back as a distinct UI → runtime response command.
              6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
              7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
              8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

              The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

              Interaction design

              Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

              PR1 — permission composer takeover

              • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
              • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
              • Keep the action summary, risk, allow/deny actions, and Stop visible.
              • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
              • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
              • Preserve the existing per-session FIFO and remember-for-turn semantics.

              PR2 — AskUserQuestion runtime + TUI

              • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
              • Register the tool only in the interactive TUI root-agent toolset.
              • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
              • Keep desktop unregistered in this slice.
              • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

              PR3 — AskUserQuestion desktop

              • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
              • Reuse PR1's composer-takeover container.
              • Show one question at a time with option descriptions and client-owned Other input.
              • Use explicit Previous / Next controls; selecting an option does not auto-advance.
              • Show progress (2 / 3), allow review, and submit all answers once on the last question.
              • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
              • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

              Acceptance criteria

              • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
              • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
              • The answer is persisted exactly once as the tool result and survives history replay.
              • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
              • Parallel permission/question requests are queued without overwrite or competing focus.
              • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
              • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

              Out of scope

              Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  feat(runtime): add AskUserQuestion for in-turn bounded choices #825

                  Description

                  @Astro-Han

                  Problem

                  Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

                  Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

                  Decided v1 contract

                  AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
                  • The client always adds an Other choice with free-text input. The model does not provide is_other.
                  • One option is selected per question. Multi-select is out of scope.
                  • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
                  • The tool returns and persists one normal JSON tool result:
                  {answers: [{question: string,answer: string|null},],}
                  • permissionRequired: false.
                  • Available in explore, ask, execute, and bypass.
                  • Registered only for interactive root agents in desktop and TUI.
                  • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

                  There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

                  Runtime design

                  Reuse the existing await machinery; do not build a second waiting pipeline.

                  1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
                  2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
                  3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
                  4. Add a distinct user_question_request session event.
                  5. Route the UI answer back as a distinct UI → runtime response command.
                  6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
                  7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
                  8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

                  The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

                  Interaction design

                  Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

                  PR1 — permission composer takeover

                  • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
                  • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
                  • Keep the action summary, risk, allow/deny actions, and Stop visible.
                  • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
                  • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
                  • Preserve the existing per-session FIFO and remember-for-turn semantics.

                  PR2 — AskUserQuestion runtime + TUI

                  • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
                  • Register the tool only in the interactive TUI root-agent toolset.
                  • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
                  • Keep desktop unregistered in this slice.
                  • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

                  PR3 — AskUserQuestion desktop

                  • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
                  • Reuse PR1's composer-takeover container.
                  • Show one question at a time with option descriptions and client-owned Other input.
                  • Use explicit Previous / Next controls; selecting an option does not auto-advance.
                  • Show progress (2 / 3), allow review, and submit all answers once on the last question.
                  • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
                  • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

                  Acceptance criteria

                  • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
                  • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
                  • The answer is persisted exactly once as the tool result and survives history replay.
                  • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
                  • Parallel permission/question requests are queued without overwrite or competing focus.
                  • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
                  • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

                  Out of scope

                  Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      feat(runtime): add AskUserQuestion for in-turn bounded choices #825

                      Description

                      @Astro-Han

                      Problem

                      Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

                      Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

                      Decided v1 contract

                      AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
                      • The client always adds an Other choice with free-text input. The model does not provide is_other.
                      • One option is selected per question. Multi-select is out of scope.
                      • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
                      • The tool returns and persists one normal JSON tool result:
                      {answers: [{question: string,answer: string|null},],}
                      • permissionRequired: false.
                      • Available in explore, ask, execute, and bypass.
                      • Registered only for interactive root agents in desktop and TUI.
                      • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

                      There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

                      Runtime design

                      Reuse the existing await machinery; do not build a second waiting pipeline.

                      1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
                      2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
                      3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
                      4. Add a distinct user_question_request session event.
                      5. Route the UI answer back as a distinct UI → runtime response command.
                      6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
                      7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
                      8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

                      The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

                      Interaction design

                      Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

                      PR1 — permission composer takeover

                      • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
                      • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
                      • Keep the action summary, risk, allow/deny actions, and Stop visible.
                      • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
                      • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
                      • Preserve the existing per-session FIFO and remember-for-turn semantics.

                      PR2 — AskUserQuestion runtime + TUI

                      • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
                      • Register the tool only in the interactive TUI root-agent toolset.
                      • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
                      • Keep desktop unregistered in this slice.
                      • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

                      PR3 — AskUserQuestion desktop

                      • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
                      • Reuse PR1's composer-takeover container.
                      • Show one question at a time with option descriptions and client-owned Other input.
                      • Use explicit Previous / Next controls; selecting an option does not auto-advance.
                      • Show progress (2 / 3), allow review, and submit all answers once on the last question.
                      • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
                      • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

                      Acceptance criteria

                      • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
                      • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
                      • The answer is persisted exactly once as the tool result and survives history replay.
                      • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
                      • Parallel permission/question requests are queued without overwrite or competing focus.
                      • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
                      • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

                      Out of scope

                      Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          feat(runtime): add AskUserQuestion for in-turn bounded choices #825

                          Description

                          @Astro-Han

                          Problem

                          Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

                          Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

                          Decided v1 contract

                          AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
                          • The client always adds an Other choice with free-text input. The model does not provide is_other.
                          • One option is selected per question. Multi-select is out of scope.
                          • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
                          • The tool returns and persists one normal JSON tool result:
                          {answers: [{question: string,answer: string|null},],}
                          • permissionRequired: false.
                          • Available in explore, ask, execute, and bypass.
                          • Registered only for interactive root agents in desktop and TUI.
                          • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

                          There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

                          Runtime design

                          Reuse the existing await machinery; do not build a second waiting pipeline.

                          1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
                          2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
                          3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
                          4. Add a distinct user_question_request session event.
                          5. Route the UI answer back as a distinct UI → runtime response command.
                          6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
                          7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
                          8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

                          The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

                          Interaction design

                          Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

                          PR1 — permission composer takeover

                          • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
                          • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
                          • Keep the action summary, risk, allow/deny actions, and Stop visible.
                          • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
                          • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
                          • Preserve the existing per-session FIFO and remember-for-turn semantics.

                          PR2 — AskUserQuestion runtime + TUI

                          • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
                          • Register the tool only in the interactive TUI root-agent toolset.
                          • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
                          • Keep desktop unregistered in this slice.
                          • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

                          PR3 — AskUserQuestion desktop

                          • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
                          • Reuse PR1's composer-takeover container.
                          • Show one question at a time with option descriptions and client-owned Other input.
                          • Use explicit Previous / Next controls; selecting an option does not auto-advance.
                          • Show progress (2 / 3), allow review, and submit all answers once on the last question.
                          • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
                          • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

                          Acceptance criteria

                          • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
                          • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
                          • The answer is persisted exactly once as the tool result and survives history replay.
                          • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
                          • Parallel permission/question requests are queued without overwrite or competing focus.
                          • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
                          • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

                          Out of scope

                          Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              feat(runtime): add AskUserQuestion for in-turn bounded choices #825

                              Description

                              @Astro-Han

                              Problem

                              Maka's PermissionEngine already has most of the round trip needed for in-turn human input: a parked Promise, stream-watchdog pause, timeout, and clean abort when the turn stops. Maka still has no model-callable question tool, so an agent can only ask in prose and end the turn. It cannot wait for a bounded user decision and continue the same turn.

                              Open-ended follow-up is not the target. The model should continue to ask those in normal assistant text and wait for the next user turn.

                              Decided v1 contract

                              AskUserQuestion({questions: [{question: string,options: [{label: string,description?: string,}, ...],// 2–3 model-provided, mutually exclusive options}, ...],// 1–3 independent questions})
                              • The client always adds an Other choice with free-text input. The model does not provide is_other.
                              • One option is selected per question. Multi-select is out of scope.
                              • The UI returns { requestId, answers: Array<string | null> }, ordered exactly like questions. null means the user explicitly left that question unanswered.
                              • The tool returns and persists one normal JSON tool result:
                              {answers: [{question: string,answer: string|null},],}
                              • permissionRequired: false.
                              • Available in explore, ask, execute, and bypass.
                              • Registered only for interactive root agents in desktop and TUI.
                              • Not registered in headless/Harbor, Pi transport, or child-agent toolsets.

                              There is no current excludedToolNames extension point on main. v1 does not add a new root-agent tool blacklist solely for this tool.

                              Runtime design

                              Reuse the existing await machinery; do not build a second waiting pipeline.

                              1. Extract the turn-scoped parked-request registry from PermissionEngine into a shared low-level primitive.
                              2. Keep permission policy and remember-for-turn behavior owned by PermissionEngine.
                              3. Let ToolRuntime use the same primitive for user questions, including watchdog pause, late-response rejection, and turn stop/abort cleanup. Permission keeps its existing 300-second hard timeout; user questions have no fixed timeout and remain parked until answer, explicit skip, stop, abort, or turn teardown.
                              4. Add a distinct user_question_request session event.
                              5. Route the UI answer back as a distinct UI → runtime response command.
                              6. Do not persist a second user_question_response event. The normal tool result is the single source of truth for the answer.
                              7. Do not synthesize a UserMessage, because that would create a false user turn and break tool-call/result adjacency.
                              8. Do not add a dedicated user_answerToolResultContent kind; the existing JSON result preserves the structured answer without expanding every result consumer.

                              The tool description must say that it is for bounded choices required to continue the current turn. It must not replace ordinary open-ended conversation.

                              Interaction design

                              Deliver three sequential PRs. Start each one from the latest main after the previous PR is squash-merged.

                              PR1 — permission composer takeover

                              • Replace the full-screen permission modal with a compact surface that temporarily takes over the existing composer slot.
                              • Hide the composer while waiting, but preserve its draft and restore it unchanged after allow, deny, or stop.
                              • Keep the action summary, risk, allow/deny actions, and Stop visible.
                              • Put long raw parameters and diffs behind a disclosure with a capped internal scroll area.
                              • Keep the default height compact (about 160 px at the standard chat measure); expanded content grows upward with a viewport-relative cap.
                              • Preserve the existing per-session FIFO and remember-for-turn semantics.

                              PR2 — AskUserQuestion runtime + TUI

                              • Add the core request/response/event contract, shared turn-scoped await primitive, AI SDK tool execution, abort handling, and runtime response routing.
                              • Register the tool only in the interactive TUI root-agent toolset.
                              • Reuse the TUI's existing selection overlay, ask the 1–3 questions sequentially, open short text input for Other, use Esc to leave only the current question unanswered, and submit all answers once.
                              • Keep desktop unregistered in this slice.
                              • Prove the complete same-turn round trip through focused core, runtime, and TUI tests.

                              PR3 — AskUserQuestion desktop

                              • Register the tool in the desktop root-agent toolset and add the main/preload/renderer response route.
                              • Reuse PR1's composer-takeover container.
                              • Show one question at a time with option descriptions and client-owned Other input.
                              • Use explicit Previous / Next controls; selecting an option does not auto-advance.
                              • Show progress (2 / 3), allow review, and submit all answers once on the last question.
                              • Let permission and question requests share one per-session interaction FIFO so parallel tool calls cannot create competing input surfaces.
                              • Cover one complete fake-backend desktop journey plus focused renderer, IPC, and computed-style contracts.

                              Acceptance criteria

                              • A model can call AskUserQuestion, receive the user's answer as the tool result, and continue in the same turn.
                              • Desktop and TUI both support 1–3 single-choice questions plus client-owned Other.
                              • The answer is persisted exactly once as the tool result and survives history replay.
                              • Permission timeout and user-question answer/skip/stop/abort/teardown paths settle cleanly without leaving a parked request; late responses are ignored.
                              • Parallel permission/question requests are queued without overwrite or competing focus.
                              • The tool is absent from headless/Harbor, Pi transport, and child-agent toolsets.
                              • The permission tray and question tray are covered by one representative fake-backend desktop E2E journey plus focused runtime, UI, TUI, and computed-style contracts.

                              Out of scope

                              Pure free-text questions, secrets/masked input, autoResolutionMs, caller-provided is_other, per-question ids or headers, multi-select, more than three questions, headless/subagent availability, a dedicated transcript question card, a persisted response event, a dedicated answer result kind, and a new generic tool-exclusion system.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions