opencode-thinking-fix 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -60
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,11 +12,20 @@ npm install opencode-thinking-fix
|
|
|
12
12
|
|
|
13
13
|
> Restores reasoning content that clients and SDKs drop from multi-turn conversations — DeepSeek, Kimi, GLM, MiMo, MiniMax, OpenCode Go, and OpenRouter.
|
|
14
14
|
>
|
|
15
|
-
> **
|
|
15
|
+
> **Start with the plugin.** It is a zero-config seatbelt that stops the 400 error. Add the proxy when you need the model to remember what it was thinking. [Choose your setup](#choose-your-setup).
|
|
16
16
|
>
|
|
17
17
|
> Docs: [OpenCode Plugins](https://opencode.ai/docs/plugins)
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Table of Contents
|
|
20
|
+
|
|
21
|
+
- [The Problem](#the-problem)
|
|
22
|
+
- [The Evidence/Proof](#the-evidence--proof)
|
|
23
|
+
- [Quick Install](#quick-install)
|
|
24
|
+
- [Choose Your Setup](#choose-your-setup)
|
|
25
|
+
- [Affected Models](#affected-models)
|
|
26
|
+
- [Troubleshooting](#troubleshooting)
|
|
27
|
+
|
|
28
|
+
## The Problem
|
|
20
29
|
|
|
21
30
|
When DeepSeek or Kimi answers you, it scribbles notes first. "Let me think... the user wants a login page... I should use React Hook Form... check the API docs..." These notes are `reasoning_content`. You never see them. But the AI needs them — the provider docs require the notes to be sent back on every later turn.
|
|
22
31
|
|
|
@@ -24,10 +33,25 @@ The client and its SDK throw the notebook away. Next turn, the AI reaches for it
|
|
|
24
33
|
|
|
25
34
|
The drop happens in SDK serializers and client transforms (truthy checks, missing type members, stripped signatures), not in the providers. The providers do exactly what their docs say.
|
|
26
35
|
|
|
36
|
+
<!-- TODO for Developer: Insert a terminal screenshot or GIF here showing the 400 error on the left and a successful turn-2 on the right -->
|
|
37
|
+
|
|
27
38
|
**Without the fix:** "Build me a login page." → AI builds it. "Now add password reset." → notes gone, the answer comes back generic.
|
|
28
39
|
|
|
29
40
|
**With the fix:** "Now add password reset." → AI reads its notes: "I used React Hook Form for login, I'll extend that." → works.
|
|
30
41
|
|
|
42
|
+
> **Deprecated:** the historical symptom of this drop — `HTTP 400: The reasoning_content in the thinking mode must be passed back to the API` — is **resolved** by this fix. This repo is about the drop itself, not the error it used to cause.
|
|
43
|
+
|
|
44
|
+
DeepSeek V4 (and Kimi K2.7, GLM 5.x, MiMo V2.5) require that `reasoning_content` from every prior assistant turn gets included in subsequent API requests. The [docs](https://api-docs.deepseek.com/guides/thinking_mode) describe the contract, and the strictness varies by provider and endpoint: some reject the request, some ignore the field, and the conversation still loses its context either way. All five providers confirm the passback requirement in their official documentation:
|
|
45
|
+
|
|
46
|
+
- **DeepSeek**: "[The reasoning_content will be ignored by the API](https://api-docs.deepseek.com/guides/thinking_mode)", but the conversation history must contain the field.
|
|
47
|
+
- **Z.AI / GLM**: "[Key: return reasoning_content to keep the reasoning coherent](https://docs.z.ai/en/api/thinking)."
|
|
48
|
+
- **Kimi / Moonshot**: "[You must keep the reasoning_content in the multi-round conversation... otherwise an error will be thrown](https://platform.moonshot.ai/docs/guide/thinking-mode)."
|
|
49
|
+
- **MiniMax**: "[The complete model response must be append to the conversation history](https://platform.minimaxi.com/document/ChatCompletion%20v2)."
|
|
50
|
+
- **Xiaomi MiMo**: "[Any assistant message with tool calls... must preserve its full reasoning_content field, otherwise the API will return a 400 error](https://dev.mi.com/doc/llm-api). Affected frameworks include TRAE, Cursor, Roo Code, Codex, GitHub Copilot CLI, Zed, AutoGen."
|
|
51
|
+
|
|
52
|
+
The field is non-standard per OpenAI, so SDKs and clients ignore it — the drop is
|
|
53
|
+
systemic, not a single tool's bug. This repo fixes it. Three layers, pick what you need.
|
|
54
|
+
|
|
31
55
|
## See it happen
|
|
32
56
|
|
|
33
57
|
You don't need a specific free-tier model. Use any reasoning model on your own API key (DeepSeek V4, Kimi K2.7, GLM-5, MiMo) or any reasoning-capable model from the routing table below.
|
|
@@ -60,7 +84,7 @@ This bug is conditional, and the conditions are "you work the tool hard":
|
|
|
60
84
|
|
|
61
85
|
If you run long sessions, heavy tool calls, model switching, or subagents, you have probably been hit and blamed the model. That is who this fix is for. If you send three messages a day, you may genuinely never notice — and the plugin detects that and does nothing.
|
|
62
86
|
|
|
63
|
-
## The
|
|
87
|
+
## The Evidence / Proof
|
|
64
88
|
|
|
65
89
|
The numbers above come from four passive taps (DeepSeek, OpenCode Go, OpenCode Zen, OpenRouter) that forwarded traffic byte-for-byte untouched and logged only metadata. They ran for a week of real work, then were retired. Methodology and raw data: [`proof/` directory](proof/).
|
|
66
90
|
|
|
@@ -172,7 +196,23 @@ As of 3.2.0, the plugin is optional. The shipped npm plugin still contains the t
|
|
|
172
196
|
|
|
173
197
|
## Quick Install
|
|
174
198
|
|
|
175
|
-
|
|
199
|
+
### Choose Your Setup
|
|
200
|
+
|
|
201
|
+
| If you want to... | What you need to install | Go to section |
|
|
202
|
+
| :--- | :--- | :--- |
|
|
203
|
+
| Just stop the 400 error | Plugin only (The Seatbelt) | [Quick Install: Plugin](#quick-install-plugin) |
|
|
204
|
+
| Make the AI remember its thinking | Plugin + Proxy (The Real Fix) | [Full Setup: Proxy](#full-setup-proxy) |
|
|
205
|
+
| Run this automatically in background | Add the Watchdog | [Auto-Recovery](#auto-recovery) |
|
|
206
|
+
|
|
207
|
+
### Quick Install: Plugin
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm install opencode-thinking-fix
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Then add `"plugin": ["opencode-thinking-fix"]` to `opencode.json` and restart OpenCode.
|
|
214
|
+
|
|
215
|
+
> **The plugin is a seatbelt, not the real fix.** It keeps the field present so requests don't 400, but it does not replay the model's actual prior reasoning. To make the model remember its earlier thinking, see [Full Setup: Proxy](#full-setup-proxy). For the trade-offs, see [Plugin details](#plugin-details-the-seatbelt).
|
|
176
216
|
|
|
177
217
|
| Method | Command | Best for |
|
|
178
218
|
|--------|---------|----------|
|
|
@@ -180,11 +220,11 @@ As of 3.2.0, the plugin is optional. The shipped npm plugin still contains the t
|
|
|
180
220
|
| **CLI** | `opencode plugin opencode-thinking-fix` | Scripting |
|
|
181
221
|
| **Manual** | Add `"plugin": ["opencode-thinking-fix"]` to `opencode.json` | Version pinning |
|
|
182
222
|
|
|
183
|
-
|
|
223
|
+
#### Already installed? Update
|
|
184
224
|
|
|
185
225
|
Versions before 3.3 do not cover OpenRouter or the OpenCode Zen free-tier lane. Update inside OpenCode (`Ctrl+P` → install plugin → `opencode-thinking-fix`) or with `opencode plugin opencode-thinking-fix`, then restart. The plugin self-detects reasoning models, so updating is safe on non-reasoning setups too.
|
|
186
226
|
|
|
187
|
-
|
|
227
|
+
#### Method 1: TUI (press `Ctrl+P` while OpenCode is running)
|
|
188
228
|
|
|
189
229
|
1. Press `Ctrl+P` to open the command palette.
|
|
190
230
|
2. Type `install plugin` and press `Enter`.
|
|
@@ -194,7 +234,7 @@ Versions before 3.3 do not cover OpenRouter or the OpenCode Zen free-tier lane.
|
|
|
194
234
|
|
|
195
235
|
Check `~/.local/share/opencode/thinking-fix.log` for `plugin_loaded`. See [Is it working?](#is-it-working).
|
|
196
236
|
|
|
197
|
-
|
|
237
|
+
#### Method 2: CLI (shell command)
|
|
198
238
|
|
|
199
239
|
```bash
|
|
200
240
|
opencode plugin opencode-thinking-fix
|
|
@@ -208,7 +248,7 @@ opencode plugin opencode-thinking-fix@3.3.0
|
|
|
208
248
|
|
|
209
249
|
Restart OpenCode after installing.
|
|
210
250
|
|
|
211
|
-
|
|
251
|
+
#### Method 3: Manual config (add to `opencode.json`)
|
|
212
252
|
|
|
213
253
|
```json
|
|
214
254
|
{
|
|
@@ -228,51 +268,13 @@ See also: [OpenCode plugin docs](https://opencode.ai/docs/plugins)
|
|
|
228
268
|
|
|
229
269
|
---
|
|
230
270
|
|
|
231
|
-
|
|
232
|
-
- [See it happen](#see-it-happen)
|
|
233
|
-
- [Why you've probably never seen it](#why-youve-probably-never-seen-it)
|
|
234
|
-
- [The evidence](#the-evidence)
|
|
235
|
-
- [For the skeptics](#for-the-skeptics)
|
|
236
|
-
- [Option 1: Plugin (partial safety net)](#option-1-plugin-partial-safety-net)
|
|
237
|
-
- [Option 2: Proxy (replays real reasoning)](#option-2-proxy-replays-real-reasoning)
|
|
238
|
-
- [Option 3: Watchdog (auto-recovery)](#option-3-watchdog-auto-recovery)
|
|
239
|
-
- [How they work together](#how-they-work-together)
|
|
240
|
-
- [Affected models](#affected-models)
|
|
241
|
-
- [Model routing](#model-routing-proxy-port-3457)
|
|
242
|
-
- [Is it working?](#is-it-working)
|
|
243
|
-
- [This bug is everywhere](#this-bug-is-everywhere)
|
|
244
|
-
- [Files in this repo](#files-in-this-repo)
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
## What problem this fixes
|
|
271
|
+
### Plugin details (the seatbelt)
|
|
249
272
|
|
|
250
|
-
|
|
273
|
+
#### Install via npm (recommended)
|
|
251
274
|
|
|
252
|
-
|
|
275
|
+
See [Quick Install: Plugin](#quick-install-plugin) above, use OpenCode TUI (`Ctrl+P`) or CLI (`opencode plugin opencode-thinking-fix`).
|
|
253
276
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
DeepSeek V4 (and Kimi K2.7, GLM 5.x, MiMo V2.5) require that `reasoning_content` from every prior assistant turn gets included in subsequent API requests. The [docs](https://api-docs.deepseek.com/guides/thinking_mode) describe the contract, and the strictness varies by provider and endpoint: some reject the request, some ignore the field, and the conversation still loses its context either way. All five providers confirm the passback requirement in their official documentation:
|
|
257
|
-
|
|
258
|
-
- **DeepSeek**: "[The reasoning_content will be ignored by the API](https://api-docs.deepseek.com/guides/thinking_mode)", but the conversation history must contain the field.
|
|
259
|
-
- **Z.AI / GLM**: "[Key: return reasoning_content to keep the reasoning coherent](https://docs.z.ai/en/api/thinking)."
|
|
260
|
-
- **Kimi / Moonshot**: "[You must keep the reasoning_content in the multi-round conversation... otherwise an error will be thrown](https://platform.moonshot.ai/docs/guide/thinking-mode)."
|
|
261
|
-
- **MiniMax**: "[The complete model response must be append to the conversation history](https://platform.minimaxi.com/document/ChatCompletion%20v2)."
|
|
262
|
-
- **Xiaomi MiMo**: "[Any assistant message with tool calls... must preserve its full reasoning_content field, otherwise the API will return a 400 error](https://dev.mi.com/doc/llm-api). Affected frameworks include TRAE, Cursor, Roo Code, Codex, GitHub Copilot CLI, Zed, AutoGen."
|
|
263
|
-
|
|
264
|
-
The field is non-standard per OpenAI, so SDKs and clients ignore it — the drop is
|
|
265
|
-
systemic, not a single tool's bug. This repo fixes it. Three layers, pick what you need.
|
|
266
|
-
|
|
267
|
-
---
|
|
268
|
-
|
|
269
|
-
## Option 1: plugin (partial safety net)
|
|
270
|
-
|
|
271
|
-
### Install via npm (recommended)
|
|
272
|
-
|
|
273
|
-
See [Quick Install](#quick-install) above, use OpenCode TUI (`Ctrl+P`) or CLI (`opencode plugin opencode-thinking-fix`).
|
|
274
|
-
|
|
275
|
-
### Manual install (for local development)
|
|
277
|
+
#### Manual install (for local development)
|
|
276
278
|
|
|
277
279
|
Drop the plugin file in your OpenCode plugins directory and restart:
|
|
278
280
|
|
|
@@ -293,6 +295,8 @@ For the V2 API, add the package to `plugins` instead of `plugin`:
|
|
|
293
295
|
|
|
294
296
|
The same package supports both APIs from one default export. V2 reads `setup()`; V1 (>= 1.18.29) reads `server()`.
|
|
295
297
|
|
|
298
|
+
#### What the plugin does
|
|
299
|
+
|
|
296
300
|
It scans outgoing messages for any assistant turn that already has `reasoning_content`. If it finds one (meaning you are using a reasoning model), it adds `reasoning_content: ""` to every assistant turn missing it. If it finds nothing (Qwen, GPT, Claude, they never produce this field), it does nothing.
|
|
297
301
|
|
|
298
302
|
It also handles `reasoning` for the OpenCode Go provider, and patches empty `content` fields that OpenAI-compatible SDKs sometimes omit.
|
|
@@ -305,13 +309,15 @@ No config file changes. No build step. OpenCode compiles `.ts` plugins when it s
|
|
|
305
309
|
|
|
306
310
|
---
|
|
307
311
|
|
|
308
|
-
|
|
312
|
+
### Full Setup: Proxy
|
|
313
|
+
|
|
314
|
+
If you have not installed the plugin yet, start with [Quick Install: Plugin](#quick-install-plugin). The proxy is the part that puts the model's real reasoning back into the conversation.
|
|
309
315
|
|
|
310
316
|
A Node.js proxy that catches API responses as they come back, pulls out the actual `reasoning_content` text, and caches it in memory. On the next request, it injects that real text back into the conversation history instead of empty strings.
|
|
311
317
|
|
|
312
318
|
Your model sees its full chain-of-thought from turn 1 on every subsequent turn. The difference is noticeable on complex multi-turn coding sessions.
|
|
313
319
|
|
|
314
|
-
|
|
320
|
+
#### Two-proxy architecture
|
|
315
321
|
|
|
316
322
|
The proxy runs on **two ports**:
|
|
317
323
|
|
|
@@ -332,7 +338,7 @@ PORT=3458 UPSTREAM_URL=https://opencode.ai/zen/go/v1 node proxy/proxy.js
|
|
|
332
338
|
|
|
333
339
|
> **Windows PowerShell:** use `$env:PORT=3457; node proxy/proxy.js` (PowerShell) or `set PORT=3457 && node proxy/proxy.js` (CMD).
|
|
334
340
|
|
|
335
|
-
|
|
341
|
+
#### OpenCode Go setup (explicit and credentials-safe)
|
|
336
342
|
|
|
337
343
|
The npm package intentionally does **not** run a `postinstall` script. It never
|
|
338
344
|
reads `auth.json`, edits your OpenCode configuration, starts a process, or
|
|
@@ -370,7 +376,7 @@ configured upstream and does not persist the credential. Stop the proxy when
|
|
|
370
376
|
it is not needed. The systemd and watchdog options below are deliberately
|
|
371
377
|
opt-in alternatives for operators who want automatic restarts.
|
|
372
378
|
|
|
373
|
-
|
|
379
|
+
#### Install as systemd services (auto-start at boot)
|
|
374
380
|
|
|
375
381
|
```bash
|
|
376
382
|
mkdir -p ~/.config/systemd/user
|
|
@@ -408,9 +414,9 @@ One runtime dependency (`eventsource-parser`). The proxy uses Node.js built-in `
|
|
|
408
414
|
|
|
409
415
|
---
|
|
410
416
|
|
|
411
|
-
|
|
417
|
+
### Auto-Recovery
|
|
412
418
|
|
|
413
|
-
The watchdog script checks both proxy instances every 4 minutes and restarts any that are down:
|
|
419
|
+
The watchdog only keeps the proxy alive, so start the proxy first. The script checks both proxy instances every 4 minutes and restarts any that are down:
|
|
414
420
|
|
|
415
421
|
```bash
|
|
416
422
|
cp watchdog/watchdog.sh ~/reasoning-cache-proxy/
|
|
@@ -419,9 +425,21 @@ systemctl --user daemon-reload
|
|
|
419
425
|
systemctl --user enable --now reasoning-proxy-watchdog.service
|
|
420
426
|
```
|
|
421
427
|
|
|
428
|
+
#### Windows: NSSM or Task Scheduler
|
|
429
|
+
|
|
430
|
+
On Windows, NSSM (Non-Sucking Service Manager) or Task Scheduler can run the proxy or a PowerShell watchdog script in the background. A quick NSSM setup:
|
|
431
|
+
|
|
432
|
+
```powershell
|
|
433
|
+
nssm install ReasoningCacheProxy node.exe proxy\proxy.js
|
|
434
|
+
nssm set ReasoningCacheProxy AppEnvironmentExtra PORT=3457
|
|
435
|
+
nssm start ReasoningCacheProxy
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
See [Windows notes](#windows-notes) for the full setup.
|
|
439
|
+
|
|
422
440
|
---
|
|
423
441
|
|
|
424
|
-
|
|
442
|
+
### How They Work Together
|
|
425
443
|
|
|
426
444
|
```
|
|
427
445
|
OpenCode → [plugin patches missing reasoning_content/reasoning]
|
|
@@ -434,7 +452,7 @@ The plugin is partial insurance. The hard 400 is mostly gone from modern gateway
|
|
|
434
452
|
|
|
435
453
|
---
|
|
436
454
|
|
|
437
|
-
## Affected
|
|
455
|
+
## Affected Models
|
|
438
456
|
|
|
439
457
|
| Model | Plugin helps | Proxy helps | What it needs |
|
|
440
458
|
|---|---|---|---|
|
|
@@ -455,6 +473,10 @@ The plugin is partial insurance. The hard 400 is mostly gone from modern gateway
|
|
|
455
473
|
|
|
456
474
|
The proxy auto-routes by model name prefix. All routes:
|
|
457
475
|
|
|
476
|
+
<details>
|
|
477
|
+
<summary><b>Click to expand the full model routing table</b></summary>
|
|
478
|
+
<br>
|
|
479
|
+
|
|
458
480
|
| Prefix | Upstream | Reasoning |
|
|
459
481
|
|---|---|---|
|
|
460
482
|
| `deepseek-v4-flash`, `deepseek-v4-pro`, `deepseek-v4-flash-vision-exp` | `https://api.deepseek.com` | Yes (`reasoning_content`) |
|
|
@@ -473,6 +495,8 @@ The proxy auto-routes by model name prefix. All routes:
|
|
|
473
495
|
| `llama` | `https://api.together.xyz` | No |
|
|
474
496
|
| `mistral` | `https://api.mistral.ai` | No |
|
|
475
497
|
|
|
498
|
+
</details>
|
|
499
|
+
|
|
476
500
|
Unknown models fall back to `https://api.deepseek.com` with reasoning disabled.
|
|
477
501
|
|
|
478
502
|
### OpenRouter (port 3462)
|
|
@@ -495,11 +519,13 @@ PORT=3462 UPSTREAM_URL=https://openrouter.ai/api/v1 REASONING_KEY=reasoning_cont
|
|
|
495
519
|
}
|
|
496
520
|
```
|
|
497
521
|
|
|
498
|
-
The passive-tap corpus includes an OpenRouter lane: 22 session-confirmed drops, concentrated on `minimax/minimax-m3:free` within one long session — enough to show the pattern exists on OpenRouter, with the full model-by-model matrix left to a follow-up corpus. See [The
|
|
522
|
+
The passive-tap corpus includes an OpenRouter lane: 22 session-confirmed drops, concentrated on `minimax/minimax-m3:free` within one long session — enough to show the pattern exists on OpenRouter, with the full model-by-model matrix left to a follow-up corpus. See [The Evidence/Proof](#the-evidence--proof).
|
|
499
523
|
|
|
500
524
|
---
|
|
501
525
|
|
|
502
|
-
##
|
|
526
|
+
## Troubleshooting
|
|
527
|
+
|
|
528
|
+
### Is it working?
|
|
503
529
|
|
|
504
530
|
The plugin writes a structured JSON log: `~/.local/share/opencode/thinking-fix.log`.
|
|
505
531
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-thinking-fix",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Restores reasoning content dropped from multi-turn OpenCode conversations (DeepSeek, Kimi, GLM, MiMo, MiniMax, InclusionAI/Ling, OpenCode Go, OpenRouter). V1 + V2 plugin, proxy, and watchdog.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|