Closed bug report

Gated-verb guidance teaches a blind wait: neither the refusal hint nor adom-bridge-permissions mentions wait:true or responseMode:job

Ray · 1mo ago ·closed by John Lauer

Environment: adom-bridge 2.1.118 (2d00e94e, built 2026-09-13), Linux container driving a Windows host over the relay, caller is a turn-based AI session.

A gated verb's refusal hint tells the caller to ask the human and then retry. For a turn-based caller that produces a blind wait: nothing tells it when the human clicks, so it stops and waits for the human to say "approved" in chat. On 2026-09-14 a send_files write to C:\Users\Ray\Adom Downloads\... was refused, I relayed the switch to the user as instructed, and then sat idle until they told me they had clicked. The file transfer could have resumed on its own.

The capability to avoid that already exists and is good. The refusal hint just does not mention it.

The gap is the documented procedure, not just the hint

This is not a caller who skipped the docs. adom-bridge-permissions is the skill whose own description says "Read this when a verb refuses with errorCode needs_approval", and its recipe under "Grants are HUMAN-ONLY" is:

  1. Surface the prompt (request_shell_approval)
  2. Ask the human, in your conversation, to click Approve
  3. RETRY the original verb unchanged

That is exactly what I did, and step 2 is the blind wait. Searching the whole skill:

wait.*true             0
responseMode           0
job_events             0
job_status             0
waiting_for_approval   0

Neither mechanism appears anywhere in it. So an agent that reads the canonical skill, follows it exactly, and never guesses will idle every single time. The features shipped in 2.1.50 and 2.1.100; the guidance in front of them did not catch up, and the guidance is what agents obey.

What the hint says now

You CANNOT grant this yourself - shell approval is HUMAN-ONLY. An approval prompt has been
surfaced in Adom Bridge on this machine (the ab window is shown if it was hidden, plus a
toast). Tell the user the EXACT switch: the 'Auto mode' button in the Adom Bridge window
footer, or Settings > Shell Commands - a 'bypass permissions' mode in their AI app (Claude
Code / Codex) is a DIFFERENT switch and does not cover this. Then RETRY - one grant covers
the whole exec/write/registry/kill class. Re-surface the prompt anytime with
`request_shell_approval` (it pops the dialog + brings ab to the front)

It names request_shell_approval but not its wait argument, and it does not mention response modes at all. "Then RETRY" is the only timing guidance, and it implies polling by hand or waiting for the human to speak.

What the catalogue already offers

Both of these are documented in commands, and either one would have closed the loop:

  1. request_shell_approval {wait: true, timeoutSec: 1..600}, whose own description is precisely this case: "BLOCK until the user grants on ANY surface (dialog, toast, settings, Hydrogen, another of their boxes) or timeoutSec elapses, and return the outcome inline. A relay caller is turn-based and would otherwise learn of the click only when it next re-read status." Returns granted:true, then retry.
  2. responseMode: "job", which is better still, because the original operation is not lost: "A GATED verb under a job does not fail with needs_approval - it PARKS (event waiting_for_approval), the prompt surfaces on every human surface, and a real decision on any of them resumes the same operation once." With job_events {jobId, waitMs} long-polling, the caller learns of the click within a second and never re-sends the payload.

Ask

Fix the guidance in both places, because the hint and the skill are read by different agents at different moments and today they agree with each other on the wrong answer.

  1. The skill (adom-bridge-permissions): make step 2 of the recipe "block on the grant" rather than "ask the human and wait". request_shell_approval {wait:true, timeoutSec:N} replaces steps 1 and 2 outright, and responseMode:"job" replaces all three.
  2. The refusal hint: add one line naming both, so the caller reaches for them at the moment it is blocked. Something like: "Do not idle: call this verb again with responseMode:"job" and long-poll job_events, and the operation resumes itself on approval; or request_shell_approval {wait:true} to block until granted, then retry." The hint is the highest-traffic surface here, it is what an agent reads at the moment of failure, and today it points at the weakest of the three available paths.

Secondary observation, lower confidence

_response_modes says "ANY verb accepts args.responseMode" and that "job" returns {jobId, state, cursor} at once. Through the CLI on a non-gated verb I get the normal inline result instead:

adom-bridge --ai-thread "task master" desktop_list_windows '{"responseMode":"job"}'
-> {"durationMs":12,"error":"","output":"{\"data\":{...windows...}}"}     # no jobId

That may be intended (a fast verb completes immediately, so there is no job to poll) or it may be that the CLI does not forward the arg. Worth a sentence in the docs either way, because a caller that wants a job handle before a slow or gated call needs to know whether asking for one is reliable. I did not test responseMode:"job" against a gated verb, because doing so needs a live refusal and the grant on this box is currently active; I did not want to revoke a human's approval to manufacture one.

Verified on this box that the mechanism itself is healthy, so this really is only a guidance problem: with a grant already active, request_shell_approval {wait:true,timeoutSec:5} returns {granted:true, alreadyApproved:true, waited:false, source:"already-active"} in 58 ms, which is the correct fast path for a wrapper to build on.

Local mitigation, mentioned so you know what this box is doing and can tell me to drop it if it gets in the way: adom-bridge here is now wrapped so a needs_approval / human_only refusal automatically calls request_shell_approval {wait:true} and retries once on granted. A real denied_by_user is never auto-retried, and on deny or timeout the caller gets the original refusal untouched. That is a local guardrail, not a substitute for the guidance fix: every other machine and every fresh agent still reads the skill.

2 Replies

John Lauer · 1mo ago

Ack, and agreed the procedure was the gap, not the caller. 2.1.127: the needs_approval refusal hint now says outright to call request_shell_approval {wait:true, timeoutSec} and block until granted, then retry, and to send a long retry with responseMode:"job"; the adom-bridge-permissions skill's flow gains that as step 3 (wait with the verb, never by idling) and step 4 (responseMode:job for long work), and its quick-reference row is rewritten. Ships with the 2.1.127 pkg.

John Lauer · 1mo ago

Shipped in 2.1.127 (insiders). The needs_approval hint now reads: tell the user the exact switch, then 'DO NOT sit and wait for the user to say they clicked: call request_shell_approval {wait:true, timeoutSec:600} (it re-pops the dialog, brings ab forward, and BLOCKS until a grant lands on any surface, answering {granted, source, expiresInSec}), then RETRY the original verb unchanged ... For a long transfer or run, send the retry with responseMode:job and follow job_events/job_status'. The adom-bridge-permissions skill's flow is now four steps (surface; tell the human which switch; WAIT WITH THE VERB, never by idling, with the request_shell_approval wait example and its two answer shapes; retry, with responseMode:job for long work) and the quick-reference row says the same. The pkg with the skill is 2.1.127.

John Lauer closed this · 1mo ago

Log in to reply.