Skip to content

Commit c13ae3d

Browse files
committed
refactor(schema): own tool name aliases
1 parent 1c0ccb7 commit c13ae3d

13 files changed

Lines changed: 43 additions & 39 deletions

File tree

‎packages/cli/src/acp/tool.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { isAbsolute, resolve } from "node:path"
22
import type { ToolCall, ToolCallContent, ToolCallLocation, ToolCallUpdate, ToolKind } from "@agentclientprotocol/sdk"
3-
import type { Tool } from "@opencode/schema/tool"
4-
import { canonicalToolName, readDisplayText } from "@opencode/tui/mini/tool"
3+
import { Tool } from "@opencode/schema/tool"
4+
import { readDisplayText } from "@opencode/tui/mini/tool"
55
import { Patch } from "@opencode/util/patch"
66
import { Result } from "effect"
77

@@ -179,7 +179,7 @@ function locationFrom(cwd: string, ...values: unknown[]): ToolCallLocation[] {
179179
}
180180

181181
export function canonicalName(toolName: string) {
182-
return canonicalToolName(toolName.toLocaleLowerCase())
182+
return Tool.canonicalName(toolName.toLocaleLowerCase())
183183
}
184184

185185
// Sessions migrated from V1 keep their original `filePath` tool inputs.

