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.
Files changed (2) hide show
  1. package/README.md +86 -60
  2. 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
- > **Zero config.** Install via `Ctrl+P`, restart OpenCode, done. The plugin detects reasoning models and only patches when needed.
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
- ## The problem
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 evidence
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
- **This is an OpenCode plugin. Install it inside OpenCode, no terminal needed.**
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
- ### Already installed? Update
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
- ### Method 1: TUI (press `Ctrl+P` while OpenCode is running)
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
- ### Method 2: CLI (shell command)
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
- ### Method 3: Manual config (add to `opencode.json`)
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
- - [The problem](#the-problem)
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
- You ask DeepSeek a question. It picks a tool, calls it, works fine. Then you ask a follow-up — and the AI has lost the reasoning it produced on every earlier turn.
273
+ #### Install via npm (recommended)
251
274
 
252
- The client/SDK dropped the field before it reached the API — the providers are doing exactly what their docs say. The drop happens in SDK serializers and client transforms.
275
+ See [Quick Install: Plugin](#quick-install-plugin) above, use OpenCode TUI (`Ctrl+P`) or CLI (`opencode plugin opencode-thinking-fix`).
253
276
 
254
- > **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.
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
- ## Option 2: proxy (replays real reasoning)
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
- ### Two-proxy architecture
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
- ### OpenCode Go setup (explicit and credentials-safe)
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
- ### Install as systemd services (auto-start at boot)
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
- ## Option 3: watchdog (auto-recovery)
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
- ## How they work together
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 models
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 evidence](#the-evidence).
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
- ## Is it working?
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.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",