@mcp-b/react-components 0.20.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -140,6 +140,8 @@ interface AgentChatProps<MessageT extends UIMessage = UIMessage> {
140
140
  onApprovalResponse?: ChatAddToolApproveResponseFunction;
141
141
  /** Whole upstream Think stub used to resolve paused and durable execution approvals. */
142
142
  thinkStub?: AgentStub<Think>;
143
+ /** Disable approval scanning, automatic responses, cards, and composer controls. */
144
+ approvalsEnabled?: boolean;
143
145
  /** Parent-owned approval policy. Omit to keep AgentChat's session-local mode. */
144
146
  permissionModeControl?: PermissionModeControl;
145
147
  /** Called after a Think approve/reject RPC settles, including when it rejects. */
@@ -216,6 +218,7 @@ declare function AgentChat<MessageT extends UIMessage = UIMessage>({
216
218
  onEditAndResend,
217
219
  onApprovalResponse,
218
220
  thinkStub,
221
+ approvalsEnabled,
219
222
  permissionModeControl,
220
223
  onExecutionSettled,
221
224
  pendingApprovals: durableApprovals,
@@ -45,9 +45,12 @@ function thinkPendingExecutionApprovalsQueryOptions(stub, sourceKey) {
45
45
  staleTime: 5e3
46
46
  });
47
47
  }
48
- function useThinkPendingExecutionApprovals(stub, sourceKey, refreshSignal) {
48
+ function useThinkPendingExecutionApprovals(stub, sourceKey, refreshSignal, enabled = true) {
49
49
  const queryClient = useQueryClient();
50
- const approvalsQuery = useQuery(thinkPendingExecutionApprovalsQueryOptions(stub, sourceKey));
50
+ const approvalsQuery = useQuery({
51
+ ...thinkPendingExecutionApprovalsQueryOptions(stub, sourceKey),
52
+ enabled
53
+ });
51
54
  const refreshState = React.useRef({
52
55
  sourceKey,
53
56
  observedSignal: refreshSignal,
@@ -55,15 +58,20 @@ function useThinkPendingExecutionApprovals(stub, sourceKey, refreshSignal) {
55
58
  });
56
59
  const refetch = React.useEffectEvent(() => approvalsQuery.refetch());
57
60
  const refreshPendingApprovals = React.useCallback(async () => {
61
+ if (!enabled) return;
58
62
  const filters = {
59
63
  queryKey: thinkPendingExecutionApprovalsQueryKey(sourceKey),
60
64
  exact: true
61
65
  };
62
66
  await queryClient.cancelQueries(filters);
63
67
  await queryClient.invalidateQueries(filters);
64
- }, [queryClient, sourceKey]);
68
+ }, [
69
+ enabled,
70
+ queryClient,
71
+ sourceKey
72
+ ]);
65
73
  React.useEffect(() => {
66
- if (refreshState.current.sourceKey !== sourceKey) {
74
+ if (!enabled || refreshState.current.sourceKey !== sourceKey) {
67
75
  refreshState.current = {
68
76
  sourceKey,
69
77
  observedSignal: refreshSignal,
@@ -80,6 +88,7 @@ function useThinkPendingExecutionApprovals(stub, sourceKey, refreshSignal) {
80
88
  refetch();
81
89
  }, [
82
90
  approvalsQuery.isFetching,
91
+ enabled,
83
92
  refreshSignal,
84
93
  sourceKey
85
94
  ]);
@@ -122,7 +131,7 @@ function ThinkChatProvider({ agentTools, chat, children }) {
122
131
  * </QueryClientProvider>
123
132
  * ```
124
133
  */
125
- function ThinkChatRoot({ agent: agentOption, name, sourceKey, children }) {
134
+ function ThinkChatRoot({ agent: agentOption, approvalsEnabled = true, name, sourceKey, children }) {
126
135
  const agent = useAgent(typeof agentOption === "string" ? {
127
136
  agent: agentOption,
128
137
  name
@@ -133,7 +142,7 @@ function ThinkChatRoot({ agent: agentOption, name, sourceKey, children }) {
133
142
  const chat = useAgentChat({ agent });
134
143
  const agentTools = useAgentToolEvents({ agent });
135
144
  const agentIdentity = JSON.stringify(agent.path);
136
- const approvals = useThinkPendingExecutionApprovals(agent.stub, sourceKey, `${chat.messages.length}:${chat.status}`);
145
+ const approvals = useThinkPendingExecutionApprovals(agent.stub, sourceKey, `${chat.messages.length}:${chat.status}`, approvalsEnabled);
137
146
  const resetAgentTools = React.useEffectEvent(() => agentTools.resetLocalState());
138
147
  React.useEffect(() => {
139
148
  resetAgentTools();
@@ -1,5 +1,5 @@
1
1
  import { i as AgentMessageRenderPartContext } from "./AgentMessageParts-CyD2UB3V.js";
2
- import { r as AgentChatProps } from "./AgentChat-CaQaT0LF.js";
2
+ import { r as AgentChatProps } from "./AgentChat-C-ZyXbJz.js";
3
3
  import { l as ThinkChatPart } from "./ThinkChatToolParts-CSzVwMXf.js";
4
4
  import * as React from "react";
5
5
  import { DynamicToolUIPart, ToolUIPart, UIMessage } from "ai";
@@ -1,2 +1,2 @@
1
- import { a as PermissionModeControl, i as AgentChatRenderMessageContext, n as AgentChatComposerDraft, r as AgentChatProps, t as AgentChat } from "../AgentChat-CaQaT0LF.js";
1
+ import { a as PermissionModeControl, i as AgentChatRenderMessageContext, n as AgentChatComposerDraft, r as AgentChatProps, t as AgentChat } from "../AgentChat-C-ZyXbJz.js";
2
2
  export { AgentChat, AgentChatComposerDraft, AgentChatProps, AgentChatRenderMessageContext, PermissionModeControl };
@@ -108,7 +108,7 @@ function getEditableUserText(message) {
108
108
  * transport. Product code that talks to a Cloudflare Think agent should render
109
109
  * `ThinkChat`; use this directly only for plain AI SDK or read-only messages.
110
110
  */
111
- function AgentChat({ className, messages, status = "ready", onSubmit, showComposer = onSubmit !== void 0, onStop, placeholder = "Ask anything...", disabled, emptyState, composerHeader, composerTools, slashCommands, mentionCommands, contextSelector, composerEndTools, voiceInput, beforeVoiceStart, composerAddMenuItems, notice, onRegenerate, onEditAndResend, onApprovalResponse, thinkStub, permissionModeControl, onExecutionSettled, pendingApprovals: durableApprovals = EMPTY_PENDING_APPROVALS, contextWindow, renderPart, renderMessageAfter, ref }) {
111
+ function AgentChat({ className, messages, status = "ready", onSubmit, showComposer = onSubmit !== void 0, onStop, placeholder = "Ask anything...", disabled, emptyState, composerHeader, composerTools, slashCommands, mentionCommands, contextSelector, composerEndTools, voiceInput, beforeVoiceStart, composerAddMenuItems, notice, onRegenerate, onEditAndResend, onApprovalResponse, thinkStub, approvalsEnabled = true, permissionModeControl, onExecutionSettled, pendingApprovals: durableApprovals = EMPTY_PENDING_APPROVALS, contextWindow, renderPart, renderMessageAfter, ref }) {
112
112
  const [copyAnnouncement, setCopyAnnouncement] = React.useState("");
113
113
  const [composerDraft, setComposerDraft] = React.useState("");
114
114
  const [composerReferences, setComposerReferences] = React.useState([]);
@@ -214,19 +214,21 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
214
214
  }, [thinkStub]);
215
215
  const skippedApprovalIdSet = React.useMemo(() => new Set(skippedApprovalIds), [skippedApprovalIds]);
216
216
  const pendingApprovalItems = React.useMemo(() => {
217
+ if (!approvalsEnabled) return [];
217
218
  return [...getPendingToolApprovals(messages, { pausedExecutions: Boolean(thinkStub) }).filter((part) => {
218
219
  if (part.state === "approval-requested" && !onApprovalResponse) return false;
219
220
  const id = getApprovalStateKey(part);
220
221
  return Boolean(id) && !skippedApprovalIdSet.has(id);
221
222
  }).map((part) => ({ part })), ...thinkStub ? durableApprovals.filter((approval) => approval.source === "action" && !skippedApprovalIdSet.has(approval.executionId)).map((approval) => ({ approval })) : []];
222
223
  }, [
224
+ approvalsEnabled,
223
225
  durableApprovals,
224
226
  messages,
225
227
  onApprovalResponse,
226
228
  skippedApprovalIdSet,
227
229
  thinkStub
228
230
  ]);
229
- const approvalsWired = Boolean(onApprovalResponse || thinkStub);
231
+ const approvalsWired = approvalsEnabled && Boolean(onApprovalResponse || thinkStub);
230
232
  const autoApprovedIds = React.useRef({
231
233
  thinkStub,
232
234
  ids: /* @__PURE__ */ new Set()
@@ -5,7 +5,7 @@ import { AppTopBar } from "./AppTopBar.js";
5
5
  import { NaniteMark } from "./NaniteScene.js";
6
6
  import { SubagentRunIndicator, getSubagentRunName } from "./SubagentRunIndicator.js";
7
7
  import { SubagentRunMilestone, SubagentRunMilestones, SubagentRunProgress } from "./SubagentRunDetails.js";
8
- import { t as ThinkChat } from "../ThinkChat-15EhfvYJ.js";
8
+ import { t as ThinkChat } from "../ThinkChat-E_HrfH76.js";
9
9
  import * as React from "react";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
11
  import { useAgentChat } from "@cloudflare/think/react";
@@ -1,9 +1,9 @@
1
1
  import { i as AgentMessageRenderPartContext } from "../AgentMessageParts-CyD2UB3V.js";
2
- import { r as AgentChatProps } from "../AgentChat-CaQaT0LF.js";
2
+ import { r as AgentChatProps } from "../AgentChat-C-ZyXbJz.js";
3
3
  import { i as useOptionalAgentToolRuns, n as AgentToolRunsProvider, r as AgentToolRunsProviderProps, t as AgentToolEventsValue } from "../AgentToolRunsContext-BVe4stgw.js";
4
4
  import { S as isThinkChatActivityPart } from "../ThinkChatToolParts-CSzVwMXf.js";
5
5
  import { l as describeThinkChatActivityPart } from "../ThinkChatActivitySummary-D1xOrNrq.js";
6
- import { a as ThinkChatToolRenderers, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as renderThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-CnIJPvV5.js";
6
+ import { a as ThinkChatToolRenderers, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as renderThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-CdbL0aoA.js";
7
7
  import { n as ThinkChatValue } from "../ThinkChatContext-DrsZqdaO.js";
8
8
  import { n as ThinkRecoveryNoticeProps, t as ThinkRecoveryNotice } from "../ThinkRecoveryNotice-DFIcG9UX.js";
9
9
  import { n as BrowserLiveViewProps, t as BrowserLiveView } from "../BrowserLiveView-BV5qgFgI.js";
@@ -34,6 +34,8 @@ interface ThinkChatProviderProps<State = unknown, MessageT extends UIMessage = U
34
34
  interface ThinkChatRootProps<State = unknown> {
35
35
  /** Agent to connect to: a kebab-case class name, or full `useAgent` options. */
36
36
  agent: string | UseAgentOptions<State>;
37
+ /** Disable durable pending-approval discovery. Also disable it on `ThinkChat.Messages`. */
38
+ approvalsEnabled?: boolean;
37
39
  /** Instance name (e.g. a session or room id). Wins over `agent.name`. */
38
40
  name?: string;
39
41
  /**
@@ -91,6 +93,12 @@ interface ThinkChatMessagesProps<MessageT extends UIMessage = UIMessage> extends
91
93
  renderPart?: AgentChatProps<MessageT>["renderPart"];
92
94
  /** Whole upstream Think stub. `ThinkChat.Root` wires `agent.stub` automatically. */
93
95
  thinkStub?: AgentStub<Think>;
96
+ /**
97
+ * Disable transcript approval scanning, automatic responses, cards, and
98
+ * composer controls. Set the matching prop on `ThinkChat.Root` to also skip
99
+ * durable pending-approval discovery; `ThinkFullApp` wires both from here.
100
+ */
101
+ approvalsEnabled?: AgentChatProps<MessageT>["approvalsEnabled"];
94
102
  /** Parent-owned Ask/Bypass policy; omit to keep AgentChat's session-local mode. */
95
103
  permissionModeControl?: AgentChatProps<MessageT>["permissionModeControl"];
96
104
  /** Called after a Think approve/reject RPC settles. Root uses this to refresh approvals. */
@@ -106,7 +114,7 @@ interface ThinkChatMessagesProps<MessageT extends UIMessage = UIMessage> extends
106
114
  }
107
115
  /** Shared pending-approval options for components and TanStack Router loaders. */
108
116
  declare function thinkPendingExecutionApprovalsQueryOptions(stub: AgentStub<Think>, sourceKey: string): ThinkPendingExecutionApprovalsQueryOptions;
109
- declare function useThinkPendingExecutionApprovals(stub: AgentStub<Think>, sourceKey: string, refreshSignal?: unknown): {
117
+ declare function useThinkPendingExecutionApprovals(stub: AgentStub<Think>, sourceKey: string, refreshSignal?: unknown, enabled?: boolean): {
110
118
  pendingApprovals: readonly PendingApproval[];
111
119
  refreshPendingApprovals: () => Promise<void>;
112
120
  };
@@ -141,6 +149,7 @@ declare function ThinkChatProvider<State = unknown, MessageT extends UIMessage =
141
149
  */
142
150
  declare function ThinkChatRoot<State = unknown, MessageT extends UIMessage = UIMessage>({
143
151
  agent: agentOption,
152
+ approvalsEnabled,
144
153
  name,
145
154
  sourceKey,
146
155
  children
@@ -5,7 +5,7 @@ import { inferWorkspaceLanguage } from "./WorkspaceFile.js";
5
5
  import { describeThinkChatActivityPart } from "./ThinkChatActivitySummary.js";
6
6
  import { ThinkChatActivity, createThinkChatActivityRenderer, renderThinkChatActivityPart } from "./ThinkChatActivity.js";
7
7
  import { ThinkRecoveryNotice } from "./ThinkRecoveryNotice.js";
8
- import { a as thinkPendingExecutionApprovalsQueryOptions, i as ThinkChatRoot, n as ThinkChatMessages, o as useThinkChat, r as ThinkChatProvider, s as useThinkPendingExecutionApprovals, t as ThinkChat } from "../ThinkChat-15EhfvYJ.js";
8
+ import { a as thinkPendingExecutionApprovalsQueryOptions, i as ThinkChatRoot, n as ThinkChatMessages, o as useThinkChat, r as ThinkChatProvider, s as useThinkPendingExecutionApprovals, t as ThinkChat } from "../ThinkChat-E_HrfH76.js";
9
9
  import { BrowserReplay } from "./BrowserReplay.js";
10
10
  import { useWorkspace, workspaceBinaryFileQueryOptions, workspaceDirectoryQueryOptions, workspaceFileAssetQueryOptions, workspaceFileStatQueryOptions, workspaceInfoQueryOptions, workspaceQueryKey, workspaceTextFileQueryOptions } from "./Workspace.js";
11
11
  import { WorkspaceExplorer } from "./WorkspaceExplorer.js";
@@ -1,4 +1,4 @@
1
1
  import { S as isThinkChatActivityPart } from "../ThinkChatToolParts-CSzVwMXf.js";
2
2
  import { l as describeThinkChatActivityPart } from "../ThinkChatActivitySummary-D1xOrNrq.js";
3
- import { a as ThinkChatToolRenderers, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as renderThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-CnIJPvV5.js";
3
+ import { a as ThinkChatToolRenderers, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as renderThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-CdbL0aoA.js";
4
4
  export { ThinkChatActivity, ThinkChatActivityProps, ThinkChatActivityRendererOptions, ThinkChatToolRenderer, ThinkChatToolRenderers, createThinkChatActivityRenderer, describeThinkChatActivityPart, isThinkChatActivityPart, renderThinkChatActivityPart };
@@ -240,7 +240,7 @@ function describeBrowserToolPart(part) {
240
240
  if (part.type === "tool-browser_execute") {
241
241
  const output = part.state === "output-available" ? part.output : void 0;
242
242
  if (failed || output?.status === "error") return "Browser automation failed";
243
- if (output?.status === "paused") return "Browser waiting for approval";
243
+ if (output?.status === "paused") return isHumanWaitPause(output) ? "Browser waiting for human" : "Browser waiting for approval";
244
244
  return working ? "Using the browser" : "Used the browser";
245
245
  }
246
246
  const page = displayUrl(part.input?.url) || "a page";
@@ -257,11 +257,15 @@ function describeBrowserToolPart(part) {
257
257
  function describeExecuteToolPart(part) {
258
258
  const output = part.state === "output-available" ? part.output : void 0;
259
259
  if (isPartFailed(part) || output?.status === "error") return "Code failed";
260
- if (output?.status === "paused") return "Code waiting for approval";
260
+ if (output?.status === "paused") return isHumanWaitPause(output) ? "Code waiting for human" : "Code waiting for approval";
261
261
  const label = isPartWorking(part) ? "Running code" : "Ran code";
262
262
  const snippet = summarizeToolInput(part.input);
263
263
  return snippet ? `${label} · ${snippet}` : label;
264
264
  }
265
+ function isHumanWaitPause(output) {
266
+ const pending = output.pending ?? [];
267
+ return pending.length > 0 && pending.every((action) => action.connector === "human" && action.method === "waitFor");
268
+ }
265
269
  function describeFetchToolPart(part) {
266
270
  const target = part.input?.url ?? part.input?.path ?? "";
267
271
  const output = part.state === "output-available" ? part.output : void 0;
@@ -329,20 +329,25 @@ function formatAuditValue(value) {
329
329
  const maxLength = 240;
330
330
  return formatted.length > maxLength ? `${formatted.slice(0, maxLength - 1)}…` : formatted;
331
331
  }
332
+ function isHumanWait(action) {
333
+ return action.connector === "human" && action.method === "waitFor";
334
+ }
332
335
  function toolCallStateLabel(call) {
333
- const state = call.state === "pending" ? "awaiting approval" : call.state === "applied" ? "applied" : call.state;
334
- const qualifiers = [call.requiresApproval && call.state !== "pending" ? "approval required" : "", call.ephemeral ? "re-executes on replay" : ""].filter(Boolean);
336
+ const humanWait = isHumanWait(call);
337
+ const state = call.state === "pending" ? humanWait ? "waiting for human" : "awaiting approval" : call.state === "applied" ? "applied" : call.state;
338
+ const qualifiers = [call.requiresApproval && call.state !== "pending" && !humanWait ? "approval required" : "", call.ephemeral ? "re-executes on replay" : ""].filter(Boolean);
335
339
  return qualifiers.length ? `${state} · ${qualifiers.join(" · ")}` : state;
336
340
  }
337
341
  function formatPendingActions(pending) {
338
342
  return pending.map((action) => {
339
343
  const args = action.args === void 0 ? "" : JSON.stringify(action.args);
340
- return `awaiting approval: ${action.connector}.${action.method}(${args})`;
344
+ return `${isHumanWait(action) ? "waiting for a human step" : "awaiting approval"}: ${action.connector}.${action.method}(${args})`;
341
345
  }).join("\n");
342
346
  }
343
347
  function executeStatusText(output) {
344
348
  if (!output) return void 0;
345
- return output.status === "paused" ? "paused · needs approval" : output.status;
349
+ if (output.status !== "paused") return output.status;
350
+ return output.pending.length > 0 && output.pending.every(isHumanWait) ? "paused · waiting for human" : "paused · needs approval";
346
351
  }
347
352
  /** The codemode execute tool as an activity panel: highlighted TypeScript
348
353
  plus logs, result, and the paused pending-approval list. */
@@ -3,7 +3,7 @@ import { S as FileIcon, X as TerminalIcon, d as ChevronDownIcon } from "../icons
3
3
  import { Button } from "./Button.js";
4
4
  import { a as WorkspaceNavigationRestorationBoundary, i as WorkspaceNavigationFocusRestorer, n as WorkspaceNavigationProvider } from "../WorkspaceLink-BYYeTQLf.js";
5
5
  import { AppTopBar } from "./AppTopBar.js";
6
- import { c as resolveThinkApprovalProps, s as useThinkPendingExecutionApprovals, t as ThinkChat } from "../ThinkChat-15EhfvYJ.js";
6
+ import { c as resolveThinkApprovalProps, s as useThinkPendingExecutionApprovals, t as ThinkChat } from "../ThinkChat-E_HrfH76.js";
7
7
  import { t as mergeRefs } from "../merge-refs-KlanQB7A.js";
8
8
  import { useWorkspace } from "./Workspace.js";
9
9
  import { WorkspaceExplorer } from "./WorkspaceExplorer.js";
@@ -113,7 +113,7 @@ function ThinkFullApp({ agent, appBarActions, appBarLeading, appBarTitle, chat,
113
113
  const appRef = React.useRef(null);
114
114
  const viewScopeRef = React.useRef(null);
115
115
  const workbenchPanelRef = React.useRef(null);
116
- const approvals = useThinkPendingExecutionApprovals(agent.stub, sourceKey, `${chat.messages.length}:${chat.status}`);
116
+ const approvals = useThinkPendingExecutionApprovals(agent.stub, sourceKey, `${chat.messages.length}:${chat.status}`, messages?.approvalsEnabled !== false);
117
117
  const resolvedMessages = React.useMemo(() => {
118
118
  const approvalProps = resolveThinkApprovalProps({
119
119
  thinkStub: messages?.thinkStub,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/react-components",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "MCP-B React components built on Base UI and shared design tokens, including Cloudflare Think chat primitives.",
5
5
  "homepage": "https://design-system.sigvelo.com",
6
6
  "bugs": {
@@ -57,7 +57,7 @@
57
57
  "streamdown": "^2.5.0",
58
58
  "unist-util-visit": "^5.1.0",
59
59
  "yet-another-react-lightbox": "^3.32.1",
60
- "@mcp-b/design-tokens": "0.20.0"
60
+ "@mcp-b/design-tokens": "0.20.1"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@ai-sdk/react": "^3.0.222",