‎packages/cli/test/import-boundaries.test.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ describe("CLI frontend import boundaries", () => {
3434
expect(Object.keys(run).sort()).toEqual(["runNonInteractive", "runV1Bridge"])
3535
expect(Object.keys(mini).sort()).toEqual(["runMiniFrontend"])
3636
expect(Object.keys(tool).sort()).toEqual([
37-
"canonicalToolName",
3837
"nonEmptyToolContent",
3938
"readDisplayText",
4039
"toolInlineInfo",

‎packages/schema/src/tool.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ export type Metadata = Readonly<Record<string, any>>
1111
export const CallID = Schema.String.pipe(Schema.brand("Tool.CallID"))
1212
export type CallID = typeof CallID.Type
1313

14+
export function canonicalName(name: string) {
15+
if (name === "bash") return "shell"
16+
if (name === "task") return "subagent"
17+
if (name === "apply_patch") return "patch"
18+
return name
19+
}
20+
1421
export interface Context {
1522
readonly sessionID: Session.ID
1623
readonly agent: Agent.ID

‎packages/schema/test/tool.test.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { expect, test } from "bun:test"
2+
import { Tool } from "../src/tool.js"
3+
4+
test("maps V1 tool names to their V2 names", () => {
5+
expect(["bash", "task", "apply_patch", "plugin_tool"].map(Tool.canonicalName)).toEqual([
6+
"shell",
7+
"subagent",
8+
"patch",
9+
"plugin_tool",
10+
])
11+
})

‎packages/tui/src/mini/stream-v2.subagent.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type {
2323
SessionMessageInfo,
2424
SessionMessageUser,
2525
} from "@opencode/client/promise"
26+
import { Tool } from "@opencode/schema/tool"
2627
import { Locale } from "../util/locale"
2728
import { createFragmentReconciler, fragmentRef, type FragmentReconciler } from "./stream-v2.fragment"
2829
import { toolImageCommits, userImageCommits } from "./stream-v2.image"
@@ -34,7 +35,7 @@ import type {
3435
MiniPermissionRequest,
3536
StreamCommit,
3637
} from "./types"
37-
import { canonicalToolName, normalizeTool, toolOutputText, toolView } from "./tool"
38+
import { normalizeTool, toolOutputText, toolView } from "./tool"
3839
import { toolDisplayContent } from "../util/tool-display"
3940
import { isRecord } from "../util/record"
4041

@@ -967,7 +968,7 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
967968
main(sdk, event, signal = input.signal) {
968969
if (!active(signal)) return
969970
if (event.type === "session.tool.input.started") {
970-
if (canonicalToolName(event.data.name) === "subagent")
971+
if (Tool.canonicalName(event.data.name) === "subagent")
971972
pendingCalls.set(sourceKey(event.data.assistantMessageID, event.data.id), {})
972973
return
973974
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export { readDisplayText, toolInlineInfo, toolOutputText } from "./tool"
2-
export { canonicalToolName, nonEmptyToolContent } from "../util/tool-display"
2+
export { nonEmptyToolContent } from "../util/tool-display"
33
export type { MiniToolPart } from "./types"

‎packages/tui/src/mini/tool.ts‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import os from "os"
1515
import path from "path"
1616
import stripAnsi from "strip-ansi"
1717
import type { SessionMessageAssistantTool } from "@opencode/client/promise"
18+
import { Tool } from "@opencode/schema/tool"
1819
import { LANGUAGE_EXTENSIONS } from "../util/filetype"
1920
import { Locale } from "../util/locale"
2021
import {
21-
canonicalToolName,
2222
finiteNumber,
2323
primitiveInputSummary,
2424
toolDisplayContent,
@@ -29,8 +29,6 @@ import { formatPath } from "../util/path-format"
2929
import { isRecord } from "../util/record"
3030
import type { RunEntryBody, StreamCommit, ToolSnapshot } from "./types"
3131

32-
export { canonicalToolName } from "../util/tool-display"
33-
3432
type ToolView = {
3533
output: boolean
3634
final: boolean
@@ -157,9 +155,9 @@ function text(v: unknown): string {
157155
export function toolOutputText(name: string, content: ReadonlyArray<{ type: string; text?: string }> | undefined) {
158156
if (!content) return ""
159157
// V2 shell content appends model-only status after the user-visible command output.
160-
if (canonicalToolName(name) === "shell") return content.find((item) => item.type === "text")?.text ?? ""
158+
if (Tool.canonicalName(name) === "shell") return content.find((item) => item.type === "text")?.text ?? ""
161159
const joined = content.flatMap((item) => (item.type === "text" && item.text ? [item.text] : [])).join("\n")
162-
if (canonicalToolName(name) === "read") return readDisplayText(joined) ?? joined
160+
if (Tool.canonicalName(name) === "read") return readDisplayText(joined) ?? joined
163161
return joined
164162
}
165163

@@ -240,7 +238,7 @@ function normalizeMetadata(name: string, value: unknown) {
240238
}
241239

242240
export function normalizeTool(tool: SessionMessageAssistantTool): SessionMessageAssistantTool {
243-
const name = canonicalToolName(tool.name)
241+
const name = Tool.canonicalName(tool.name)
244242
if (tool.state.status === "streaming") return { ...tool, name }
245243
return {
246244
...tool,
@@ -1116,7 +1114,7 @@ function toolFrame(commit: StreamCommit, raw: string): ToolFrame {
11161114
return {
11171115
directory: commit.directory,
11181116
raw,
1119-
name: canonicalToolName(commit.tool || current?.name || "tool"),
1117+
name: Tool.canonicalName(commit.tool || current?.name || "tool"),
11201118
input: current?.input ?? {},
11211119
meta: current?.meta ?? {},
11221120
state: current?.state ?? {},
@@ -1138,7 +1136,7 @@ function runShell(p: ToolProps): ToolInline {
11381136

11391137
export function toolView(name?: string): ToolView {
11401138
return (
1141-
rule(name ? canonicalToolName(name) : undefined)?.view ?? {
1139+
rule(name ? Tool.canonicalName(name) : undefined)?.view ?? {
11421140
output: true,
11431141
final: true,
11441142
}

‎packages/tui/src/routes/session/activity-summary.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import type {
44
SessionMessageAssistantTool,
55
SessionMessageInfo,
66
} from "@opencode/client"
7-
import { canonicalToolName, executeCalls } from "../../util/tool-display"
7+
import { Tool } from "@opencode/schema/tool"
8+
import { executeCalls } from "../../util/tool-display"
89
import { visitEntries } from "./anchor-view"
910
import { instructionPaths, type PartRef, type SessionEntry, type SessionNode } from "./grouping/session"
1011
import { reasoningContent } from "./message-parts"
@@ -46,7 +47,7 @@ export function summarizeActivity(
4647
/** Status for a running item, independent of its details, so it doesn't flicker as they arrive. */
4748
export function busyLabel(part: Item["part"]) {
4849
if (part.type === "reasoning") return "Thinking…"
49-
const name = canonicalToolName(part.name)
50+
const name = Tool.canonicalName(part.name)
5051
const noun = name === "shell" ? "command" : name === "execute" ? "code" : name
5152
return `${part.state.status === "streaming" ? "Preparing" : "Running"} ${noun}…`
5253
}
@@ -66,13 +67,13 @@ export function activitySummary(items: readonly Item[], instructions: number, cl
6667
if (!isActive(item, closed) && reasoningContent(item.part)) counts.thought++
6768
return
6869
}
69-
const name = canonicalToolName(item.part.name)
70+
const name = Tool.canonicalName(item.part.name)
7071
if (name === "execute") {
7172
const calls = executeCalls(
7273
item.part.state.status === "streaming" ? undefined : item.part.state.metadata?.toolCalls,
7374
).filter((call) => call.status !== "running")
7475
calls.forEach((call) => {
75-
if (canonicalToolName(call.tool) === "read") counts.read++
76+
if (Tool.canonicalName(call.tool) === "read") counts.read++
7677
else counts.tool++
7778
})
7879
// With no finished nested calls to count, a finished execute counts as itself.

‎packages/tui/src/routes/session/index.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
type Accessor,
1414
} from "solid-js"
1515
import path from "node:path"
16+
import { Tool } from "@opencode/schema/tool"
1617
import { EOL, tmpdir } from "node:os"
1718
import { mkdir, writeFile } from "node:fs/promises"
1819
import { useRoute, useRouteData } from "../../context/route"
@@ -39,7 +40,6 @@ import { useLocal } from "../../context/local"
3940
import { Locale } from "../../util/locale"
4041
import { FilePath } from "../../ui/file-path"
4142
import {
42-
canonicalToolName,
4343
executeCalls,
4444
executeCallSummary,
4545
finiteNumber,
@@ -1762,7 +1762,7 @@ function BackgroundToolHint(props: { messages: SessionMessageInfo[] }) {
17621762
)
17631763
const part = current?.content.find((part): part is SessionMessageAssistantTool => {
17641764
if (part.type !== "tool" || part.state.status !== "running") return false
1765-
const name = canonicalToolName(part.name)
1765+
const name = Tool.canonicalName(part.name)
17661766
return name === "shell" || name === "subagent"
17671767
})
17681768
if (!current || !part) return

‎packages/tui/src/routes/session/message-parts.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createMemo, createSignal, Match, Show, Switch } from "solid-js"
2+
import { Tool } from "@opencode/schema/tool"
23
import { RGBA, TextAttributes } from "@opentui/core"
34
import type { JSX } from "@opentui/solid"
45
import type {
@@ -14,7 +15,6 @@ import { SplitBorder } from "../../ui/border"
1415
import { Locale } from "../../util/locale"
1516
import { use } from "./render-context"
1617
import { generateThinkingSyntax } from "./thinking-syntax"
17-
import { canonicalToolName } from "../../util/tool-display"
1818

1919
export const INLINE_TOOL_ICON_WIDTH = 2
2020

@@ -35,7 +35,7 @@ const toolDisplays = new Set([
3535
])
3636

3737
export function toolDisplay(tool: string) {
38-
const normalized = canonicalToolName(tool)
38+
const normalized = Tool.canonicalName(tool)
3939
return toolDisplays.has(normalized) ? normalized : "generic"
4040
}
4141

0 commit comments

Comments
 (0)