Add supporting context to AI tool confirmation dialogs (#4422)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Zeno Kapitein
2026-07-21 11:45:51 +02:00
committed by GitHub
parent 1eb763f9c5
commit bf674a47d9
5 changed files with 71 additions and 12 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"@gitbook/browser-types": patch
"@gitbook/embed": patch
"gitbook": patch
---
Add an optional `context` property (string, up to 512 characters) to the `confirmation` of custom AI tools, shown above the confirmation dialog to help the user understand what they are approving or rejecting. The `confirmation` can now also be a function that receives the AI-provided input and returns the confirmation, so the context can be derived dynamically from the arguments the tool is about to run with. Available both to integrations (`GitBookIntegrationTool`) and to embed consumers (`GitBookToolDefinition`).
+18 -4
View File
@@ -5,14 +5,28 @@ export type GitBookIntegrationEvent = 'load' | 'unload';
export type GitBookIntegrationEventCallback = (...args: any[]) => void;
export type GitBookIntegrationToolConfirmation = {
icon?: IconName;
label: string;
/**
* Supporting context displayed to the user above the confirmation dialog,
* to help them understand what they are approving or rejecting.
* Limited to 512 characters.
*/
context?: string;
};
export type GitBookIntegrationTool = AIToolDefinition & {
/**
* Confirmation action to be displayed to the user before executing the tool.
* Provide a static object, or a function that receives the input provided by
* the AI assistant and returns the confirmation — useful to display dynamic
* context based on the arguments the tool is about to be executed with.
*/
confirmation?: {
icon?: IconName;
label: string;
};
confirmation?:
| GitBookIntegrationToolConfirmation
| ((input: object) => GitBookIntegrationToolConfirmation);
/**
* Callback when the tool is executed.
+19 -4
View File
@@ -1,17 +1,32 @@
import type { AIToolCallResult, AIToolDefinition } from '@gitbook/api';
import type { IconName } from '@gitbook/icons';
/**
* Confirmation action to be displayed to the user before executing a tool.
*/
export type GitBookToolConfirmation = {
icon?: IconName;
label: string;
/**
* Supporting context displayed to the user above the confirmation dialog,
* to help them understand what they are approving or rejecting.
* Limited to 512 characters.
*/
context?: string;
};
/**
* Custom tool definition to be passed to the AI assistant.
*/
export type GitBookToolDefinition = AIToolDefinition & {
/**
* Confirmation action to be displayed to the user before executing the tool.
* Provide a static object, or a function that receives the input provided by
* the AI assistant and returns the confirmation — useful to display dynamic
* context based on the arguments the tool is about to be executed with.
*/
confirmation?: {
icon?: IconName;
label: string;
};
confirmation?: GitBookToolConfirmation | ((input: object) => GitBookToolConfirmation);
/**
* Callback when the tool is executed.
@@ -16,6 +16,13 @@ export const ConfirmControlDef = createAIControl({
description:
'Display a confirmation prompt to the user (Confirm / Cancel) to approve or abort a pending action. Use this when an operation is irreversible, sensitive, or should only proceed with explicit user consent. Returns either a `confirmed` or `cancelled` result based on the users click.',
inputSchema: z.object({
context: z
.string()
.max(512)
.optional()
.describe(
'Supporting context shown above the prompt to help the user understand what they are approving or rejecting.'
),
icon: z
.string()
.optional()
@@ -29,10 +36,13 @@ export const ConfirmControlDef = createAIControl({
});
function ConfirmControl(props: GetAIControlProps<typeof ConfirmControlDef>) {
const { label, icon, onSubmit } = props;
const { label, icon, context, onSubmit } = props;
const language = useLanguage();
return (
<AIToolContainer className="flex w-full flex-col gap-2">
{context ? (
<p className="whitespace-pre-line px-2 pt-1 text-sm text-tint">{context}</p>
) : null}
<Button
data-testid="ai-chat-tool-confirm-cancel"
onClick={() => {
@@ -434,6 +434,18 @@ export function AIChatProvider(props: {
const confirmation = 'confirmation' in toolDef && toolDef.confirmation;
if (confirmation) {
// The confirmation can be a static object or a function that
// derives it from the AI-provided input (e.g. dynamic context).
// The function call is awaited because, for embed-registered
// tools, it arrives as an async proxy over the postMessage channel.
const resolvedConfirmation =
typeof confirmation === 'function'
? await confirmation(event.toolCall.input)
: confirmation;
const supportingContext =
typeof resolvedConfirmation.context === 'string'
? resolvedConfirmation.context.slice(0, 512)
: undefined;
globalState.setState((state) => ({
...state,
control: ConfirmControlDef.createControl({
@@ -442,8 +454,9 @@ export function AIChatProvider(props: {
toolCallId: event.toolCallId,
},
input: {
label: confirmation.label,
icon: confirmation.icon,
label: resolvedConfirmation.label,
icon: resolvedConfirmation.icon,
context: supportingContext,
},
language,
send: async (result) => {
@@ -462,7 +475,7 @@ export function AIChatProvider(props: {
text: tString(
language,
'tool_call_skipped',
confirmation.label
resolvedConfirmation.label
),
},
},