1
>
/*---------------------------------------------------------------------------------------------
claudeToolDisplay.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import { localize } from '../../../../nls.js';
7
>
import { appendEscapedMarkdownInlineCode, escapeMarkdownLinkLabel } from '../../../../base/common/htmlContent.js';
8
>
import { basename } from '../../../../base/common/resources.js';
9
>
import { truncate } from '../../../../base/common/strings.js';
10
>
import { URI } from '../../../../base/common/uri.js';
11
>
import { toToolCallMeta, type IToolCallMeta, type ToolKind } from '../../common/meta/agentToolCallMeta.js';
12
>
import type { StringOrMarkdown } from '../../common/state/protocol/state.js';
13
>
import { getServerToolDisplay } from '../shared/serverToolGroups.js';
14
>
15
>
/**
16
>
* Phase 7 S4 — pure tool-name → display/permission helpers for Claude.
17
>
*
18
>
* Mirrors the shape of [copilotToolDisplay.ts](../copilot/copilotToolDisplay.ts)
19
>
* but is keyed off the SDK's built-in tool list. The mapping table lives
20
>
* here (and is snapshot-tested in
21
>
* [claudeToolDisplay.test.ts](../../test/node/claudeToolDisplay.test.ts))
22
>
* so renames of either the SDK tool names or the host's `permissionKind`
23
>
* union flow through compile-checks and the snapshot diff.
24
>
*
25
>
* No I/O, no DI; safe to import from any layer of `agentHost`.
26
>
*/
27
>
28
>
/**
29
>
* Auto-approval kind reported alongside `pending_confirmation` signals
30
>
* (see `IAgentToolPendingConfirmationSignal.permissionKind` in
31
>
* [agentService.ts:317](../../common/agentService.ts#L317)).
32
>
*
33
>
* Phase 7 only emits the subset relevant to Claude's built-in tools —
34
>
* `hook` and `memory` are reserved for later phases.
35
>
*/
36
>
export type ClaudePermissionKind =
37
>
| 'shell'
38
>
| 'write'
39
>
| 'mcp'
40
>
| 'read'
41
>
| 'url'
42
>
| 'skill'
43
>
| 'custom-tool';
44
>
45
>
/**
46
>
* Phase 8.5 — rendering hint for the workbench. Drives terminal /
47
>
* search / subagent renderers (the workbench picks a renderer off
48
>
* `_meta.toolKind`; unknown values fall through to the generic tool
49
>
* renderer). Mirror of
50
>
* [`copilotToolDisplay.getToolKind`](../copilot/copilotToolDisplay.ts).
51
>
*/
52
>
export type ClaudeToolKind = ToolKind;
53
>
54
>
/**
55
>
* Which field on the SDK's `tool_input` carries the path/url surfaced
56
>
* to the user (and tracked by Phase 8 for file-edit tools). One field
57
>
* per tool — tools without a path-bearing field omit this.
58
>
*/
59
>
type ClaudeToolPathField = 'file_path' | 'notebook_path' | 'path' | 'url';
60
>
61
>
/**
62
>
* Single source-of-truth row for one of Claude's built-in tools. Every
63
>
* structural fact the host needs about the tool sits in this row; the
64
>
* exported helpers below are one-liners over the table. Adding a new
65
>
* SDK tool means adding one row and one `displayName` arm. The
66
>
* snapshot test in [claudeToolDisplay.test.ts](../../test/node/claudeToolDisplay.test.ts)
67
>
* fails until both this map and the snapshot are updated together.
68
>
*
69
>
* `displayName` is intentionally NOT on the row — it is user-facing
70
>
* and must be `localize()`-d, which we cannot do at module-init time
71
>
* without freezing the bundle's locale. Lookup lives in
72
>
* {@link getClaudeToolDisplayName}.
73
>
*/
74
>
interface ClaudeToolRow {
75
>
readonly permissionKind: ClaudePermissionKind;
76
>
/** Field on `tool_input` carrying the path/url for this tool, if any. */
77
>
readonly pathField?: ClaudeToolPathField;
78
>
/** True for tools whose execution writes to disk and is tracked by `FileEditTracker` (Phase 8). */
79
>
readonly isFileEdit?: true;
80
>
/**
81
>
* True for tools the SDK never auto-approves under any
82
>
* `permissionMode` (so they always reach `canUseTool`). Drives
83
>
* {@link INTERACTIVE_CLAUDE_TOOLS}.
84
>
*/
85
>
readonly interactive?: true;
86
>
/**
87
>
* Phase 8.5 — rendering hint for the workbench (drives the
88
>
* terminal / search / subagent renderers). Omit for tools that
89
>
* render in the generic tool renderer (read, write, MCP, …).
90
>
*/
91
>
readonly toolKind?: ClaudeToolKind;
92
>
}
93
>
94
>
const TOOL_ROWS: { readonly [toolName: string]: ClaudeToolRow } = {
95
>
// shell tools — no `language` is carried: the workbench picks
96
>
// `'shellscript'` from the tool name (it only special-cases
97
>
// `'powershell'`), and the SDK's `Bash` tool is the generic shell
98
>
// entry point (bash on POSIX, Git Bash on Windows), so claiming a
99
>
// specific dialect here would be misleading and unused.
100
>
Bash: { permissionKind: 'shell', toolKind: 'terminal' },
101
>
BashOutput: { permissionKind: 'shell', toolKind: 'terminal' },
102
>
KillBash: { permissionKind: 'shell', toolKind: 'terminal' },
103
>
104
>
// read tools
105
>
Read: { permissionKind: 'read', pathField: 'file_path' },
106
>
Glob: { permissionKind: 'read', pathField: 'path', toolKind: 'search' },
107
>
Grep: { permissionKind: 'read', pathField: 'path', toolKind: 'search' },
108
>
LS: { permissionKind: 'read', pathField: 'path' },
109
>
NotebookRead: { permissionKind: 'read', pathField: 'notebook_path' },
110
>
111
>
// write tools
112
>
Write: { permissionKind: 'write', pathField: 'file_path', isFileEdit: true },
113
>
Edit: { permissionKind: 'write', pathField: 'file_path', isFileEdit: true },
114
>
MultiEdit: { permissionKind: 'write', pathField: 'file_path', isFileEdit: true },
115
>
NotebookEdit: { permissionKind: 'write', pathField: 'notebook_path', isFileEdit: true },
116
>
TodoWrite: { permissionKind: 'write' },
117
>
118
>
// network tools
119
>
WebFetch: { permissionKind: 'url', pathField: 'url' },
120
>
121
>
// host-routed / custom
122
>
Task: { permissionKind: 'custom-tool', toolKind: 'subagent' },
123
>
Agent: { permissionKind: 'custom-tool', toolKind: 'subagent' },
124
>
ExitPlanMode: { permissionKind: 'custom-tool', interactive: true },
125
>
AskUserQuestion: { permissionKind: 'custom-tool', interactive: true },
126
>
127
>
// skill + task-list family — host-routed custom tools that render in the
128
>
// generic tool renderer (no `toolKind`) but carry rich invocation /
129
>
// past-tense messages so their collapsed row is self-explanatory.
130
>
Skill: { permissionKind: 'skill' },
131
>
TaskCreate: { permissionKind: 'custom-tool' },
132
>
TaskUpdate: { permissionKind: 'custom-tool' },
133
>
TaskList: { permissionKind: 'custom-tool' },
134
>
TaskGet: { permissionKind: 'custom-tool' },
135
>
};
136
>
137
>
const MCP_TOOL_PREFIX = 'mcp__';
138
>
139
>
/**
140
>
* S4 row lookup. Falls back to `'custom-tool'` for unknown tools so
141
>
* Claude's growing built-in list never breaks the host.
142
>
*/
143
>
export function getClaudePermissionKind(toolName: string): ClaudePermissionKind {
144
const row = TOOL_ROWS[toolName];
145
if (row) {