- Introduce CleanseBoardModel and clean up status board rendering logic.
- Decouple CLI and interactive modes with CleanseRunUi interface and core runner.
- Add CleansePanelComponent and controller to manage interactive cleanse overlay.
- Register builtin `/cleanse` slash command and integrate into interactive mode.
- Implemented an animated collapse effect for the interactive mode todo header bar when tasks settle.
- Added timer management and render loop updates for the todo bar collapse transition.
- Replaced the stage-position counter in the todo HUD header with a summed progress bar counting closed and total tasks across all stages.
- Added a trailing overflow summary row to announce hidden stages past the collapsed cap.
- Introduced theme symbol support for filled and empty progress bar glyphs across unicode, nerd, and ascii presets.
- Add `setPromptDropped` hook and `DroppedPrompt` type to return prompts that were cancelled during turn setup before dispatch.
- Restore dropped prompt text and image attachments to the interactive mode editor and remove the optimistic transcript row.
- Replace newline characters with visual indicators in subagent HUD descriptions and task previews.
- Add unit tests verifying multiline description and task rendering.
InputController.#invokeSkillCommand cleared the draft and awaited the full
promptCustomMessage dispatch with no transcript render. AgentSession.#promptWithMessage
runs awaited preflight (memory recall, before_agent_start hooks, auto-thinking
classification, pre-prompt compaction) before the message reaches the agent, so a slow
step such as a Hindsight auto-recall timeout left the composer cleared with no pending
row, unlike a normal prompt's optimistic row from startPendingSubmission.
Idle skill submissions now paint an optimistic skill row before the awaited dispatch;
the canonical message_start reconciles it in place via EventController instead of
appending a duplicate. Streaming submissions still queue and show their chip.
Fixes#8895
- Exported stopSharedSpinnerTicker() and wired it into InteractiveMode.stop(): a live block missed by per-component stopAnimation kept the shared 80ms interval alive as a lingering event-loop handle; the spinner suite uses it to observe a freshly armed ticker instead of one leaked by earlier files
- Title-disposal tests now save/clear/restore PI_NO_TITLE (main() in ACP/RPC mode sets it process-wide), matching the prewarm and orphan-submit precedent
Persistence is lazy: getSessionFile() returns an allocated path from the
start, but the JSONL is only materialized once an assistant message (or an
explicit ensureOnDisk()) crosses the persistence gate. The shutdown banner
printed the resume hint based solely on the path, so any session that ended
before the first assistant message advertised a copy-pasteable command that
always fails with Session not found.
Gate the hint on the new SessionManager#isSessionOnDisk() (file exists in
the active storage backend) and add unit tests.
Fixes#8860
The first HUD commit hid Name: Name. The cause was earlier: task
name was copied into identity.label, which became progress.description
and skipped generateTaskLabel. Keep the handle for id allocation, but
only treat eval label as a real UI description so the tiny-model
summary can run.
The anchored Subagents list printed only `Id: description` and treated a
label that repeated the spawn handle as a real description. Show the same
⟨role⟩ badge as inline task rows and omit descriptions that only echo the id.
Rolled partial file appends back to their pre-write size and marked malformed resumed sessions for an atomic rewrite.
Retried transient persistence failures from in-memory state and surfaced the first failure in the interactive TUI.
Fixes#8596
- Replaced time-based sleeps and polling loops with event-driven promise resolvers and fake timers across agent and tool tests.
- Migrated test suites to share in-memory auth storage and fixtures using lifecycle hooks.
- Updated catalog model definitions, metadata, and configurations.
- Stage transcript initialization inside a detached TranscriptContainer to keep existing messages visible during incremental rendering.
- Add fallback state restoration in InteractiveMode.renderInitialMessages when chat rendering is aborted or fails.
- Update render-initial-messages tests to assert that old transcripts remain visible until replacements are fully committed.
setModel() invalidates the render cache but doesn't schedule a paint.
The initial forced startup paint (line 1104) may have already rendered
the old model, so the catch-up #updateWelcomeModel() must explicitly
request a render — matching #updateWelcomeLspServers().
Address review feedback: init-time model switches (#reconcileModeFromSession,
#enterPlanMode for plan.defaultOnStartup) fire model_changed before the
subscription exists, leaving the banner stale for those cases too.
Adds a catch-up #updateWelcomeModel() call right after the subscription is
installed, mirroring how the sessionName listener is registered before
those same init steps (line 1094-1095 comment).
Also adds a regression test for the catch-up path and comments tying the
component test to the wiring gap it covers.
The welcome banner (WelcomeComponent) captures the session's active
model name at init time but never receives updates when the model
changes. Its setModel() method already existed but was never called
after construction.
This wires the model_changed agent session event to a new
#updateWelcomeModel() method in InteractiveMode, mirroring the
existing #updateWelcomeLspServers() pattern. The banner now reflects
the live model after:
- delayed config/modelRoles load (the startup race where an
alphabetically-first provider is picked before config resolves)
- explicit /model switches
- retry-fallback model swaps
abort()'s stranded-queue drain could restart a queued user turn on the still-live Vibe tools before teardown removed them. Run the exit teardown with the queued-message drain suppressed, then re-arm it so the queued turn resumes as a clean non-Vibe turn.
Fixes#8326
Abort an in-flight model turn before unregistering Vibe tools so it cannot emit stale worker calls after cancellation.
Add a regression test covering the live streaming transition.
Fixes#8326
/handoff mints a fresh session via newSession(), producing a new
artifactsDir and an empty local/ root. The handoff document routinely
references plans and scratch files under '/data/workspaces/can1357__oh-my-pi__8261/.omp-session/2026-08-11T16-39-09-489Z_019ff1b1-31b1-7000-81f5-c540f4ebf43d/local/,' so every reference
became a dangling pointer in the new session. The plan approve-and-execute
path already copies artifacts across the boundary; handoff did not.
Extracted the plan-approve copy helper into a shared copyLocalArtifacts()
in local-protocol.ts and invoke it across the handoff session switch
(best-effort, since the switch is already committed).
Fixes#8261
showHookCustom hardcoded the overlay geometry and never read
overlayOptions/onHandle, which regressed the v0.45.6 API (PR
badlogic/pi-mono#667). Forward overlayOptions to showOverlay (keeping the
full-cover defaults as fallback), invoke onHandle with the returned
OverlayHandle, widen the options type via a shared ExtensionCustomOptions,
and re-export OverlayHandle/OverlayOptions from the extension API.