From 288be53c248997f0cfe585f91b86315119165fb1 Mon Sep 17 00:00:00 2001 From: kevin9327 <5299031+kevin9327@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:28:17 +0900 Subject: [PATCH] Read a sandboxed tool's refusal under the id its renderer is given --- CHANGELOG.md | 10 ++++++++++ app/src/lib/copilot/sandboxed-tools.tsx | 13 ++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c658b496..c92630605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged. ## Unreleased +### A component the server refused is no longer drawn anyway + +A sandboxed component asks the server at call time whether the Bot may still use it, and a refusal +is recorded so the drawing can be replaced with a card saying so. The renderer looked that refusal +up under `props.toolCall.id`, which is the shape a tool HANDLER is given; a renderer's props carry +the id flat, as `toolCallId`. The lookup key was therefore always undefined, the refusal was never +found, and the component rendered as though it had been allowed -- so revoking a component from a +Bot did not take effect on screen until the five-second grant poll caught up, and a failed decision +request showed nothing at all. + ### The server connects to Postgres on Windows, and `localhost` is no longer a coin toss Two separate faults, both of which stop a deployment reaching its own database and neither of which diff --git a/app/src/lib/copilot/sandboxed-tools.tsx b/app/src/lib/copilot/sandboxed-tools.tsx index f69aa43fa..759d7a6e3 100644 --- a/app/src/lib/copilot/sandboxed-tools.tsx +++ b/app/src/lib/copilot/sandboxed-tools.tsx @@ -75,13 +75,20 @@ function SandboxedTool({ const isHeld = description !== undefined; const render = useCallback( + // `toolCallId`, not `toolCall.id`: a renderer's props carry the id flat, + // which is the shape gallery-tools and computer-tools already read and + // the one the library's own DefaultToolCallRenderer destructures. The + // handler below is the other shape, `context.toolCall.id`, and reading + // that one here meant the key was always undefined -- so a refusal the + // server had just recorded was never found, and the component the server + // refused was drawn instead of the card saying it was not allowed. (props: { args?: Record; status?: string; - toolCall?: { id?: string }; + toolCallId?: string; }) => { - const refusal = props.toolCall?.id - ? refusals.get(props.toolCall.id) + const refusal = props.toolCallId + ? refusals.get(props.toolCallId) : undefined; if (refusal) { return ;