Skip to content

Commit d966261

Browse files
committed
ensure conversation linearity
1 parent a9b71ca commit d966261

5 files changed

Lines changed: 51 additions & 15 deletions

File tree

packages/opencode/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ export function convertToOpenAICompatibleChatMessages(prompt: LanguageModelV2Pro
104104
type: "function",
105105
function: {
106106
name: part.toolName,
107+
// MITO WARNING: JSON.stringify re-serializes tool call arguments to compact JSON,
108+
// losing the model's original formatting. This breaks the MITO extension property
109+
// if the model generated non-compact JSON. The same issue exists in the npm
110+
// @ai-sdk/openai-compatible package (not our code). If MITO-DEBUG logs show
111+
// extension breaks from JSON formatting, store raw arguments via ToolStatePending.raw
112+
// and use them here instead of re-serializing.
107113
arguments: JSON.stringify(part.input),
108114
},
109115
...partMetadata,

packages/opencode/src/session/llm.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,17 +180,6 @@ export namespace LLM {
180180
})
181181
},
182182
async experimental_repairToolCall(failed) {
183-
const lower = failed.toolCall.toolName.toLowerCase()
184-
if (lower !== failed.toolCall.toolName && tools[lower]) {
185-
l.info("repairing tool call", {
186-
tool: failed.toolCall.toolName,
187-
repaired: lower,
188-
})
189-
return {
190-
...failed.toolCall,
191-
toolName: lower,
192-
}
193-
}
194183
return {
195184
...failed.toolCall,
196185
input: JSON.stringify({

packages/opencode/src/session/processor.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ export namespace SessionProcessor {
8989
case "reasoning-end":
9090
if (value.id in reasoningMap) {
9191
const part = reasoningMap[value.id]
92-
part.text = part.text.trimEnd()
93-
9492
part.time = {
9593
...part.time,
9694
end: Date.now(),
@@ -305,7 +303,6 @@ export namespace SessionProcessor {
305303

306304
case "text-end":
307305
if (currentText) {
308-
currentText.text = currentText.text.trimEnd()
309306
const textOutput = await Plugin.trigger(
310307
"experimental.text.complete",
311308
{

packages/opencode/src/session/system.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export namespace SystemPrompt {
3636
` Working directory: ${Instance.directory}`,
3737
` Is directory a git repo: ${project.vcs === "git" ? "yes" : "no"}`,
3838
` Platform: ${process.platform}`,
39-
` Today's date: ${new Date().toDateString()}`,
4039
`</env>`,
4140
`<directories>`,
4241
` ${
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, test } from "bun:test"
2+
import { SystemPrompt } from "../../src/session/system"
3+
4+
describe("MITO linearity", () => {
5+
describe("system prompt stability", () => {
6+
test("environment() does not contain 'Today's date:'", async () => {
7+
// SystemPrompt.environment requires Instance to be initialized, so we check
8+
// the source directly — the date line was removed from system.ts
9+
const source = await Bun.file(
10+
new URL("../../src/session/system.ts", import.meta.url).pathname,
11+
).text()
12+
expect(source).not.toContain("Today's date")
13+
expect(source).not.toContain("toDateString")
14+
})
15+
16+
test("provider() returns stable output across calls", () => {
17+
const model = {
18+
api: { id: "claude-sonnet-4-20250514" },
19+
} as any
20+
const first = SystemPrompt.provider(model)
21+
const second = SystemPrompt.provider(model)
22+
expect(first).toEqual(second)
23+
})
24+
})
25+
26+
describe("text content preservation", () => {
27+
test("processor does not trimEnd text content", async () => {
28+
const source = await Bun.file(
29+
new URL("../../src/session/processor.ts", import.meta.url).pathname,
30+
).text()
31+
// Neither text-end nor reasoning-end should trimEnd
32+
expect(source).not.toContain("trimEnd()")
33+
})
34+
})
35+
36+
describe("tool call repair does not lowercase", () => {
37+
test("experimental_repairToolCall does not lowercase tool names", async () => {
38+
const source = await Bun.file(
39+
new URL("../../src/session/llm.ts", import.meta.url).pathname,
40+
).text()
41+
// The lowercasing branch should be removed
42+
expect(source).not.toContain("toolName.toLowerCase()")
43+
})
44+
})
45+
})

0 commit comments

Comments
 (0)