本章总览
PermissionRequest.tsx 是工具权限 UI 的统一入口:根据 tool 实例选择 Bash/FileEdit/WebFetch 等子组件,并处理系统通知、interrupt 快捷键与用户交互防 auto-approve。本章衔接 useCanUseTool,要求你能从 ask 分支追到具体 Permission 子目录。
学完本章你应该能
- 解释 permissionComponentForTool 的 switch 映射表
- 说明 ToolUseConfirm 回调字段各自触发的引擎行为
- 描述 setStickyFooter 在全屏长计划场景的作用
- 理解 BashPermissionRequest 与 classifier 动画的性能拆分
- 能在 REPL 中定位 overlay 挂载条件
核心概念(先读懂这些)
门面模式:PermissionRequest 不渲染具体 UI
PermissionRequest 只做路由、通知、快捷键绑定,真正 JSX 在 BashPermissionRequest、FileEditPermissionRequest 等子目录。feature() 门控 ReviewArtifact、Workflow、Monitor 等 ant-only 工具。default 走 FallbackPermissionRequest,保证新工具未适配时仍有通用 Allow/Deny。
onUserInteraction 防 classifier 抢焦点
ToolUseConfirm.onUserInteraction 在用户按方向键、Tab、输入时调用,阻止 bash classifier 异步自动批准在用户阅读对话框时关闭弹窗。这是「自动化」与「可读性」之间的显式 Mutex。
建议学习步骤
- 阅读 permissionComponentForTool(源码块 A)
- 阅读 ToolUseConfirm 类型(源码块 B)
- 阅读 PermissionRequest 主函数(源码块 C)
- 打开 BashPermissionRequest 的 ClassifierCheckingSubtitle(源码块 D)
- 对照 REPL toolPermissionOverlay(源码块 E)
常见误区
注意
onDone 与 onReject 在 interrupt 时都会被调用,注意队列出队顺序
注意
Fallback 与专用组件 API 相同,但 diff 预览能力不同
注意
workerBadge 仅在 swarm worker 路径传入,本地 REPL 常为 undefined
权限 UI 全链路
hasPermissionsToUseTool() → ask
→ useCanUseTool push ToolUseConfirm
→ REPL toolUseConfirmQueue[0]
→ focusedInputDialog = 'tool-permission'
→ <PermissionRequest toolUseConfirm={...} />
→ permissionComponentForTool(tool)
→ BashPermissionRequest | FileEditPermissionRequest | ...
→ onAllow / onReject → resolve Promise → query 继续
components/permissions/ 约 53 文件,除请求组件外还有:
PermissionDialog.tsx、PermissionPrompt.tsx:共享对话框框架rules/*:/permissions 设置界面的规则编辑hooks.ts、utils.ts:日志与 unary 事件SandboxPermissionRequest.tsx:网络 sandbox 独立队列(REPL 另一路径)
permissionComponentForTool 映射
下列 switch 以 Tool 单例引用(===)分支,而非字符串 name——重构 Tool 导出时必须同步更新。
| Tool | 组件 |
|---|---|
| FileEditTool | FileEditPermissionRequest |
| FileWriteTool | FileWritePermissionRequest |
| BashTool | BashPermissionRequest |
| PowerShellTool | PowerShellPermissionRequest |
| Glob/Grep/FileRead | FilesystemPermissionRequest |
| ExitPlanModeV2 | ExitPlanModePermissionRequest |
| EnterPlanMode | EnterPlanModePermissionRequest |
| default | FallbackPermissionRequest |
ReviewArtifact、Workflow、Monitor 通过 feature + require 动态加载,编译期可从 external 构建剔除。
源码引用: src/components/permissions/PermissionRequest.tsx · 第 47–82 行(共 233 行)
47| const WorkflowTool = feature('WORKFLOW_SCRIPTS')
48| ? (
49| require('../../tools/WorkflowTool/WorkflowTool.js') as typeof import('../../tools/WorkflowTool/WorkflowTool.js')
50| ).WorkflowTool
51| : null
52|
53| const WorkflowPermissionRequest = feature('WORKFLOW_SCRIPTS')
54| ? (
55| require('../../tools/WorkflowTool/WorkflowPermissionRequest.js') as typeof import('../../tools/WorkflowTool/WorkflowPermissionRequest.js')
56| ).WorkflowPermissionRequest
57| : null
58|
59| const MonitorTool = feature('MONITOR_TOOL')
60| ? (
61| require('../../tools/MonitorTool/MonitorTool.js') as typeof import('../../tools/MonitorTool/MonitorTool.js')
62| ).MonitorTool
63| : null
64|
65| const MonitorPermissionRequest = feature('MONITOR_TOOL')
66| ? (
67| require('./MonitorPermissionRequest/MonitorPermissionRequest.js') as typeof import('./MonitorPermissionRequest/MonitorPermissionRequest.js')
68| ).MonitorPermissionRequest
69| : null
70|
71| import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'
72| /* eslint-enable @typescript-eslint/no-require-imports */
73| import type { z } from 'zod/v4'
74| import type { PermissionUpdate } from '../../utils/permissions/PermissionUpdateSchema.js'
75| import type { WorkerBadgeProps } from './WorkerBadge.js'
76|
77| function permissionComponentForTool(
78| tool: Tool,
79| ): React.ComponentType<PermissionRequestProps> {
80| switch (tool) {
81| case FileEditTool:
82| return FileEditPermissionRequest
源码引用: src/components/permissions/PermissionRequest.tsx · 第 35–42 行(共 233 行)
35| const ReviewArtifactTool = feature('REVIEW_ARTIFACT')
36| ? (
37| require('../../tools/ReviewArtifactTool/ReviewArtifactTool.js') as typeof import('../../tools/ReviewArtifactTool/ReviewArtifactTool.js')
38| ).ReviewArtifactTool
39| : null
40|
41| const ReviewArtifactPermissionRequest = feature('REVIEW_ARTIFACT')
42| ? (
ToolUseConfirm 数据结构
ToolUseConfirm 是队列元素,连接引擎与 UI:
- assistantMessage / tool / input / toolUseID:渲染与回调标识
- permissionResult:含 behavior 与 rules 提示,供 PermissionRuleExplanation 展示
- onAllow(updatedInput, permissionUpdates, feedback, contentBlocks):可改写 input(如用户编辑命令)并持久化 allow 规则
- onReject(feedback?, contentBlocks?):拒绝并可带用户说明
- recheckPermission():classifier 完成后重新求值
- classifierCheckInProgress / classifierAutoApproved:UI 展示自动批准进度
PermissionRequestProps 额外接收 toolUseContext、verbose、workerBadge、setStickyFooter。
源码引用: src/components/permissions/PermissionRequest.tsx · 第 83–127 行(共 233 行)
83| case FileWriteTool:
84| return FileWritePermissionRequest
85| case BashTool:
86| return BashPermissionRequest
87| case PowerShellTool:
88| return PowerShellPermissionRequest
89| case ReviewArtifactTool:
90| return ReviewArtifactPermissionRequest ?? FallbackPermissionRequest
91| case WebFetchTool:
92| return WebFetchPermissionRequest
93| case NotebookEditTool:
94| return NotebookEditPermissionRequest
95| case ExitPlanModeV2Tool:
96| return ExitPlanModePermissionRequest
97| case EnterPlanModeTool:
98| return EnterPlanModePermissionRequest
99| case SkillTool:
100| return SkillPermissionRequest
101| case AskUserQuestionTool:
102| return AskUserQuestionPermissionRequest
103| case WorkflowTool:
104| return WorkflowPermissionRequest ?? FallbackPermissionRequest
105| case MonitorTool:
106| return MonitorPermissionRequest ?? FallbackPermissionRequest
107| case GlobTool:
108| case GrepTool:
109| case FileReadTool:
110| return FilesystemPermissionRequest
111| default:
112| return FallbackPermissionRequest
113| }
114| }
115|
116| export type PermissionRequestProps<Input extends AnyObject = AnyObject> = {
117| toolUseConfirm: ToolUseConfirm<Input>
118| toolUseContext: ToolUseContext
119| onDone(): void
120| onReject(): void
121| verbose: boolean
122| workerBadge: WorkerBadgeProps | undefined
123| /**
124| * Register JSX to render in a sticky footer below the scrollable area.
125| * Fullscreen mode only (non-fullscreen has no sticky area — terminal
126| * scrollback moves everything together). Call with null to clear.
127| *
PermissionRequest 主组件行为
主函数职责极简但关键:
- app:interrupt 键:串联 onDone、onReject、toolUseConfirm.onReject(Esc/Ctrl+C 统一拒绝)
- getNotificationMessage:生成 OS 通知文案(Plan 模式、ReviewArtifact 有特殊句)
- useNotifyAfterTimeout:长时间未操作提醒用户
- 渲染
<PermissionComponent ... />
TODO 注释提到未来可能下放到 Tool.renderPermissionRequest,当前集中 switch 便于审计所有工具的权限 UI。
源码引用: src/components/permissions/PermissionRequest.tsx · 第 128–216 行(共 233 行)
128| * Used by ExitPlanModePermissionRequest to keep response options visible
129| * while the user scrolls through a long plan. The callback is stable —
130| * JSX passed should use refs for callbacks that close over component state
131| * to avoid stale closures (React reconciles the JSX, preserving Select's
132| * internal focus/input state).
133| */
134| setStickyFooter?: (jsx: React.ReactNode | null) => void
135| }
136|
137| export type ToolUseConfirm<Input extends AnyObject = AnyObject> = {
138| assistantMessage: AssistantMessage
139| tool: Tool<Input>
140| description: string
141| input: z.infer<Input>
142| toolUseContext: ToolUseContext
143| toolUseID: string
144| permissionResult: PermissionDecision
145| permissionPromptStartTimeMs: number
146| /**
147| * Called when user interacts with the permission dialog (e.g., arrow keys, tab, typing).
148| * This prevents async auto-approval mechanisms (like the bash classifier) from
149| * dismissing the dialog while the user is actively engaging with it.
150| */
151| classifierCheckInProgress?: boolean
152| classifierAutoApproved?: boolean
153| classifierMatchedRule?: string
154| workerBadge?: WorkerBadgeProps
155| onUserInteraction(): void
156| onAbort(): void
157| onDismissCheckmark?(): void
158| onAllow(
159| updatedInput: z.infer<Input>,
160| permissionUpdates: PermissionUpdate[],
161| feedback?: string,
162| contentBlocks?: ContentBlockParam[],
163| ): void
164| onReject(feedback?: string, contentBlocks?: ContentBlockParam[]): void
165| recheckPermission(): Promise<void>
166| }
167|
168| function getNotificationMessage(toolUseConfirm: ToolUseConfirm): string {
169| const toolName = toolUseConfirm.tool.userFacingName(
170| toolUseConfirm.input as never,
171| )
172|
173| if (toolUseConfirm.tool === ExitPlanModeV2Tool) {
174| return 'Claude Code needs your approval for the plan'
175| }
176|
177| if (toolUseConfirm.tool === EnterPlanModeTool) {
178| return 'Claude Code wants to enter plan mode'
179| }
180|
181| if (
182| feature('REVIEW_ARTIFACT') &&
183| toolUseConfirm.tool === ReviewArtifactTool
184| ) {
185| return 'Claude needs your approval for a review artifact'
186| }
187|
188| if (!toolName || toolName.trim() === '') {
189| return 'Claude Code needs your attention'
190| }
191|
192| return `Claude needs your permission to use ${toolName}`
193| }
194|
195| // TODO: Move this to Tool.renderPermissionRequest
196| export function PermissionRequest({
197| toolUseConfirm,
198| toolUseContext,
199| onDone,
200| onReject,
201| verbose,
202| workerBadge,
203| setStickyFooter,
204| }: PermissionRequestProps): React.ReactNode {
205| // Handle Ctrl+C (app:interrupt) to reject
206| useKeybinding(
207| 'app:interrupt',
208| () => {
209| onDone()
210| onReject()
211| toolUseConfirm.onReject()
212| },
213| { context: 'Confirmation' },
214| )
215|
216| const notificationMessage = getNotificationMessage(toolUseConfirm)
getNotificationMessage 规则
通知字符串影响 macOS/Windows 横幅与终端 bell:
- ExitPlanModeV2 → 「needs your approval for the plan」
- EnterPlanMode → 「wants to enter plan mode」
- ReviewArtifact feature → 专用文案
- 空 toolName → 通用 「needs your attention」
- 默认 → 「permission to use {toolName}」
与 BashPermissionRequest 内的 destructive warning、sandbox 选项正交——通知只负责把人拉回终端。
源码引用: src/components/permissions/PermissionRequest.tsx · 第 128–143 行(共 233 行)
128| * Used by ExitPlanModePermissionRequest to keep response options visible
129| * while the user scrolls through a long plan. The callback is stable —
130| * JSX passed should use refs for callbacks that close over component state
131| * to avoid stale closures (React reconciles the JSX, preserving Select's
132| * internal focus/input state).
133| */
134| setStickyFooter?: (jsx: React.ReactNode | null) => void
135| }
136|
137| export type ToolUseConfirm<Input extends AnyObject = AnyObject> = {
138| assistantMessage: AssistantMessage
139| tool: Tool<Input>
140| description: string
141| input: z.infer<Input>
142| toolUseContext: ToolUseContext
143| toolUseID: string
BashPermissionRequest 与 classifier 性能
Bash 权限是最复杂的子 UI 之一(约 480 行)。ClassifierCheckingSubtitle 单独抽离 shimmer 动画:
- 原把 useShimmerAnimation 放在 535 行 Inner 内,20fps 时钟导致整窗重绘
- 抽离后仅 subtitle 重绘,PermissionDialog + Select 保持稳定
- CHECKING_TEXT = 「Attempting to auto-approve…」
Inner 还集成:destructiveCommandWarning、sed 编辑解析、sandbox Select、PermissionRuleExplanation、bashToolUseOptions 生成的 Allow 列表。
源码引用: src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx · 第 34–60 行(共 613 行)
34| import { type UnaryEvent, usePermissionRequestLogging } from '../hooks.js'
35| import { PermissionDecisionDebugInfo } from '../PermissionDecisionDebugInfo.js'
36| import { PermissionDialog } from '../PermissionDialog.js'
37| import {
38| PermissionExplainerContent,
39| usePermissionExplainerUI,
40| } from '../PermissionExplanation.js'
41| import type { PermissionRequestProps } from '../PermissionRequest.js'
42| import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js'
43| import { SedEditPermissionRequest } from '../SedEditPermissionRequest/SedEditPermissionRequest.js'
44| import { useShellPermissionFeedback } from '../useShellPermissionFeedback.js'
45| import { logUnaryPermissionEvent } from '../utils.js'
46| import { bashToolUseOptions } from './bashToolUseOptions.js'
47|
48| const CHECKING_TEXT = 'Attempting to auto-approve\u2026'
49|
50| // Isolates the 20fps shimmer clock from BashPermissionRequestInner. Before this
51| // extraction, useShimmerAnimation lived inside the 535-line Inner body, so every
52| // 50ms clock tick re-rendered the entire dialog (PermissionDialog + Select +
53| // all children) for the ~1-3 seconds the classifier typically takes. Inner also
54| // has a Compiler bailout (see below), so nothing was auto-memoized — the full
55| // JSX tree was reconstructed 20-60 times per classifier check.
56| function ClassifierCheckingSubtitle(): React.ReactNode {
57| const [ref, glimmerIndex] = useShimmerAnimation(
58| 'requesting',
59| CHECKING_TEXT,
60| false,
源码引用: src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx · 第 1–33 行(共 613 行)
1| import { feature } from 'bun:bundle'
2| import figures from 'figures'
3| import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4| import { Box, Text, useTheme } from '../../../ink.js'
5| import { useKeybinding } from '../../../keybindings/useKeybinding.js'
6| import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../../services/analytics/growthbook.js'
7| import {
8| type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
9| logEvent,
10| } from '../../../services/analytics/index.js'
11| import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js'
12| import { useAppState } from '../../../state/AppState.js'
13| import { BashTool } from '../../../tools/BashTool/BashTool.js'
14| import {
15| getFirstWordPrefix,
16| getSimpleCommandPrefix,
17| } from '../../../tools/BashTool/bashPermissions.js'
18| import { getDestructiveCommandWarning } from '../../../tools/BashTool/destructiveCommandWarning.js'
19| import { parseSedEditCommand } from '../../../tools/BashTool/sedEditParser.js'
20| import { shouldUseSandbox } from '../../../tools/BashTool/shouldUseSandbox.js'
21| import { getCompoundCommandPrefixesStatic } from '../../../utils/bash/prefix.js'
22| import {
23| createPromptRuleContent,
24| generateGenericDescription,
25| getBashPromptAllowDescriptions,
26| isClassifierPermissionsEnabled,
27| } from '../../../utils/permissions/bashClassifier.js'
28| import { extractRules } from '../../../utils/permissions/PermissionUpdate.js'
29| import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js'
30| import { SandboxManager } from '../../../utils/sandbox/sandbox-adapter.js'
31| import { Select } from '../../CustomSelect/select.js'
32| import { ShimmerChar } from '../../Spinner/ShimmerChar.js'
33| import { useShimmerAnimation } from '../../Spinner/useShimmerAnimation.js'
FileEdit 与 Plan 模式专用组件
FileEditPermissionRequest / FileWritePermissionRequest:展示 diff(FileWriteToolDiff、NotebookEditToolDiff),支持 acceptEdits 模式下的简化批准。
ExitPlanModePermissionRequest:使用 setStickyFooter 把响应选项固定在 FullscreenLayout bottom,用户滚动长 plan 时仍可按 1/2/3 选择。
FilesystemPermissionRequest:统一处理 Glob/Grep/Read 的 path 展示。
AskUserQuestionPermissionRequest:渲染 PreviewBox,与 AskUserQuestionTool 输入 schema 对齐。
阅读这些文件时共用 PermissionDialog + Select + usePermissionRequestLogging 模式,日志事件名在 hooks.ts 统一。
源码引用: src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx · 第 1–40 行(共 80 行)
1| import { basename, relative } from 'path'
2| import React from 'react'
3| import { FileEditToolDiff } from 'src/components/FileEditToolDiff.js'
4| import { getCwd } from 'src/utils/cwd.js'
5| import type { z } from 'zod/v4'
6| import { Text } from '../../../ink.js'
7| import { FileEditTool } from '../../../tools/FileEditTool/FileEditTool.js'
8| import { FilePermissionDialog } from '../FilePermissionDialog/FilePermissionDialog.js'
9| import {
10| createSingleEditDiffConfig,
11| type FileEdit,
12| type IDEDiffSupport,
13| } from '../FilePermissionDialog/ideDiffConfig.js'
14| import type { PermissionRequestProps } from '../PermissionRequest.js'
15|
16| type FileEditInput = z.infer<typeof FileEditTool.inputSchema>
17|
18| const ideDiffSupport: IDEDiffSupport<FileEditInput> = {
19| getConfig: (input: FileEditInput) =>
20| createSingleEditDiffConfig(
21| input.file_path,
22| input.old_string,
23| input.new_string,
24| input.replace_all,
25| ),
26| applyChanges: (input: FileEditInput, modifiedEdits: FileEdit[]) => {
27| const firstEdit = modifiedEdits[0]
28| if (firstEdit) {
29| return {
30| ...input,
31| old_string: firstEdit.old_string,
32| new_string: firstEdit.new_string,
33| replace_all: firstEdit.replace_all,
34| }
35| }
36| return input
37| },
38| }
39|
40| export function FileEditPermissionRequest(
源码引用: src/components/permissions/PermissionDialog.tsx · 第 1–35 行(共 55 行)
1| import * as React from 'react'
2| import { Box } from '../../ink.js'
3| import type { Theme } from '../../utils/theme.js'
4| import { PermissionRequestTitle } from './PermissionRequestTitle.js'
5| import type { WorkerBadgeProps } from './WorkerBadge.js'
6|
7| type Props = {
8| title: string
9| subtitle?: React.ReactNode
10| color?: keyof Theme
11| titleColor?: keyof Theme
12| innerPaddingX?: number
13| workerBadge?: WorkerBadgeProps
14| titleRight?: React.ReactNode
15| children: React.ReactNode
16| }
17|
18| export function PermissionDialog({
19| title,
20| subtitle,
21| color = 'permission',
22| titleColor,
23| innerPaddingX = 1,
24| workerBadge,
25| titleRight,
26| children,
27| }: Props): React.ReactNode {
28| return (
29| <Box
30| flexDirection="column"
31| borderStyle="round"
32| borderColor={color}
33| borderLeft={false}
34| borderRight={false}
35| borderBottom={false}
REPL 如何挂载 PermissionRequest
REPL 4519 行:
focusedInputDialog === 'tool-permission' 时才渲染 overlay。
- key=
toolUseID保证队列切换时 remount 干净状态 - onDone 出队:
setToolUseConfirmQueue(([_, ...tail]) => tail) - getToolUseContext(...) 传入最新 messages 与 abortController
- 全屏时 setStickyFooter 传入 ExitPlan 等组件
ScrollKeybindingHandler 在 tool-permission 时仍 active,允许滚动背后 transcript——设计意图是用户可对照历史命令再批准。
源码引用: src/screens/REPL.tsx · 第 4519–4519 行(共 7050 行)
4519| }
源码引用: src/screens/REPL.tsx · 第 4561–4561 行(共 7050 行)
4561| }
rules/ 与设置界面
permissions/rules/ 下的 AddPermissionRules、PermissionRuleList、WorkspaceTab 等服务于 /permissions 斜杠命令,与单次 ToolUseConfirm 不同生命周期。
调试「为何总是 ask」时:
- utils/permissions/permissions.ts 看规则求值
- useCanUseTool 看是否入队
- PermissionRequest 看是否映射到正确子组件
- rules/ 看用户是否配置了 deny/allow 模式
WorkerPendingPermission、WorkerBadge 处理 swarm worker 发起、leader 批准的路径,Mailbox 与 REPL 队列桥接。
源码引用: src/components/permissions/rules/PermissionRuleList.tsx · 第 1–30 行(共 800 行)
1| import chalk from 'chalk'
2| import figures from 'figures'
3| import * as React from 'react'
4| import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5| import { useAppState, useSetAppState } from 'src/state/AppState.js'
6| import {
7| applyPermissionUpdate,
8| persistPermissionUpdate,
9| } from 'src/utils/permissions/PermissionUpdate.js'
10| import type { PermissionUpdateDestination } from 'src/utils/permissions/PermissionUpdateSchema.js'
11| import type { CommandResultDisplay } from '../../../commands.js'
12| import { Select } from '../../../components/CustomSelect/select.js'
13| import { useExitOnCtrlCDWithKeybindings } from '../../../hooks/useExitOnCtrlCDWithKeybindings.js'
14| import { useSearchInput } from '../../../hooks/useSearchInput.js'
15| import type { KeyboardEvent } from '../../../ink/events/keyboard-event.js'
16| import { Box, Text, useTerminalFocus } from '../../../ink.js'
17| import { useKeybinding } from '../../../keybindings/useKeybinding.js'
18| import {
19| type AutoModeDenial,
20| getAutoModeDenials,
21| } from '../../../utils/autoModeDenials.js'
22| import type {
23| PermissionBehavior,
24| PermissionRule,
25| PermissionRuleValue,
26| } from '../../../utils/permissions/PermissionRule.js'
27| import { permissionRuleValueToString } from '../../../utils/permissions/permissionRuleParser.js'
28| import {
29| deletePermissionRule,
30| getAllowRules,
源码引用: src/components/permissions/WorkerBadge.tsx · 第 1–25 行(共 28 行)
1| import * as React from 'react'
2| import { BLACK_CIRCLE } from '../../constants/figures.js'
3| import { Box, Text } from '../../ink.js'
4| import { toInkColor } from '../../utils/ink.js'
5|
6| export type WorkerBadgeProps = {
7| name: string
8| color: string
9| }
10|
11| /**
12| * Renders a colored badge showing the worker's name for permission prompts.
13| * Used to indicate which swarm worker is requesting the permission.
14| */
15| export function WorkerBadge({
16| name,
17| color,
18| }: WorkerBadgeProps): React.ReactNode {
19| const inkColor = toInkColor(color)
20| return (
21| <Box flexDirection="row" gap={1}>
22| <Text color={inkColor}>
23| {BLACK_CIRCLE} <Text bold>@{name}</Text>
24| </Text>
25| </Box>
源码目录
展开 BashPermissionRequest/、FileEditPermissionRequest/ 等子目录对照本页表格。
动手练习
- 触发 FileEdit 确认,观察 diff 组件与 Fallback 的差异
- 在 classifier 等待时注意 subtitle shimmer 是否独立闪烁
- 按 interrupt 绑定,确认队列出队且 transcript 出现 reject 结果
- 打开 /permissions,对照 rules/ 与 PermissionRuleExplanation 文案来源
本章小结与延伸
PermissionRequest = 权限 UI 路由器。策略求值在 utils/permissions;队列在 useCanUseTool;展示在各 *PermissionRequest 子目录。 继续学习: