Skip to content

Commit 49104df

Browse files
authored
feat: auto-repair reasoning_content for beam models and bump CLI version to 1.88.0 (v1.0.6) (#7)
- Support models requiring previous assistant reasoning_content in history (e.g. reflection/beam:free) - Prevent HTTP 400 parameter errors in multi-turn assistant and agent workflows - Update CLI_VERSION to 1.88.0 matching current npm release - Unit tests added, 86 pass / 0 fail
1 parent 2d00b02 commit 49104df

6 files changed

Lines changed: 55 additions & 4 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) - SemVer.
55

66
## [Unreleased]
77

8+
## [1.0.6] - 2026-10-25
9+
10+
### Added
11+
- Auto-repair for models requiring historical `reasoning_content` (such as `reflection/beam:free`). Prevents HTTP 400 parameter errors in multi-turn assistant and agent workflows by synthesizing compliant thoughts.
12+
13+
### Changed
14+
- Updated tracked Command Code CLI version signature (`CLI_VERSION`) from `1.73.4` to `1.88.0`.
15+
816
## [1.0.5] - 2026-10-24
917

1018
### Changed

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "opencode-commandcode",
3-
"version": "1.0.5",
3+
"version": "1.0.6",
44
"description": "Production-grade OpenCode plugin for Command Code: dynamic model discovery, stability hooks and quota telemetry",
55
"main": "index.ts",
66
"type": "module",

‎src/client/api.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { resolveApiKey, resolveApiKeyWithSource } from "../auth/resolver.ts";
1818
import { resolveStateDir } from "../hooks/resilience.ts";
1919

2020
export const API_BASE = "https://api.commandcode.ai";
21-
export const CLI_VERSION = "1.73.4";
21+
export const CLI_VERSION = "1.88.0";
2222

2323
const DEFAULT_TIMEOUT_MS = 10_000;
2424
const CATALOG_TTL_MS = 6 * 60 * 60 * 1000;

‎src/hooks/resilience.ts‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export interface SanitizeResult {
105105
* 4. `null` / empty tool response content -> normalized to "{}".
106106
* 5. Assistant tool_calls with no answer in their block -> synthetic response.
107107
*/
108-
export function sanitizeCommandCodeMessages(messages: Message[]): SanitizeResult {
108+
export function sanitizeCommandCodeMessages(messages: Message[], targetModel?: string): SanitizeResult {
109109
if (!Array.isArray(messages) || messages.length === 0) {
110110
return { messages: Array.isArray(messages) ? messages : [], modified: false, fixes: [] };
111111
}
@@ -225,6 +225,25 @@ export function sanitizeCommandCodeMessages(messages: Message[]): SanitizeResult
225225
}
226226
}
227227

228+
// 6. Inject reasoning_content for models requiring reasoning history (e.g. reflection/beam).
229+
const requiresReasoning =
230+
typeof targetModel === "string" &&
231+
(targetModel.toLowerCase().includes("beam") || targetModel.toLowerCase().includes("reflection"));
232+
233+
if (requiresReasoning) {
234+
for (let i = 0; i < finalMessages.length; i++) {
235+
const msg = finalMessages[i]!;
236+
if (msg.role === "assistant" && (!msg.reasoning_content || typeof msg.reasoning_content !== "string")) {
237+
msg.reasoning_content =
238+
typeof msg.content === "string" && msg.content.trim().length > 0
239+
? `Thought: ${msg.content.slice(0, 100)}`
240+
: "Thought: Processing step";
241+
fixes.push(`Injected missing reasoning_content at assistant message ${i} for ${targetModel}`);
242+
modified = true;
243+
}
244+
}
245+
}
246+
228247
void syntheticCounter;
229248
return { messages: finalMessages, modified, fixes };
230249
}

‎src/index.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ export default {
195195
}
196196
if (!Array.isArray(json.messages)) return;
197197

198-
const { messages, modified, fixes } = sanitizeCommandCodeMessages(json.messages);
198+
const targetModel = typeof json.model === "string" ? json.model : undefined;
199+
const { messages, modified, fixes } = sanitizeCommandCodeMessages(json.messages, targetModel);
199200
if (!modified) return;
200201

201202
json.messages = messages;

‎tests/sanitizer.test.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,26 @@ describe("capExcessiveSleepCommand (unwired helper, kept for a future tool hook)
210210
expect(res.command).toBe("sleep 10 && ls");
211211
});
212212
});
213+
214+
test("injects synthetic reasoning_content on assistant messages when targetModel is reflection/beam", () => {
215+
const messages = [
216+
{ role: "user", content: "hello" },
217+
{ role: "assistant", content: "I am assistant" },
218+
{ role: "user", content: "ping" },
219+
];
220+
const res = sanitizeCommandCodeMessages(messages as any, "reflection/beam:free");
221+
expect(res.modified).toBe(true);
222+
expect(res.messages[1]?.reasoning_content).toBe("Thought: I am assistant");
223+
expect(res.fixes.some((f) => f.includes("Injected missing reasoning_content"))).toBe(true);
224+
});
225+
226+
test("does not inject reasoning_content when model is not reasoning-dependent", () => {
227+
const messages = [
228+
{ role: "user", content: "hello" },
229+
{ role: "assistant", content: "I am assistant" },
230+
{ role: "user", content: "ping" },
231+
];
232+
const res = sanitizeCommandCodeMessages(messages as any, "deepseek/deepseek-v4.1-flash");
233+
expect(res.modified).toBe(false);
234+
expect(res.messages[1]?.reasoning_content).toBeUndefined();
235+
});

0 commit comments

Comments
 (0)