You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
improvement(copilot): refuse approval-gated tools on the in-band lane (#8044)
* improvement(copilot): refuse approval-gated tools on the in-band lane
Copilot's approval gate is scaffolding today: COPILOT_TOOL_PERMISSIONS_ENABLED is off
by default, so nothing is gated on any lane. It is built only on the dispatch lane,
which holds a call against a streaming context and a decision row and then declines
to dispatch anything the mothership marks in-band. Those calls run via
POST /api/copilot/tools/execute, which has no context and no waiter, so turning the
flag on would gate the foreground and leave background lanes ungated — a gate that
looks enforced but is not.
Add toolRequiresApprovalLane next to toolCallNeedsApproval so the covered tool set is
defined once, and refuse a gated tool at the in-band route before it runs. Refuse
rather than block: a background lane must never hang on a prompt with no row behind
it. The check deliberately ignores the stored auto-allow list — an auto-allowed tool
sent to the checkpoint lane is admitted there without prompting anyone, so reading it
here would only add a database read to reach the same place.
Inert while the flag is off, which is the state this ships in; a test pins that.
Also record on the flag itself that the gate is a property of the lane, since that is
what the next person reads before enabling it.
* improvement(copilot): move the approval-lane predicate beside the tool router
Importing the dispatch gate module for a one-line predicate pulled the permission
persistence layer in with it, whose module body opens a pub/sub channel — two Redis
clients and a channel subscription — in every process that loads the in-band route.
Move toolRequiresApprovalLane to tool-executor/router.ts, which imports only the
catalog. The route already imported @/lib/copilot/tool-executor for
ensureHandlersRegistered, so the guard now costs no new import edge at all. The
dispatch gate keeps a pointer to it.
Its flag-and-catalog behavior is covered in the router tests against the real flag and
the real catalog; the route tests keep to what the route does with the answer.
error: `${toolName} was not run: it requires user approval, and this lane cannot hold an approval prompt. Dispatch it on the checkpoint lane instead.`,
0 commit comments