Skip to content

[BUG] The AI tool-confirmation dialog still opens in Obsidian during an interactive run #1631

Description

@chhoumann

Describe the bug

quickadd:interactive forwards a run's prompts to the connected client. After
#1614 that includes every prompt the Template and Capture engines open. The AI
assistant's tool-confirmation dialog is the one that still does not travel: a
Macro whose user script calls quickAddApi.ai.agent(...) with a non-readOnly tool
opens "Run AI tool: X?" in the Obsidian window while the client's /poll returns
nothing, and POST /abort cannot reach it.

src/ai/tools/Agent.ts confirm() reads neither choiceExecutor.promptProvider
nor the routing seam, and goes straight to AIToolConfirmModal.Prompt. The
Agent is constructed with the choiceExecutor (src/quickAddApi.ts), so the
executor is in hand - only the routing is missing.

needsGlobalConfirmation defaults to "destructive", so most tools ask.

What #1614 already fixed here

The headless case: a plain quickadd:run (no ui) used to hang forever on this
modal, which was strictly worse than the #1614 symptom. It now aborts with an
actionable message naming the three ways to make the choice runnable headlessly.
The remote case was left out of that PR to keep it reviewable, and is documented
as a known limitation in docs/.../CLI.md.

Why it needs more than a one-line route

Unlike the pickers #1614 converted, this prompt is not a suggester over a list. It
has four outcomes (allow / allow-all / deny / abort, see
AIToolConfirmModal), and dismissing it means deny, not cancel - so it maps
onto neither the confirm prompt type (two-state, dismissal aborts the run) nor
plainly onto suggester (whose empty reply the strict engine channel treats as a
dismissal). Either the wire needs a prompt type whose dismissal is a real answer,
or the four outcomes need to be modelled as a strict suggester with an explicit
"deny" row and a documented dismissal mapping.

It also carries the tool name and arguments, which is the most sensitive payload
anything on this bridge would send - worth deciding deliberately rather than as a
line inside a routing change.

Shape of a fix

Route Agent.confirm through routePrompt (src/interactive/routePrompt.ts),
with a remote branch built on a strict four-item engineChoice and
{"cancelled": true} mapped to "deny" to match the modal. Keep the existing
headless guard verbatim; keep the app branch as it is. Then drop the
limitation note from docs/src/content/docs/docs/Advanced/CLI.md.

Found while fixing #1614.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions