v0.5.0
⚠️ Known issue: the Windows portable.exebuilds in this release do not launch. Use thePI-Dashboard-win32-x64.zip/PI-Dashboard-win32-arm64.ziparchive instead — extract anywhere and runPI Dashboard.exe. The portable assets have been removed from this release.
Added
- Skill invocations in chat now render as collapsible cards; ↑ recalls the slash form, not the expanded body. When a user types
/skill:openspec-explore continue with X(or any other skill), the dashboard's bridge now wraps the expanded skill body in pi's own<skill name="..." location="...">body</skill>\n\nargsenvelope (byte-identical to pi's_expandSkillCommandoutput). The chat view detects this envelope and renders a distinct purple-tinted card with a wrench icon, the full slash form (/skill:openspec-explore continue with X) always visible in the header, and a body that's collapsed by default — click the chevron to expand the skill body and args. Four copy buttons: copy as Markdown (raw wrapper), copy as plain text (rendered body), copy as/skill:command (slash form to invoke again), and copy as message (just the user's typed args, hidden when the skill was invoked without args). Only the chevron icon is the expand/collapse toggle so the slash text remains mouse-selectable for native drag-copy. The chat-input ↑ history-recall now returns the slash form too, so users can re-invoke a skill without deleting thousands of characters first. The session sidebar's display name and search also see the condensed form becausefirstMessageis now condensed server-side before truncation. As a side effect, this aligns dashboard-typed skill invocations with pi-TUI's persisted format — single source of truth across both ingress paths. (change:render-skill-invocations-collapsibly) - Chat markdown now renders local-file images and LaTeX math. Agents can reference local screenshots inline as
or; the bridge inlines the bytes via a new streaming-safepi-asset:<hash>token + side-channelasset_registerWebSocket event so each unique image's bytes ride exactly once per session regardless of how manymessage_updatechunks repeat the token. Math expressions — inline$x = \beta$and display$$\sum_i^n i$$(block-level) — are typeset viaremark-math+rehype-katexwiththrowOnError:falseso half-formed mid-stream expressions render as a fallback rather than crashing the markdown view. SVG, PNG, JPEG, GIF, WebP, AVIF, and BMP are supported with caps of 5 MB per image and 20 MB of new bytes per message; oversized / unreadable / unsupported-type tokens render as a visible placeholder rather than a broken-image glyph. The dashboard server adds zero new HTTP routes — image bytes flow through the existing event stream pattern that Read-tool images already use. (change:chat-markdown-local-images-and-math)
Changed
-
Provider Authentication settings now mirror the full provider list pi knows about, instead of an 8-item curated subset. The dashboard server's hardcoded
OAUTH_PROVIDERSandAPI_KEY_PROVIDERSarrays have been replaced by a bridge-pushed catalogue: the bridge introspectsmodelRegistry.authStorage+modelRegistry.getProviderDisplayNameand sends a newproviders_listmessage alongside the existingmodels_list. The server caches the catalogue per pi process and uses it as the source forGET /api/provider-auth/status. Providers likedeepseek,fireworks,cerebras,mistral,kimi-coding,huggingface,google-vertex,amazon-bedrock, etc. are now manageable from Settings → Provider Authentication. The status response also surfacesenvVar(the env var pi-ai checks, e.g.OPENAI_API_KEY) andambient: truefor AWS profile / GCP ADC ambient credentials, withmaskedKey: "(ambient)". Extension-registered OAuth providers (added viapi.registerProvider({oauth: ...})from another extension) become visible automatically. Nopackage.jsonchanges; no new dependencies. (change:replace-hardcoded-provider-lists) -
Windows: NSIS installer removed. The NSIS setup wizard (
@felixrieseberg/electron-forge-maker-nsis) is dropped. Windows distribution is now ZIP (.zip) and portable.exe(7-Zip SFX viaelectron-builder --win portable— no NSIS dependency). Users previously using the NSIS installer should extract the.zipto any directory and runPI Dashboard.exedirectly. No system-level installation is required. (change:simplify-electron-bootstrap-derived-state) -
Electron Bootstrap via Derived State (Phase C —
simplify-electron-bootstrap-derived-state).- Replaced
~/.pi-dashboard/mode.jsonstartup-flag with per-launch capability probes andDASHBOARD_STARTERenv var. - Added
selectLaunchSource()resolver — five precedence-ordered sources:attach > devMonorepo > piExtension > npmGlobal > extracted. DASHBOARD_STARTERenv var now stamped on every server spawn ("Electron"|"Bridge"|"Standalone"); exposed via/api/health.starter.- Added
pidfield to/api/healthfor lifecycle ownership; Electron stops the server only whenstarter=Electron AND pid matches. - Server reads
~/.pi/dashboard/installable.jsonbefore binding; installs missing packages in bootstrap phase (no-op when file absent). - Added
bundle-extract.ts— version-marker-driven~/.pi-dashboard/extraction with survive-extract whitelist (node/,node-pending/,node-old/). - Added
POST /api/electron/reextractendpoint (Electron-only; 403 for other starters; 202 triggers Electron-side restart). - Config files (
*config*,mode.json,recommended-wizard.json,api-key.json) auto-migrated to~/.pi/dashboard/migrate/<timestamp>/on upgrade. LAUNCH_SOURCE_V2flag now defaults totrue; setLAUNCH_SOURCE_V2=falseto revert to the legacy mode.json-based flow (escape hatch only).- Note:
LAUNCH_SOURCE_V2flag will be removed in a follow-up change after Phase C ships without regressions.
- Replaced
Migration Notes (for users upgrading from v0.4.x)
~/.pi-dashboard/mode.jsonis no longer used; existing installs are auto-migrated on first launch.- Config files (
*config*,mode.json,recommended-wizard.json,api-key.json) are archived to~/.pi/dashboard/migrate/<timestamp>/on first V2 launch. ~/.pi-dashboard/is now managed exclusively by the Electron app; do not manually edit files there.installable.jsonlives at~/.pi/dashboard/installable.jsonand controls which packages are installed.- API key may need re-entry if it was stored in
api-key.json(check~/.pi/dashboard/migrate/for recovery). node/,node-pending/,node-old/under~/.pi-dashboard/are preserved across Electron version upgrades.
Fixed
- Newly-spawned sessions no longer get their session name overwritten with a UUID-shaped string when the agent reads/writes/CLI-references a UUID-named OpenSpec change directory. The activity detector (
packages/shared/src/openspec-activity-detector.ts) used a permissive[^\s"']+capture group for change-name extraction that accepted UUIDs, mixed-case slugs, underscored slugs, and pathologically-long tokens. The previous fix (fix-openspec-flag-rename-bug) only rejected--prefixed tokens (CLI flags) on the explicit premise that the detector is a stable single source — that premise didn't hold. Two layers of defense now: (a) the detector validates every captured token through the newisValidOpenSpecChangeSlughelper enforcing OpenSpec's own slug shape^[a-z][a-z0-9-]{0,63}$(lowercase, leading-letter, kebab-case, max 64 chars — mirrorsopenspec new changevalidation) on all three branches (Read, Write, Bash), and (b) the auto-attach branch inpackages/server/src/event-wiring.tsre-validatesdetected.changeNameagainst the same predicate before stampingopenspecChange/attachedProposal/nameor sendingrename_session, so a future detector regression cannot propagate junk to disk. Manual attach paths (browserattach_proposal, REST/api/session/:id/attach-proposal) intentionally bypass the slug check and accept any user-supplied name from the server-curated change list. (change:fix-uuid-rename-bug) - Browser-rendered terminals no longer render at half-height on the folder-terminals page. App.tsx kept a legacy keep-alive
<TerminalView>list mounted unconditionally for the long-removed/terminal/:idroute. Whenever the user opened/folder/<cwd>/terminals, that legacy list mounted a hidden<TerminalView>for every terminal in the global Map alongside the visible<TerminalView>rendered inside<TerminalsView>— two WebSockets per terminal id, twoAttachAddons writing to the same PTY, and twoFitAddoninstances racing to sendresizemessages. The hidden one (measuring adisplay:none0×0 container) won often enough to shrink the PTY to a near-zero geometry, while the visible xterm rendered the PTY's tiny output into the top half of its viewport. The fix removes the legacy keep-alive list, the/terminal/:idroute matcher, and the redirect effects (~50 LOC out ofApp.tsx);<TerminalsView>becomes the single owner of<TerminalView>mounting. As defense-in-depth,terminal-manager.tsnow ignores any inbound{type:"resize"}control message withcols < 2orrows < 2— a PTY at those dimensions is non-functional for every supported shell and no legitimate user intent maps there. As a bundled bonus, the auto-shutdown idle timer now factors in active terminals: a long-runningcargo buildortail -fin a terminal with no agent attached keeps the server alive, instead of being killed by idle-shutdown aftershutdownIdleSeconds. BREAKING (theoretical): direct navigation to/terminal/:idnow lands on the SPA catch-all (/); no in-tree code path used the route, no docs reference it, and folder-scoped/folder/:encodedCwd/terminalsis the canonical entry. (change:fix-terminal-half-height-dual-mount) - Sidebar no longer leaves a running session below the “Show N ended” divider after the dashboard server restarts. The browser used to merge incremental on-connect
session_added+sessions_reorderedmessages into stale state from the previous server lifetime; depending on bridge-reattach timing, an actually-running session could end up rendered under the ended divider until a manual page refresh. The server now emits a single atomicsessions_snapshotmessage on every browser WebSocket connect, and the client REPLACES (does not merge) itssessionsMap andsessionOrderMapfrom that payload, so stale ids are dropped atomically. BREAKING (browser protocol): older browser tabs from before this release that reconnect to a server with this change will see no sessions until refreshed; the legacy per-session bootstrap loop has been removed. Live updates after the snapshot continue to use the existing incremental messages. (change:fix-stale-sessions-on-reconnect)
First-launch unblocking (unsigned binaries)
The Windows installers and macOS DMGs are not yet code-signed / notarized.
Both OSes will block first-launch with a security warning. These are not
malware — the artifacts are the exact ones produced by
.github/workflows/publish.yml
against this tag. Pick whichever workaround fits your workflow.
Tracking: Authenticode signing → change windows-authenticode-signing;
macOS notarization → change macos-notarization (planned). This section
will shrink and eventually disappear as each lands.
Windows — SmartScreen warning
SmartScreen will show "Windows protected your PC" the first time you
run any .exe artifact (Setup, portable, or any .exe extracted from a
ZIP).
Option A — at the SmartScreen dialog:
- Click More info.
- Click Run anyway.
Option B — pre-clear the Mark-of-the-Web:
- Right-click the downloaded
.exe(or the.zip) → Properties. - At the bottom of the General tab, tick Unblock next to
"This file came from another computer...". - Click OK and run as normal.
For ZIP archives, unblock the archive itself before extracting so
the contained .exes inherit the cleared zone.
macOS — Gatekeeper / quarantine
macOS will refuse to launch the app on first run with "PI Dashboard
cannot be opened because the developer cannot be verified" or silently
quarantine it.
Option A — control-click the app:
- Open the DMG and drag PI Dashboard to Applications.
- In Applications, right-click (or Control-click) PI Dashboard →
Open. - Click Open in the confirmation dialog. Subsequent launches are
unrestricted.
Option B — clear the quarantine attribute from the terminal:
xattr -d com.apple.quarantine "/Applications/PI Dashboard.app"If the DMG itself is being blocked, clear it on the mounted volume
before copying:
xattr -d com.apple.quarantine "/Volumes/PI Dashboard/PI Dashboard.app"