Skip to content

fix(pi): keep extension status in one row per key - #760

Merged
hardbeat920 merged 1 commit into
hardbeat920:mainfrom
Erickzao:fix/pi-status-flood
Oct 6, 2026
Merged

hardbeat920 merged 1 commit into
hardbeat920:mainfrom
Erickzao:fix/pi-status-flood

Conversation

@Erickzao

@Erickzao Erickzao commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Before: a Pi extension that calls setStatus added a new transcript row on every call. Pi treats setStatus(key, text) as a footer slot it replaces each time, so an animated status like pi-caveman's spinner turned into a wall of caveman level: … rows. One 15-second turn with real Pi and pi-caveman left 37 of them.

After: each status key gets one row per turn. Later frames update that row in place, an empty status removes it, and the next turn starts a new row. notify and statuses without a key still append, as before.

How:

  • The status harness event takes an optional key. applyHarnessEvent updates the row with that key in the current turn, or appends one, and stores the key on the block as statusKey. Unkeyed statuses still go through appendStatus.
  • The Pi adapter reads statusKey from setStatus requests and sends keyed status events, including empty ones so a cleared slot disappears. The ANSI stripping is unchanged.
  • New tests cover frames that update one row, clearing, separate keys, a new row in the next turn, and the Pi adapter's keyed events. The Ponytail test now expects its status key.

Why

Fixes #743.

The flood came from mapping a replace-in-place UI call onto an append-only transcript. Keying the row keeps the latest status visible, the way Pi's footer shows it, without dropping extension status altogether.

UI

Before, one turn with pi-caveman's animated status, the status rows expanded:

before

After, the same turn keeps one row with the last frame:

after

The screenshots use real Pi 0.73.1 with pi-caveman 1.0.8 in a demo project, with a local mock model answering slowly so the status animates. The prompt is the one from the issue.

Checklist

  • I ran npm run check. It passes in CI on macOS, Ubuntu, and Windows: 4231 web tests, 5 of them new, tsc, cargo fmt, clippy, and 473 to 539 Rust tests depending on the platform. Locally, the harness tests and tsc pass.
  • This PR is small and focused
  • I did not mix unrelated changes

Tested on Windows 11 in the app, before and after the change, with the setup above. macOS and Linux were only checked by CI.

Summary by CodeRabbit

  • New Features
    • Status messages with the same key now update in place, keeping their existing position instead of adding duplicate rows. Different keys remain separate, and updates in later turns create new rows.
    • Sending a blank status clears an existing keyed message; if no matching message exists, nothing is added. Unkeyed status messages continue to appear as before.
    • Animated status updates now display without ANSI formatting.

Pi extensions set footer status with setStatus(key, text), and Pi
replaces that slot on every call. MonoCode appended each call as a new
transcript row, so pi-caveman's animated status added a row per frame,
37 in one 15-second turn.

A status event can now carry a key. A keyed status updates its row in
the current turn, an empty text removes it, and the next turn starts a
new row. The Pi adapter keys setStatus by its statusKey; notify and
unkeyed status rows behave as before.
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 73ee22b2-6170-47bc-8091-279378e8211b
📥 Commits

Reviewing files that changed from the base of the PR and between 807c70e and e158022.

📒 Files selected for processing (7)
  • src/features/sessions/model/session.ts
  • src/integrations/harness/core/apply.test.ts
  • src/integrations/harness/core/apply.ts
  • src/integrations/harness/core/types.ts
  • src/integrations/harness/providers/pi/piFamily.ts
  • src/integrations/harness/providers/pi/piLive.test.ts
  • src/integrations/harness/providers/pi/piProtocol.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Pi extension status requests can include a key. The harness forwards that key and updates the matching status row after the latest user block. Unkeyed status events continue to append status rows.

Changes

Keyed status updates

