Description
When OpenCode communicates via ACP protocol, the initial tool_call notification (sent when a tool starts) always sends an empty rawInput: {}, even though the actual tool arguments are available shortly after in the subsequent tool_call_update.
This breaks ACP clients that display tool call details from the initial notification — they can only show the tool name (e.g. "Read", "Skill") but not the tool arguments (e.g. which file is being read, which skill is being invoked).
Root Cause
The issue is in packages/opencode/src/acp/tool.ts:121-130 — the pendingToolCall() function only receives toolCallId and toolName, so it cannot include real tool input:
export function pendingToolCall(input: { readonly toolCallId: string; readonly toolName: string }): ToolCall {
return {
toolCallId: input.toolCallId,
title: input.toolName,
kind: toToolKind(input.toolName),
status: "pending",
locations: [],
rawInput: {}, // always empty
}
}
And in event.ts:323-335, toolStart() calls pendingToolCall() with just these two fields, without passing part.state.input:
private async toolStart(sessionId: string, part: ToolPart) {
if (this.toolStarts.has(part.callID)) return
this.toolStarts.add(part.callID)
await this.input.connection.sessionUpdate({
sessionId,
update: {
sessionUpdate: "tool_call",
...pendingToolCall({
toolCallId: part.callID,
toolName: part.tool, // part.state.input is available but not passed
}),
},
})
}
Meanwhile, the runningToolUpdate() function (line 132) does include real input:
export function runningToolUpdate(input: {
readonly toolCallId: string
readonly toolName: string
readonly state: RunningToolState // has input: ToolInput
}): ToolCallUpdate {
return {
...
rawInput: input.state.input, // real data!
}
}
But this comes as a separate tool_call_update notification, so clients that only display data from the initial tool_call miss it.
Proposed Fix
Two changes:
-
In pendingToolCall(): accept an optional state parameter with the tool input, so the initial notification can include it when available.
-
In toolStart(): pass part.state.input (and any other available state) to pendingToolCall() so the initial notification carries actual tool arguments.
For tools that go from pending directly to completed (e.g., very fast tool calls), this ensures the client never misses the tool arguments.
ACP Protocol Context
The ACP spec defines rawInput as rawInput?: unknown (optional) in ToolCall, so sending it or not is valid either way. However, including real input when available is better for interoperability — ACP clients that rely on the initial notification for tool call display benefit immediately, and it costs nothing for clients that ignore it.
Impact
Without this fix, ACP clients (like cc-connect, Windsurf, etc.) can only display:
- "Read" — but not which file is being read
- "Skill" — but not which skill is being invoked
- "Bash" — but not what command is being executed
Description
When OpenCode communicates via ACP protocol, the initial
tool_callnotification (sent when a tool starts) always sends an emptyrawInput: {}, even though the actual tool arguments are available shortly after in the subsequenttool_call_update.This breaks ACP clients that display tool call details from the initial notification — they can only show the tool name (e.g. "Read", "Skill") but not the tool arguments (e.g. which file is being read, which skill is being invoked).
Root Cause
The issue is in
packages/opencode/src/acp/tool.ts:121-130— thependingToolCall()function only receivestoolCallIdandtoolName, so it cannot include real tool input:And in
event.ts:323-335,toolStart()callspendingToolCall()with just these two fields, without passingpart.state.input:Meanwhile, the
runningToolUpdate()function (line 132) does include real input:But this comes as a separate
tool_call_updatenotification, so clients that only display data from the initialtool_callmiss it.Proposed Fix
Two changes:
In
pendingToolCall(): accept an optionalstateparameter with the tool input, so the initial notification can include it when available.In
toolStart(): passpart.state.input(and any other available state) topendingToolCall()so the initial notification carries actual tool arguments.For tools that go from
pendingdirectly tocompleted(e.g., very fast tool calls), this ensures the client never misses the tool arguments.ACP Protocol Context
The ACP spec defines
rawInputasrawInput?: unknown(optional) inToolCall, so sending it or not is valid either way. However, including real input when available is better for interoperability — ACP clients that rely on the initial notification for tool call display benefit immediately, and it costs nothing for clients that ignore it.Impact
Without this fix, ACP clients (like cc-connect, Windsurf, etc.) can only display: