Merge PR #3776: Fix hidden thinking glyph-only status rows (@jeffscottward)
This commit is contained in:
@@ -169,6 +169,8 @@
|
||||
- Fixed the DuckDuckGo web_search provider returning empty results for non-encyclopedic queries by switching from the Instant Answer API to parsing the HTML frontend, and added clear error handling for bot-challenge throttling.
|
||||
- Fixed Windows --extension paths with spaces or \\?\ prefixes being truncated or incorrectly passed to Bun import/spawn APIs.
|
||||
- Fixed /mcp reauth compatibility with Cloudflare by aligning OAuth prompt behavior with the reference MCP SDK and updating the client label to oh-my-pi.
|
||||
- Fixed the `edit` tool persisting unbounded full-file `oldText` / `newText` snapshots in tool-result `details`, inflating per-turn session JSONL lines (hundreds of KB per edit on large files). `details.oldText`/`details.newText` are now pruned when their combined length exceeds 32 KB; the visible diff, path, line, and diagnostic metadata are preserved, and ACP `diff` content still flows for smaller edits. ([#3786](https://github.com/can1357/oh-my-pi/issues/3786))
|
||||
- Fixed hidden-thinking live status rows rendering as glyph-only lines by appending a persistent `Thinking` label next to the pulse.
|
||||
|
||||
## [16.2.5] - 2026-06-28
|
||||
|
||||
|
||||
@@ -329,17 +329,18 @@ export class AssistantMessageComponent extends Container {
|
||||
#thinkingDotsLabel(): string {
|
||||
const glyph = THINKING_DOTS_FRAMES[this.#thinkingDotsFrame % THINKING_DOTS_FRAMES.length] ?? "…";
|
||||
const coloredGlyph = theme.fg("thinkingText", glyph);
|
||||
const thinkingLabel = theme.fg("muted", " Thinking");
|
||||
const rate = Math.min(SPEED_MAX, sharedSpeedTracker.getSpeed());
|
||||
// The numeric badge ("<total> · <rate> toks/s") only renders while this block
|
||||
// is genuinely streaming provider tokens. A block that has observed no token
|
||||
// delta (e.g. a provider that reports usage only at turn end) or whose rate
|
||||
// has decayed to zero (a streaming lull) drops it entirely — the bare pulse
|
||||
// keeps signalling that the model is thinking. The liveness flag also stops
|
||||
// the session-wide gauge from leaking a previous turn's rate onto a fresh
|
||||
// token-less block.
|
||||
if (!this.#thinkingRateLive || rate < 0.05) return coloredGlyph;
|
||||
// has decayed to zero (a streaming lull) drops it entirely — the persistent
|
||||
// text label keeps the pulse descriptive for terminals and screen readers.
|
||||
// The liveness flag also stops the session-wide gauge from leaking a previous
|
||||
// turn's rate onto a fresh token-less block.
|
||||
if (!this.#thinkingRateLive || rate < 0.05) return coloredGlyph + thinkingLabel;
|
||||
// Total provider tokens, dimmed, sit next to the pulse.
|
||||
const totalSpan = this.#thinkingTokens > 0 ? theme.fg("dim", ` ${formatNumber(this.#thinkingTokens)}`) : "";
|
||||
const totalSpan = this.#thinkingTokens > 0 ? theme.fg("dim", ` · ${formatNumber(this.#thinkingTokens)}`) : "";
|
||||
// Speed badge color: dim gray at rest, brightening toward the theme accent as
|
||||
// streaming speed climbs (gray → bright accent). Ease (sqrt) so typical
|
||||
// mid-stream rates already read as clearly accent-tinted instead of staying
|
||||
@@ -348,7 +349,7 @@ export class AssistantMessageComponent extends Container {
|
||||
const hex = lerpHex(theme.getColorHex("dim"), theme.getAccentColorHex(), ratio);
|
||||
const rateText = ` · ${rate.toFixed(1)} toks/s`;
|
||||
const rateSpan = theme.getColorMode() === "truecolor" ? chalk.hex(hex)(rateText) : theme.fg("muted", rateText);
|
||||
return coloredGlyph + totalSpan + rateSpan;
|
||||
return coloredGlyph + thinkingLabel + totalSpan + rateSpan;
|
||||
}
|
||||
|
||||
#startThinkingAnimation(): void {
|
||||
|
||||
@@ -178,10 +178,13 @@ describe("AssistantMessageComponent streaming thinking pulse", () => {
|
||||
|
||||
// First frame of the expanding/shrinking ✻ pulse; deterministic right after updateContent.
|
||||
const PULSE = "✻";
|
||||
const THINKING_LABEL = "Thinking";
|
||||
const THINKING_GLYPH_ONLY_LINE = /^[✻✼❉❊✺✹✸✶]\s*$/;
|
||||
|
||||
it("shows the pulse in place of hidden reasoning while thinking streams", () => {
|
||||
it("shows a described pulse in place of hidden reasoning while thinking streams", () => {
|
||||
const lines = liveLines(streaming([{ type: "thinking", thinking: "private reasoning" }]));
|
||||
expect(lines.some(line => line.includes(PULSE))).toBe(true);
|
||||
expect(lines.some(line => line.includes(PULSE) && line.includes(THINKING_LABEL))).toBe(true);
|
||||
expect(lines.map(line => line.trim()).some(line => THINKING_GLYPH_ONLY_LINE.test(line))).toBe(false);
|
||||
expect(lines.some(line => line.includes("private reasoning"))).toBe(false);
|
||||
});
|
||||
|
||||
@@ -261,7 +264,7 @@ describe("AssistantMessageComponent streaming thinking pulse", () => {
|
||||
component.updateContent(streaming([{ type: "thinking", thinking: "ab" }], 57), { transient: true });
|
||||
|
||||
const plain = Bun.stripANSI(component.render(RENDER_WIDTH).join("\n"));
|
||||
// Layout: "<glyph> <total> · <rate> toks/s" — 57 provider tokens, 47.0 tok/s.
|
||||
// Layout: "<glyph> Thinking · <total> · <rate> toks/s" — 57 provider tokens, 47.0 tok/s.
|
||||
expect(plain).toContain("57 · 47.0 toks/s");
|
||||
|
||||
nowSpy.mockRestore();
|
||||
@@ -301,12 +304,14 @@ describe("AssistantMessageComponent streaming thinking pulse", () => {
|
||||
|
||||
// Long pause: rate observations age out of the window. A same-token update
|
||||
// refreshes the live label, which now drops the numeric badge entirely
|
||||
// rather than lingering on "0.0 toks/s" — only the bare pulse remains.
|
||||
// rather than lingering on "0.0 toks/s" while retaining descriptive text.
|
||||
mockTime = 30_000;
|
||||
component.updateContent(streaming([{ type: "thinking", thinking: "ab" }], 57), { transient: true });
|
||||
const plain = Bun.stripANSI(component.render(RENDER_WIDTH).join("\n"));
|
||||
expect(plain).not.toContain("toks/s");
|
||||
expect(plain).not.toContain("57");
|
||||
expect(plain.includes(PULSE)).toBe(true);
|
||||
expect(plain).toContain(THINKING_LABEL);
|
||||
|
||||
nowSpy.mockRestore();
|
||||
component.dispose();
|
||||
@@ -335,6 +340,7 @@ describe("AssistantMessageComponent streaming thinking pulse", () => {
|
||||
expect(plain).not.toContain("toks/s");
|
||||
expect(plain).not.toContain("99");
|
||||
expect(plain.includes(PULSE)).toBe(true);
|
||||
expect(plain).toContain(THINKING_LABEL);
|
||||
|
||||
nowSpy.mockRestore();
|
||||
b.dispose();
|
||||
|
||||
Reference in New Issue
Block a user