mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 12:40:32 +00:00
feat: Add tool names to tool calls (#1128)
Supports the new tool call name RFD
This commit is contained in:
@@ -71,7 +71,9 @@ with `Reason: `. The SDK `description` operation subtitle is not copied there.
|
||||
|
||||
The permission request carries the same standard ACP tool information used for normal tool updates:
|
||||
|
||||
- `name`, `kind`, `title`, `content`, and `locations` come from `toolInfoFromToolUse`;
|
||||
- `name` is the SDK tool name (for example `Read` or `mcp__server__tool`), matching the standard
|
||||
`name` field on the initial `tool_call` update;
|
||||
- `kind`, `title`, `content`, and `locations` come from `toolInfoFromToolUse`;
|
||||
- `status` is `pending`;
|
||||
- `rawInput` is the original SDK input object;
|
||||
- `blockedPath` is appended to `locations` when it is valid and not already present;
|
||||
|
||||
@@ -1252,7 +1252,9 @@ type ProviderConfig = {
|
||||
export type ToolUpdateMeta = {
|
||||
contextCompaction?: ContextCompactionMetadata;
|
||||
claudeCode?: {
|
||||
/* The name of the tool that was used in Claude Code. */
|
||||
/* The name of the tool that was used in Claude Code. Also carried as the
|
||||
standard ACP `name` field on the initial `tool_call`; kept here so every
|
||||
`tool_call_update` stays self-describing for clients that key off it. */
|
||||
toolName: string;
|
||||
/* A human-readable title supplied by Claude Code for the tool call. */
|
||||
title?: string;
|
||||
@@ -9358,7 +9360,10 @@ function resolveSkillPath(skillName: string, cwd?: string): string | undefined {
|
||||
* refine) and the permission flow (`ensureToolCallEmitted`), so they can't
|
||||
* drift. The initial `tool_call` carries `status: "pending"` and, for Bash, the
|
||||
* `terminal_info` _meta that the later `terminal_output`/`terminal_exit`
|
||||
* updates key off of; a refining `tool_call_update` carries neither. */
|
||||
* updates key off of, and the programmatic tool `name` (ACP's tool-call-name
|
||||
* RFD); a refining `tool_call_update` carries none of these. `name` is set
|
||||
* once at first report — on a v1 update, omitting it means "unchanged", and
|
||||
* the tool behind a `toolCallId` never changes. */
|
||||
function toolCallNotification(
|
||||
toolUse: { id: string; name: string; input: unknown },
|
||||
rawInput: unknown,
|
||||
@@ -9384,6 +9389,7 @@ function toolCallNotification(
|
||||
} satisfies ToolUpdateMeta,
|
||||
toolCallId: toolUse.id,
|
||||
sessionUpdate: "tool_call",
|
||||
name: toolUse.name,
|
||||
rawInput,
|
||||
status: "pending",
|
||||
...toolInfoFromToolUse(toolUse, supportsTerminalOutput, cwd),
|
||||
|
||||
@@ -422,6 +422,9 @@ function failedControlFallback(
|
||||
...terminal.update,
|
||||
sessionUpdate: "tool_call",
|
||||
status: "failed",
|
||||
// The synthesized tool_call is this call's first report, so give it
|
||||
// the standard `name` the initial one would have carried.
|
||||
...(typeof claudeMeta?.toolName === "string" ? { name: claudeMeta.toolName } : {}),
|
||||
title:
|
||||
typeof terminal.update.title === "string" && terminal.update.title.length > 0
|
||||
? terminal.update.title
|
||||
|
||||
@@ -140,6 +140,7 @@ describe("NativeSubagentRuntime lifecycle", () => {
|
||||
update: {
|
||||
sessionUpdate: "tool_call",
|
||||
toolCallId: "agent-tool",
|
||||
name: "Agent",
|
||||
title: "Agent",
|
||||
status: "failed",
|
||||
_meta: { claudeCode: { toolName: "Agent" } },
|
||||
|
||||
@@ -3420,6 +3420,50 @@ describe("Skill tool rendering", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ACP tool-call-name RFD: the initial tool_call carries the programmatic
|
||||
// tool name as the standard `name` field, alongside `_meta.claudeCode.toolName`.
|
||||
describe("standard `name` on tool_call notifications", () => {
|
||||
it.each([
|
||||
["Read", { file_path: "/tmp/a.ts" }],
|
||||
["Bash", { command: "ls" }],
|
||||
["mcp__github__list_issues", { repo: "acp" }],
|
||||
])("reports %s as `name` on the initial tool_call", (name, input) => {
|
||||
const notifications = toAcpNotifications(
|
||||
[{ type: "tool_use", id: "toolu_name", name, input }] as any,
|
||||
"assistant",
|
||||
"test-session",
|
||||
{},
|
||||
{} as AcpClient,
|
||||
mockLogger,
|
||||
);
|
||||
expect(notifications[0]?.update).toMatchObject({
|
||||
sessionUpdate: "tool_call",
|
||||
toolCallId: "toolu_name",
|
||||
name,
|
||||
_meta: { claudeCode: { toolName: name } },
|
||||
});
|
||||
});
|
||||
|
||||
it("does not re-send `name` on the refining tool_call_update", () => {
|
||||
const notifications = toAcpNotifications(
|
||||
[
|
||||
{ type: "tool_use", id: "toolu_refine", name: "Read", input: { file_path: "/tmp/a" } },
|
||||
] as any,
|
||||
"assistant",
|
||||
"test-session",
|
||||
{},
|
||||
{} as AcpClient,
|
||||
mockLogger,
|
||||
{ emittedToolCalls: new Set(["toolu_refine"]) },
|
||||
);
|
||||
expect(notifications[0]?.update).toMatchObject({
|
||||
sessionUpdate: "tool_call_update",
|
||||
toolCallId: "toolu_refine",
|
||||
});
|
||||
expect((notifications[0]?.update as any).name).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("_meta.claudeCode.skillPath", () => {
|
||||
const skillMeta = (skill: string, cwd?: string) =>
|
||||
(
|
||||
|
||||
Reference in New Issue
Block a user