Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ 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.
### A component whose name has a stray space is the same component

The catalogue announcement asked whether each component's `name`, `title`, `kind` and `description`
Expand Down
13 changes: 10 additions & 3 deletions app/src/lib/copilot/sandboxed-tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>;
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 <RefusedCard reason={refusal} title={component.name} />;
Expand Down