From 5ab866015bd5cd2f9dcb48afc53c9c2b81ee019a Mon Sep 17 00:00:00 2001 From: David McKay Date: Fri, 21 Aug 2026 21:22:58 -0700 Subject: [PATCH] Report a missing grant as a missing grant Asked what was in a PRD it had just found in Drive, a Bot opened docs.google.com in its own container, met Google's sign-in page and asked the person to take the wheel and sign in. Only search_files was granted. read_file_content was not. The Bot could find the document and had no way to read it, and rather than say so it fell back to the browser, so a gap in what it holds surfaced as an authentication problem somewhere else entirely. The person already had access. What was missing was the Bot's, not theirs, and nothing on screen said that. The guidance a Bot gets about its grants already told it not to browse to a vendor it holds tools for. It now also says what to do when it holds some of a vendor's tools and not the one it needs: say so plainly, name the capability, and say an administrator can grant it on that connector. Not the browser, not a sign-in request, and not asking the person to fetch it, which is the same mistake wearing a hat. Closes #141. --- server/src/plugins/tools.ts | 8 ++++++-- server/tests/copilot.test.ts | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/server/src/plugins/tools.ts b/server/src/plugins/tools.ts index 35aa4753..9036b0e3 100644 --- a/server/src/plugins/tools.ts +++ b/server/src/plugins/tools.ts @@ -85,8 +85,12 @@ export function grantedToolGuidance(tools: GrantedTool[]): string { ), "Use them for anything about those systems. Do NOT browse to one of their websites instead: your", "browser is signed in as nobody, so it sees less than these tools do and will meet a sign-in wall", - "that connecting an account has already solved. If one of these systems is involved but no tool", - "above covers the part you need, say which part is missing rather than going around it.", + "that connecting an account has already solved.", + "If one of these systems is involved and no tool above covers the part you need, that is a", + "missing grant and not something to work around. Say so plainly, name the capability you would", + "need, and say an administrator can grant it on that connector. Do not reach for the browser, do", + "not ask the person to sign in, and do not ask them to fetch it for you: they already have the", + "access, and the thing that is missing is yours, not theirs.", ].join("\n"); } diff --git a/server/tests/copilot.test.ts b/server/tests/copilot.test.ts index ee623640..87791b21 100644 --- a/server/tests/copilot.test.ts +++ b/server/tests/copilot.test.ts @@ -537,6 +537,29 @@ describe("what a Bot is told it holds", () => { expect(grantedToolGuidance(drive).toLowerCase()).toContain("do not browse"); }); + test("says a gap in what it holds is a grant to ask for, not a wall to climb", () => { + /* + * The half of the Drive failure the "do not browse" line does not cover. + * + * Only `search_files` was granted. The Bot found the document, had no way to read it, and + * surfaced that as an authentication problem on `docs.google.com`: it opened the vendor, met + * Google's sign-in page and asked its person to take the wheel and sign in. They already had + * access. The Bot lacked a grant, and nothing on screen said so. + * + * A sentence naming the missing capability points at the screen that fixes it. A sign-in box + * does not, and asking the person to fetch it instead is the same mistake wearing a hat. + */ + // Whitespace-normalised: the guidance is assembled line by line, so a sentence spans a newline + // wherever the source happened to wrap, which is not a fact about what the Bot is told. + const guidance = grantedToolGuidance(drive) + .toLowerCase() + .replace(/\s+/g, " "); + expect(guidance).toContain("missing grant"); + expect(guidance).toContain("name the capability"); + expect(guidance).toContain("administrator can grant it"); + expect(guidance).toContain("do not ask the person to sign in"); + }); + test("says nothing at all when the Bot holds nothing", () => { // A deployment with no connectors must not be told about connectors it does not have. expect(grantedToolGuidance([])).toBe("");