Skip to content

Commit 05f1bb8

Browse files
author
Paul Kelly
committed
fix(opencode): recover write calls missing filePath
1 parent e22a2dc commit 05f1bb8

3 files changed

Lines changed: 45 additions & 6 deletions

File tree

‎packages/opencode/src/session/llm.ts‎

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,35 @@ export namespace LLM {
7373
return [value, value.slice(start)]
7474
}
7575

76-
export function repairToolInput(input: { toolInput: string; errorMessage: string }) {
76+
function str(input: unknown) {
77+
if (typeof input !== "string") return
78+
const value = input.trim()
79+
if (!value) return
80+
return value
81+
}
82+
83+
function normalize(input: Record<string, unknown>, tool: string) {
84+
const lower = tool.toLowerCase()
85+
if (!["write", "read", "edit", "multiedit", "lsp"].includes(lower)) return input
86+
if (str(input.filePath)) return input
87+
const filePath = [input.filepath, input.path, input.file, input.filename].map(str).find((x) => x)
88+
if (!filePath) return input
89+
return {
90+
...input,
91+
filePath,
92+
}
93+
}
94+
95+
export function repairToolInput(input: { toolName: string; toolInput: string; errorMessage: string }) {
7796
const match = input.errorMessage.match(/Text:\s*([\s\S]*)$/)
7897
const text = match?.[1] ?? ""
7998
const list = [input.toolInput, text].flatMap(candidates).filter(Boolean)
8099
for (const item of list) {
81100
const parsed = object(item)
82101
if (!parsed) continue
83-
if (Object.keys(parsed).length === 0) continue
84-
return JSON.stringify(parsed)
102+
const fixed = normalize(parsed, input.toolName)
103+
if (Object.keys(fixed).length === 0) continue
104+
return JSON.stringify(fixed)
85105
}
86106
}
87107

@@ -227,6 +247,7 @@ export namespace LLM {
227247
})
228248
}
229249
const repaired = repairToolInput({
250+
toolName: failed.toolCall.toolName,
230251
toolInput: InvalidToolInputError.isInstance(failed.error) ? failed.error.toolInput : failed.toolCall.input,
231252
errorMessage: failed.error.message,
232253
})

‎packages/opencode/src/session/processor.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,17 @@ export namespace SessionProcessor {
203203

204204
case "tool-error": {
205205
const match = toolcalls[value.toolCallId]
206-
if (match && match.state.status === "running") {
206+
if (match && (match.state.status === "running" || match.state.status === "pending")) {
207+
const start = match.state.status === "running" ? match.state.time.start : Date.now()
208+
const input = value.input ?? match.state.input
207209
await Session.updatePart({
208210
...match,
209211
state: {
210212
status: "error",
211-
input: value.input ?? match.state.input,
213+
input,
212214
error: (value.error as any).toString(),
213215
time: {
214-
start: match.state.time.start,
216+
start,
215217
end: Date.now(),
216218
},
217219
},

‎packages/opencode/test/session/llm.test.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ describe("session.llm.hasToolCalls", () => {
102102
describe("session.llm.repairToolInput", () => {
103103
test("repairs truncated tool input json", () => {
104104
const repaired = LLM.repairToolInput({
105+
toolName: "write",
105106
toolInput: `{"filePath":"src/app.ts","content":"hello`,
106107
errorMessage: "Invalid input for tool write",
107108
})
@@ -114,6 +115,7 @@ describe("session.llm.repairToolInput", () => {
114115

115116
test("repairs from error text when tool input is empty", () => {
116117
const repaired = LLM.repairToolInput({
118+
toolName: "write",
117119
toolInput: "",
118120
errorMessage:
119121
'Invalid input for tool write: JSON parsing failed: Text: {"filePath":"src/app.ts","content":"hello"}',
@@ -125,8 +127,22 @@ describe("session.llm.repairToolInput", () => {
125127
})
126128
})
127129

130+
test("normalizes filepath aliases for write tool", () => {
131+
const repaired = LLM.repairToolInput({
132+
toolName: "write",
133+
toolInput: '{"filepath":"src/app.ts","content":"hello"}',
134+
errorMessage:
135+
'The write tool was called with invalid arguments: [{"expected":"string","code":"invalid_type","path":["filePath"],"message":"Invalid input: expected string, received undefined"}]',
136+
})
137+
expect(repaired).toBeDefined()
138+
const parsed = JSON.parse(repaired!)
139+
expect(parsed.filePath).toBe("src/app.ts")
140+
expect(parsed.content).toBe("hello")
141+
})
142+
128143
test("returns undefined when input is not json-like", () => {
129144
const repaired = LLM.repairToolInput({
145+
toolName: "write",
130146
toolInput: "not-json",
131147
errorMessage: "Invalid input for tool write",
132148
})

0 commit comments

Comments
 (0)