Layer / File(s) Summary
Status key contracts
src/features/sessions/model/session.ts, src/integrations/harness/core/types.ts, src/integrations/harness/providers/pi/piProtocol.ts
Block and harness status events gain optional keys. The Pi request parser reads a nonempty statusKey for setStatus requests.
Pi status event forwarding
src/integrations/harness/providers/pi/piFamily.ts, src/integrations/harness/providers/pi/piLive.test.ts
No-reply setStatus requests with a key emit keyed status events, including when the text is blank. Tests cover keyed status updates and ANSI formatting removal.
Keyed session status updates
src/integrations/harness/core/apply.ts, src/integrations/harness/core/apply.test.ts
Keyed events update a matching row after the latest user block, leave identical text unchanged, and remove the row when text is blank. Tests cover row identity and position, distinct keys, and later turns.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant PiExtensionUiRequest
  participant parseExtensionUiRequest
  participant handleExtensionUi
  participant applyHarnessEvent
  PiExtensionUiRequest->>parseExtensionUiRequest: setStatus request with statusKey
  parseExtensionUiRequest->>handleExtensionUi: parsed request with statusKey
  handleExtensionUi->>applyHarnessEvent: keyed status event
  applyHarnessEvent->>applyHarnessEvent: update matching row after latest user block
Loading

Suggested reviewers: hardbeat920

Merge Risk: ⚪ Minimal · up to e1580

The status-row change appears ready to merge after normal checks; no actionable risk remains identified.

Security Architecture Review

Security architecture risk: 🔵 Low · up to e1580

The inspected change affects displayed extension statuses, not permissions or tool access. No introduced security issue was established, but status identity after recovery and across late updates is not fully preserved or established.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — An actor able to emit Pi extension UI records controls status text and slot keys. In the inspected flow, the new mutation authority is bounded to matching keyed rows after the latest user block in the session selected by the application callback. No cross-session, credential, or privileged-action path was established.

Security Findings and Attack Paths

  • inferred — The inspected key flow does not establish an introduced approval bypass: keys select status mutation, while approval and question events retain separate handling. This bounded conclusion does not establish complete security coverage.

Trust Boundaries and Controls

  • observed — Application turn callbacks reject obsolete generations, and Pi serializes submitted turns. These controls constrain stale delivery, but status events themselves have no originating turn identity and the Pi live callback is replaceable; they do not prove correct attribution for every delayed-frame interleaving.

Resilience and Maintainability Implications

  • observed — Noninteractive extension statuses already bypassed the adapter's cancellation check at the PR base. Head preserves that ordering but adds keyed replacement and clearing. A late-frame ownership ambiguity therefore remains, without evidence that it can change approval decisions, authentication state, or other security controls.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue [#743] asks Pi extension status updates to stop creating repeated chat rows. The Pi adapter parses statusKey from setStatus requests and emits keyed status events, including empty updates. `…
Out of Scope Changes check ✅ Passed All changes support [#743]. The event and block types carry the status key, the Pi adapter forwards it, and the harness implementation and tests cover the requested replacement behavior. No unrelated …
Title check ✅ Passed The title clearly and concisely describes the main change: keeping Pi extension status in one transcript row per key.
Description check ✅ Passed The description covers what changed, why, UI impact with before-and-after screenshots, and the checklist. It also explains the behavior for keyed, unkeyed, and empty statuses and reports testing.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Erickzao Erickzao changed the title Keep Pi extension status in one row per key fix(pi): keep extension status in one row per key Oct 5, 2026
@hardbeat920
hardbeat920 merged commit b74e803 into hardbeat920:main Oct 6, 2026
10 checks passed
@hardbeat920

Copy link
Copy Markdown
Owner

@Erickzao thank you. Looks good to me!

yyy0107 pushed a commit to yyy0107/ohmymonocode that referenced this pull request Oct 6, 2026
Pi extensions set footer status with setStatus(key, text), and Pi
replaces that slot on every call. MonoCode appended each call as a new
transcript row, so pi-caveman's animated status added a row per frame,
37 in one 15-second turn.

A status event can now carry a key. A keyed status updates its row in
the current turn, an empty text removes it, and the next turn starts a
new row. The Pi adapter keys setStatus by its statusKey; notify and
unkeyed status rows behave as before.

(cherry picked from commit b74e803)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pi Agent status updates flood the chat with repeated messages

2 participants