opencode-thinking-fix 3.3.2 → 3.3.4
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 +28 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,15 +28,27 @@ The drop happens in SDK serializers and client transforms (truthy checks, missin
|
|
|
28
28
|
|
|
29
29
|
**With the fix:** "Now add password reset." → AI reads its notes: "I used React Hook Form for login, I'll extend that." → works.
|
|
30
30
|
|
|
31
|
-
## See it happen
|
|
31
|
+
## See it happen
|
|
32
32
|
|
|
33
|
-
You need
|
|
33
|
+
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.
|
|
34
34
|
|
|
35
|
-
1.
|
|
36
|
-
2. Ask a follow-up that depends on the first answer, then capture the outgoing request. Every `reasoning_content` field from turn 1 arrives empty or missing.
|
|
37
|
-
3. The same request through the SDK alone keeps the field. The client is the difference.
|
|
35
|
+
### 1. The 4-step reproduction
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
1. Start a session with a reasoning model and give it a task that requires a tool call ("List the files in this directory, then read the largest one").
|
|
38
|
+
2. The model thinks, emits `reasoning_content`, and calls the tool.
|
|
39
|
+
3. Ask a follow-up that depends on the first answer ("Now summarize what you just read").
|
|
40
|
+
4. Capture the outgoing Turn-2 request. Every `reasoning_content` field from Turn 1 is missing or empty. The client dropped it.
|
|
41
|
+
|
|
42
|
+
### 2. The dataset (the actual proof)
|
|
43
|
+
|
|
44
|
+
If you don't want to run it yourself, the `proof/` directory contains the raw data from a week-long passive tap of real production traffic, byte-for-byte unmodified across DeepSeek, OpenCode Go, OpenCode Zen, and OpenRouter:
|
|
45
|
+
|
|
46
|
+
- **1,968 drops captured** (turns that carried reasoning earlier in the session arrived at the API without it).
|
|
47
|
+
- **2.29 million characters** of reasoning erased.
|
|
48
|
+
- **22 of those drops were on OpenRouter** — proving it is not a direct-provider quirk.
|
|
49
|
+
- **68% of drops happened with zero model change** (same model, same session, drop mid-conversation).
|
|
50
|
+
|
|
51
|
+
Every number is recomputable from `proof/reasoning-drop/drops.csv`. The A/B controls isolating the client as the sole point of failure live in `proof/free-models/`. The headline is the dataset; the recipe is a courtesy.
|
|
40
52
|
|
|
41
53
|
## Why you've probably never seen it
|
|
42
54
|
|
|
@@ -50,15 +62,10 @@ If you run long sessions, heavy tool calls, model switching, or subagents, you h
|
|
|
50
62
|
|
|
51
63
|
## The evidence
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
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/).
|
|
54
66
|
|
|
55
|
-
- **1,968 drops captured** — a turn that carried reasoning earlier in the same session arrives at the API without it.
|
|
56
|
-
- **2.29 million characters** of reasoning erased over a week of real traffic.
|
|
57
|
-
- **68% of drops happened with zero model change** — same model, same session. Not a model-switch artifact.
|
|
58
|
-
- **OpenRouter drops it too** — 22 drops with confirmed session identity on models served through openrouter.ai, plus the same pattern on DeepSeek and OpenCode Go lanes.
|
|
59
67
|
- **The SDK is innocent.** `@ai-sdk/openai-compatible` round-trips the field correctly; the drop happens in the client's own transform. Captured side by side in [`proof/free-models/`](proof/free-models/).
|
|
60
|
-
|
|
61
|
-
Every number is recomputable from [`proof/reasoning-drop/drops.csv`](proof/reasoning-drop/drops.csv) and [`proof/reasoning-drop/switches.csv`](proof/reasoning-drop/switches.csv).
|
|
68
|
+
- **Model-switch baseline** is in [`proof/reasoning-drop/switches.csv`](proof/reasoning-drop/switches.csv) so the 68% zero-model-change number is auditable independently of the drops file.
|
|
62
69
|
|
|
63
70
|
## For the skeptics
|
|
64
71
|
|
|
@@ -90,7 +97,7 @@ What this repo does about it:
|
|
|
90
97
|
- **The proxy** is a smarter middle-man that sits between OpenCode and the teacher. It **reads the real sticky note off each answer, remembers it, and pastes the actual writing back** into the next message. Now the teacher remembers, and your friend stays sharp. (v3 rewrote the proxy so it does this with almost zero slowdown — see below.)
|
|
91
98
|
- **The watchdog** is just a babysitter that restarts the proxy if it ever crashes.
|
|
92
99
|
|
|
93
|
-
Bottom line:
|
|
100
|
+
Bottom line: the proxy is what makes the AI actually remember its thinking. The plugin is a seatbelt for the rare hard 400 that still slips through. The watchdog keeps the lights on.
|
|
94
101
|
|
|
95
102
|
---
|
|
96
103
|
|
|
@@ -195,11 +202,11 @@ See also: [OpenCode plugin docs](https://opencode.ai/docs/plugins)
|
|
|
195
202
|
---
|
|
196
203
|
|
|
197
204
|
- [The problem](#the-problem)
|
|
198
|
-
- [See it happen
|
|
205
|
+
- [See it happen](#see-it-happen)
|
|
199
206
|
- [Why you've probably never seen it](#why-youve-probably-never-seen-it)
|
|
200
207
|
- [The evidence](#the-evidence)
|
|
201
208
|
- [For the skeptics](#for-the-skeptics)
|
|
202
|
-
- [Option 1: Plugin (safety net)](#option-1-plugin-safety-net)
|
|
209
|
+
- [Option 1: Plugin (partial safety net)](#option-1-plugin-partial-safety-net)
|
|
203
210
|
- [Option 2: Proxy (replays real reasoning)](#option-2-proxy-replays-real-reasoning)
|
|
204
211
|
- [Option 3: Watchdog (auto-recovery)](#option-3-watchdog-auto-recovery)
|
|
205
212
|
- [How they work together](#how-they-work-together)
|
|
@@ -232,7 +239,7 @@ systemic, not a single tool's bug. This repo fixes it. Three layers, pick what y
|
|
|
232
239
|
|
|
233
240
|
---
|
|
234
241
|
|
|
235
|
-
## Option 1: plugin (safety net)
|
|
242
|
+
## Option 1: plugin (partial safety net)
|
|
236
243
|
|
|
237
244
|
### Install via npm (recommended)
|
|
238
245
|
|
|
@@ -253,7 +260,9 @@ It also handles `reasoning` for the OpenCode Go provider, and patches empty `con
|
|
|
253
260
|
|
|
254
261
|
No config file changes. No build step. OpenCode compiles `.ts` plugins when it starts.
|
|
255
262
|
|
|
256
|
-
**
|
|
263
|
+
**Why keep it when the 400 is mostly resolved?** The hard 400 no longer fires on most gateways, but it still happens once in a while: on native DeepSeek tool-call turns, on present-required models like Kimi K2.7 Code, and whenever the proxy is down mid-session. Keeping the field present costs nothing and removes that failure class entirely, so it stays as insurance rather than the main fix.
|
|
264
|
+
|
|
265
|
+
**The catch:** the plugin fills in empty strings, not your model's actual prior thinking. DeepSeek, Kimi K2.5/K2.6, GLM, and MiMo accept empty strings fine, your conversation works but the model does not see its earlier reasoning. Kimi K2.7 Code requires the field to be present; present-but-empty is accepted. On native DeepSeek tool-call turns even a present-but-empty field is treated as missing, so the proxy's real cached text is what satisfies that contract.
|
|
257
266
|
|
|
258
267
|
---
|
|
259
268
|
|
|
@@ -382,7 +391,7 @@ OpenCode → [plugin patches missing reasoning_content/reasoning]
|
|
|
382
391
|
→ API
|
|
383
392
|
```
|
|
384
393
|
|
|
385
|
-
The plugin is
|
|
394
|
+
The plugin is partial insurance. The hard 400 is mostly gone from modern gateways, but it still fires once in a while — native DeepSeek tool-call turns, present-required models, or a proxy that is down mid-session. When that happens the plugin keeps the field present so the request survives; on empty-tolerant gateways the empty string is accepted, and on stricter ones the proxy's real cached text takes over because the plugin sees the field is already filled in. The proxy is the actual fix. The plugin is the seatbelt you hope never to need.
|
|
386
395
|
|
|
387
396
|
---
|
|
388
397
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-thinking-fix",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.4",
|
|
4
4
|
"description": "Restores reasoning content dropped from multi-turn OpenCode conversations (DeepSeek, Kimi, GLM, MiMo, MiniMax, OpenCode Go, OpenRouter). Plugin + proxy + watchdog.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|