From 9428abdef8ef0469c9dd7ede0a5eb8900c097737 Mon Sep 17 00:00:00 2001 From: Ben White Date: Thu, 25 Jun 2026 20:10:01 +0200 Subject: [PATCH] feat(agent-applications): send supported_client_tools at /run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Producer counterpart to the agent-platform server change that gates client-tool exposure on a per-run capability list. The agent-builder dock now declares the `kind:'client'` tool ids it can fulfil and sends them in the /run body as `supported_client_tools`, so the runner exposes only those to the model (spec ∩ supported). - `runAgentSession` (api-client): optional `supportedClientTools`, added to the /run body when non-empty. - `AgentChatSession` (core): carries `supportedClientTools`; `agentChatService` forwards it to `runAgentSession`. - `useAgentChat` (ui): new `supportedClientTools` option threaded into the session config. - `AGENT_BUILDER_CLIENT_TOOLS` declares the dock's fulfillable ids (set_secret, focus_*, toast, get_context) — verified to exactly match the agent-builder spec's `kind:'client'` tools — and is passed from the dock. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/api-client/src/posthog-client.ts | 9 ++++++++- packages/core/src/agent-chat/agentChatService.ts | 1 + packages/core/src/agent-chat/identifiers.ts | 2 ++ .../agent-builder/AgentBuilderDock.tsx | 6 +++++- .../agent-builder/useAgentBuilderClientTools.ts | 16 ++++++++++++++++ .../agent-applications/hooks/useAgentChat.ts | 14 +++++++++++++- 6 files changed, 45 insertions(+), 3 deletions(-) diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts index eed207fd5..ef4db40c1 100644 --- a/packages/api-client/src/posthog-client.ts +++ b/packages/api-client/src/posthog-client.ts @@ -4964,14 +4964,21 @@ export class PostHogAPIClient { ingressBaseUrl: string, message: string, previewToken?: string | null, + supportedClientTools?: readonly string[], ): Promise<{ session_id: string; resumed?: boolean }> { const url = new URL(`${ingressBaseUrl.replace(/\/$/, "")}/run`); + // `supported_client_tools`: the kind:'client' tool ids this client can + // execute this session, so the runner exposes only those to the model. + const body: Record = { message }; + if (supportedClientTools && supportedClientTools.length > 0) { + body.supported_client_tools = supportedClientTools; + } const response = await this.api.fetcher.fetch({ method: "post", url, path: url.pathname, parameters: previewTokenHeader(previewToken), - overrides: { body: JSON.stringify({ message }) }, + overrides: { body: JSON.stringify(body) }, }); return (await response.json()) as { session_id: string; resumed?: boolean }; } diff --git a/packages/core/src/agent-chat/agentChatService.ts b/packages/core/src/agent-chat/agentChatService.ts index 8541084c1..c3a4f018b 100644 --- a/packages/core/src/agent-chat/agentChatService.ts +++ b/packages/core/src/agent-chat/agentChatService.ts @@ -431,6 +431,7 @@ export class AgentChatService { session.ingressBaseUrl, session.buildWireText(text), token, + session.supportedClientTools, ), ); agentChatStore.getState().setSessionId(session.chatId, session_id); diff --git a/packages/core/src/agent-chat/identifiers.ts b/packages/core/src/agent-chat/identifiers.ts index b12805f0b..9c37d9e7a 100644 --- a/packages/core/src/agent-chat/identifiers.ts +++ b/packages/core/src/agent-chat/identifiers.ts @@ -49,6 +49,8 @@ export interface AgentChatSession { ingressBaseUrl: string; /** Non-null targets a specific draft revision (preview token attached per call). */ revisionId: string | null; + /** `kind:'client'` tool ids this client can fulfil; sent to the runner at /run. */ + supportedClientTools?: readonly string[]; createMapper(): AgentChatMapper; /** Resolve a client-tool call; `defer`/null ⇒ the service won't post a result. */ resolveClientTool( diff --git a/packages/ui/src/features/agent-applications/agent-builder/AgentBuilderDock.tsx b/packages/ui/src/features/agent-applications/agent-builder/AgentBuilderDock.tsx index 8cda60b52..9e2a96dd6 100644 --- a/packages/ui/src/features/agent-applications/agent-builder/AgentBuilderDock.tsx +++ b/packages/ui/src/features/agent-applications/agent-builder/AgentBuilderDock.tsx @@ -25,7 +25,10 @@ import { useAgentBuilderStore, } from "./agentBuilderStore"; import { suggestionsForPage } from "./agentBuilderSuggestions"; -import { useAgentBuilderClientTools } from "./useAgentBuilderClientTools"; +import { + AGENT_BUILDER_CLIENT_TOOLS, + useAgentBuilderClientTools, +} from "./useAgentBuilderClientTools"; const CHAT_ID = AGENT_BUILDER_CHAT_ID; @@ -118,6 +121,7 @@ export function AgentBuilderDock() { orgId: currentOrgId, }), clientTools, + supportedClientTools: AGENT_BUILDER_CLIENT_TOOLS, }); const pendingApproval = useAgentChatPendingApproval(CHAT_ID); diff --git a/packages/ui/src/features/agent-applications/agent-builder/useAgentBuilderClientTools.ts b/packages/ui/src/features/agent-applications/agent-builder/useAgentBuilderClientTools.ts index aa90a64dd..e25348f58 100644 --- a/packages/ui/src/features/agent-applications/agent-builder/useAgentBuilderClientTools.ts +++ b/packages/ui/src/features/agent-applications/agent-builder/useAgentBuilderClientTools.ts @@ -3,6 +3,22 @@ import { useCallback, useRef } from "react"; import type { ClientToolHandler } from "../hooks/useAgentChat"; import { useAgentBuilderStore } from "./agentBuilderStore"; +/** + * The `kind:'client'` tool ids the agent-builder dock can fulfil — sent to the + * runner as `supported_client_tools` at /run so it exposes only these to the + * model. Keep in sync with the handler below plus the built-in toast/get_context. + */ +export const AGENT_BUILDER_CLIENT_TOOLS = [ + "set_secret", + "focus_tab", + "focus_file", + "focus_spec_section", + "focus_revision", + "focus_session", + "toast", + "get_context", +] as const; + /** * The agent builder's UI-driving client tools. The agent calls these to steer the * user's screen (`focus_*`, which navigate code's agent routes and report back diff --git a/packages/ui/src/features/agent-applications/hooks/useAgentChat.ts b/packages/ui/src/features/agent-applications/hooks/useAgentChat.ts index 26d9537e0..659a8aead 100644 --- a/packages/ui/src/features/agent-applications/hooks/useAgentChat.ts +++ b/packages/ui/src/features/agent-applications/hooks/useAgentChat.ts @@ -50,6 +50,8 @@ export interface UseAgentChatOptions { contextProvider?: () => unknown; /** AgentBuilder UI-driving tools (focus_*, set_secret); null → built-in handling. */ clientTools?: ClientToolHandler; + /** `kind:'client'` tool ids this client can fulfil; sent to the runner at /run. */ + supportedClientTools?: readonly string[]; } /** @@ -68,6 +70,7 @@ export function useAgentChat({ recordHistory = false, contextProvider, clientTools, + supportedClientTools, }: UseAgentChatOptions) { const client = useAuthenticatedClient(); const service = useService(AGENT_CHAT_SERVICE); @@ -88,6 +91,7 @@ export function useAgentChat({ agentSlug, ingressBaseUrl: ingressBaseUrl ?? "", revisionId, + supportedClientTools, createMapper: createAgentChatMapper, resolveClientTool: (data) => resolveClientTool( @@ -113,7 +117,15 @@ export function useAgentChat({ }) : undefined, }), - [chatId, agentSlug, ingressBaseUrl, revisionId, recordHistory, recordChat], + [ + chatId, + agentSlug, + ingressBaseUrl, + revisionId, + supportedClientTools, + recordHistory, + recordChat, + ], ); const send = useCallback(