The papercuts I fixed in my own terminal agent this month
A scroll bug, a speed meter that read 232,000 tok/s, a compaction setting I changed four times in two days, and one hotkey too many. Small fixes from using pi every day.
I use pi, a terminal coding agent, every day, and I keep its whole configuration in a git repo. That turns out to be a good diary. When something annoyed me in mid-September, I typed a complaint into the agent, the agent fixed its own config, and a commit usually landed with my complaint quoted at the top.
None of these are interesting bugs. They're the kind you stop noticing after a week and then lose ten minutes a day to forever. Here are five from the four days between September 13 and 16.
I couldn't scroll
On September 15 I typed "i cant scroll pi why is it" into a session, which is about as precise as my bug reports get.
The scrolling story that day is two commits. At 12:31 I installed pi-claude-style-scroll, which pins the input box at the bottom and lets the message history scroll above it, the way Claude Code does. The commit notes that its declared peer range stopped at pi 0.80 while I was running 0.85.1, and that it applies deep patches to the TUI internals. The agent checked every patched internal still existed and called it fine.
At 13:26 I removed it and set "tuiMode": "fullscreen" instead, which uses pi's own scrolling. Fifty-five minutes, start to finish, to end up on a setting pi already had.
The speed meter was lying
I have a small extension that prints tokens per second in the footer. On the 13th it said this after a short reply:
232000 tok/s · 232 tok
The response had come back in a single processing tick, so the extension divided 232 real tokens by roughly one millisecond. There was a second bug hiding behind it: the turn-start timestamp leaked across turns, so one test case reported 50 tokens at 0.2 tok/s, timed against an interval minutes old. The fix measures from the first streamed token and simply prints the token count when less than 250 milliseconds is measurable.
Two days later it lied the other way. My report was "264 tok/s · 329 tok" plus a note about a wrong spike at the end when the response stops. The rolling four-second window used for the live estimate was also being used as the start of the whole measurement. A real 2819-token response, 19.8 seconds long, showed 705.5 tok/s at the end. The true rate was 142.5. After the fix the replay came out at 142 against a true 142.4.
The live estimate still reads about 20% high, because it assumes 4 characters per token and this model averages 4.92. I left that one. The final number is exact, and that was good enough for the commit.
Compaction, four times in two days
This one is embarrassing in sequence.
- Sep 14, 11:31. I asked why pi compacted every 100k tokens. It did, regardless of model, which meant summarizing a 1M-token window at 10%. The fix was a window-aware policy that triggers at 65% of the usable window.
- Sep 14, 16:22. Compaction was stopping runs dead. The extension called pi's manual compaction, which starts by aborting the in-flight run. Fixed by sending a continuation message after compacting a run that still had tool work coming.
- Sep 14, 21:35. I told it I didn't want it to compact at all. Off.
- Sep 15, 23:40. "reduce so it compacts earlier", then "make it 30%".
That last request turned up a nice detail. The smart policy checked the global compaction switch before doing anything, so after I turned compaction off, its 65% trigger was dead config. Lowering it would have changed nothing. The agent re-enabled compaction, set the trigger to 30%, and also had to lower a second constant, the minimum share of context a compaction has to free, from 20% to 10%. At a 30% trigger on a 128k model, a compaction only frees about 12%, so the old floor would have quietly disabled the policy on every small-window model. On a 128k window it now fires at 33.5k tokens instead of 73k.
The 30% was picked by feel. The derivation in the commit was written after I had already named the number.
One key is enough
On the morning of the 16th I added a way to nudge a stalled run: a clickable "continue" label, an Alt+C hotkey, and then, six minutes later, Enter on an empty prompt sends "continue".
Later that day I typed "the enter to continue, don't need hotkey alt c and no need for mouse clickable button". The empty prompt was already the affordance. The label cost a screen row and forced mouse capture just so it could be clicked, and the hotkey duplicated a key I was pressing anyway. The extension got renamed from continue-button to continue-on-enter, because the old name now described something that did not exist.
Half an hour later, one more fix. A stray Enter while the spinner was still going queued a "continue" onto the end of the running task, so impatient tapping stacked duplicate nudges. Now Enter does nothing while a run is in flight, and /continue refuses with a warning, since a typed command going silent would look like a bug.
Asking for the highest thinking level
On the 13th I asked "is high the highest thinking for 4.1 flash". The answer turned out to depend on who serves the model.
pi only sends the extended levels, xhigh and max, if the model's catalog entry declares them. The copy of DeepSeek V4.1 Flash on my subscription provider declares none. Asking it for xhigh or max both went out as high, with no error. On OpenRouter the same model declares xhigh and leaves max null, so xhigh is the ceiling there.
On the 16th I asked for the highest level for that model, and the config now pins xhigh explicitly on OpenRouter. The commit's reasoning is the best line in this whole list: a level that silently clamps would read as if it had worked.