From 0b700de42be8376a83de595c9d25f10f8b8084ae Mon Sep 17 00:00:00 2001 From: PythonTilk Date: Tue, 6 Oct 2026 11:40:55 +0200 Subject: [PATCH] fix(windows): the step ticker no longer stops at a session's twentieth step The ticker found new steps by their index in task.steps. That list is capped at 20: once it is full, every new step shifts the oldest out and the index stays at 19, so the ticker saw nothing new and showed steps 18 and 19 for the rest of the session, while the header went on reading 20/20. Tasks now carry stepSeq, the position of the newest step in the whole session, which keeps counting past the cap. The ticker scrolls by that, and takes the texts from the end of the list. Co-Authored-By: Claude Opus 5.5 --- windows/src/core/state.ts | 7 +++++++ windows/src/island/hooks.ts | 1 + windows/src/views/ticker.ts | 34 +++++++++++++++------------------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/windows/src/core/state.ts b/windows/src/core/state.ts index 01236b80a9..71b4f59cb3 100644 --- a/windows/src/core/state.ts +++ b/windows/src/core/state.ts @@ -13,6 +13,11 @@ export interface AgentTask { state: BotStateName; stepIndex: number; steps: string[]; + /** + * Position of the newest step in the whole session. `steps` is capped, so + * `stepIndex` stops moving once it is full; this keeps counting. + */ + stepSeq?: number; source: AgentSource; isIntegration: boolean; emote?: BotEmoteName | null; @@ -186,9 +191,11 @@ class AppState { appendStep(id: string, step: string) { const t = this.tasks.find((x) => x.id === id); if (!t) return; + const newest = t.stepSeq ?? t.steps.length - 1; t.steps.push(step); if (t.steps.length > 20) t.steps.shift(); t.stepIndex = t.steps.length - 1; + t.stepSeq = newest + 1; this.notify(); } diff --git a/windows/src/island/hooks.ts b/windows/src/island/hooks.ts index d90a78f099..246c02ea30 100644 --- a/windows/src/island/hooks.ts +++ b/windows/src/island/hooks.ts @@ -132,6 +132,7 @@ function clearSession() { if (!t) return; t.steps = []; t.stepIndex = 0; + delete t.stepSeq; t.name = "VS Code"; t.pillBadge = null; } diff --git a/windows/src/views/ticker.ts b/windows/src/views/ticker.ts index 72b18023ef..6d03f2cdb2 100644 --- a/windows/src/views/ticker.ts +++ b/windows/src/views/ticker.ts @@ -96,31 +96,27 @@ export class Ticker { } sync(task: AgentTask | null) { - const steps = task && task.steps.length > 0 ? task.steps : ["…"]; - const idx = task ? Math.min(task.stepIndex, steps.length - 1) : -1; - - // First render: drop straight into place, no animation. - if (this.displayIndex < 0) { - this.displayIndex = idx; - setText(this.a, idx > 0 ? steps[idx - 1] : "…"); - setText(this.b, steps[Math.max(idx, 0)]); - this.rest(); - return; - } - - // The session restarted (steps were cleared): re-seed rather than scroll. - if (idx < this.displayIndex) { + const steps = task?.steps ?? []; + // Where the newest step sits in the whole session, not in `steps`: that + // list is capped at 20, and counting inside it made the ticker stop for + // good at the twentieth step of a session. + const newest = steps.length === 0 ? -1 : (task?.stepSeq ?? steps.length - 1); + + // First render, or the session restarted (steps were cleared): drop + // straight into place rather than scroll. + if (this.displayIndex < 0 || newest < this.displayIndex) { this.queue = []; this.startMs = null; - this.displayIndex = idx; - setText(this.a, idx > 0 ? steps[idx - 1] : "…"); - setText(this.b, steps[Math.max(idx, 0)]); + this.displayIndex = newest; + setText(this.a, steps.at(-2) ?? "…"); + setText(this.b, steps.at(-1) ?? "…"); this.rest(); return; } - for (let i = this.displayIndex + 1; i <= idx; i++) this.queue.push(steps[i]); - this.displayIndex = idx; + const fresh = Math.min(newest - this.displayIndex, steps.length); + if (fresh > 0) this.queue.push(...steps.slice(-fresh)); + this.displayIndex = newest; if (this.queue.length > MAX_QUEUE) { this.queue = this.queue.slice(-MAX_QUEUE); }