* fix(daemon): emit tool_use from tool_execution_start in pi-rpc
The pi-rpc adapter emitted tool_use from message_end, which fires
before tool execution starts. The web UI pairs tool results to prior
tool_use events, so receiving tool_result without a preceding tool_use
broke tool card rendering and file auto-open behavior.
Move tool_use emission to tool_execution_start, matching the pattern
in copilot-stream.ts. Remove redundant tool call extraction from
message_end (tool_use is now emitted at execution time, usage is
already emitted from turn_end).
Extract mapPiRpcEvent as a pure exported function so tests exercise
the real event mapping logic instead of an inlined copy that can
diverge from production.
Ref: mrcfps review comments on PR #117
* docs(daemon): clarify mapPiRpcEvent mutability contract
The function mutates ctx.sentFirstToken to track streaming state.
Calling it "pure" is misleading; revised the doc comment to say
no I/O or child process interaction instead.
* fix(pi-rpc): remove redundant status(tool) emission from tool_execution_start
Now that tool_use fires inline from tool_execution_start, the
accompanying status(tool) event is redundant: tool_use already
carries the tool name, and the UI renders running state from the
tool card. The extra status pill breaks consecutive tool_use
grouping in AssistantMessage.buildBlocks. Aligns with
copilot-stream, which emits only tool_use from
tool.execution_start with no status event.
Closes#141.
When the user clicked the Fullscreen button, requestFullscreen() put the
stage element into native browser fullscreen and React's `fullscreen`
state was set true. Pressing Esc was meant to exit the overlay, but in
browsers like Firefox the browser consumes Esc to drop its native
fullscreen element without delivering keydown to JS. The React state
stayed true, the `ds-modal-fullscreen` class lingered, and only a second
Esc reached the keydown handler that flipped the state.
Subscribe to `fullscreenchange` so the React state mirrors the native
state. When the browser exits its fullscreen element, the overlay drops
on the same keystroke. The keydown handler is still needed for the
fallback path (no native fullscreen API support, where requestFullscreen
is undefined and only React state is set).
Adds three regression tests in e2e/tests/preview-modal-fullscreen.test.tsx
covering the bug fix path, the keydown fallback, and a non-collapse
guard for transitions where another element is still fullscreen.
Co-authored-by: d 🔹 <258577966+voidborne-d@users.noreply.github.com>
* feat(daemon): add pi coding agent adapter
Add pi (https://pi.dev) as a supported coding agent, using its
--mode rpc JSON-RPC protocol over stdio for structured event streaming.
Changes:
- apps/daemon/pi-rpc.js: new RPC session handler that drives pi's
--mode rpc protocol, translating typed agent events (text_delta,
thinking_delta, tool_use, tool_result, usage, status) into the
daemon's UI event format. Auto-resolves extension UI requests
(fire-and-forget consumed, dialogs auto-approved) so pi stays
unblocked in the headless web UI. Kills the process after agent_end
since pi's RPC process is designed for multi-prompt sessions.
- apps/daemon/agents.js: add pi agent definition with custom
fetchModels (pi --list-models outputs to stderr, not stdout),
575+ models from 20+ providers, reasoning/thinking level support
via --thinking flag, and streamFormat 'pi-rpc'.
- apps/daemon/server.js: wire pi-rpc stream format to
attachPiRpcSession; skip stdin.end() for pi-rpc since the RPC
session manages stdin bidirectionally.
- apps/daemon/acp.js: export createJsonLineStream for reuse by
pi-rpc.js.
- apps/daemon/pi-rpc.test.mjs: 19 unit tests covering model list
parsing (TSV, dedup, edge cases), RPC event translation (text,
thinking, tools, usage, compaction, retry), sendCommand wire
format, extension UI auto-resolution.
- e2e/tests/structured-streams.test.ts: add pi RPC tool_use/tool_result
event mapping test alongside existing Claude/Copilot fixtures.
Verified end-to-end: daemon /api/chat → pi RPC → SSE stream with
status, text_delta, usage, and tool events. Live E2E test passes
(OD_E2E_RUNTIMES=pi). All 59 project tests green.
* refactor(daemon): migrate pi-rpc to TypeScript
Follow upstream #118 TypeScript migration convention: rename
pi-rpc.js → pi-rpc.ts and pi-rpc.test.mjs → pi-rpc.test.ts
with @ts-nocheck header (same as all other daemon modules).
Import paths remain ./pi-rpc.js per NodeNext module resolution.
* fix(daemon): avoid duplicate usage events in pi-rpc handler
Pi emits both message_end and turn_end per turn, both carrying
usage data. Emitting from both handlers caused double-counting
in the UI and any consumer that aggregates usage.
Remove usage emission from the message_end branch since turn_end
is the canonical per-turn usage source. Keep tool call extraction
in message_end (unique data not available in turn_end).
Add regression test confirming exactly one usage event is emitted
when both message_end and turn_end carry usage for the same turn.
Addresses Copilot P2 review on PR #117.
* fix(daemon): scope pi RPC id counter per session, bump graceful shutdown
Move nextRpcId and sendCommand inside attachPiRpcSession as local
state, matching the pattern in acp.ts where nextId is scoped per
session. Prevents RPC id collisions across concurrent /api/chat
requests.
Bump post-agent_end SIGTERM grace period from 2s to 5s and make it
configurable via PI_GRACEFUL_SHUTDOWN_MS env var for resource-
constrained machines.
Add test confirming concurrent sessions get independent id sequences.
* fix(daemon): wrap parser.feed in try-catch in pi-rpc
Catch errors from parser.feed and route them through the
existing fail() handler instead of letting them propagate
as unhandled exceptions.
* feat(dev): add desktop tools-dev control plane
* refactor(sidecar): split Open Design contracts
Move Open Design-specific sidecar protocol definitions into @open-design/contracts so sidecar and platform can remain descriptor-driven primitives.
* refactor(daemon): organize package sources
Keep daemon app code, tests, and sidecar entrypoints in separate package directories so each layer can be built and verified independently.
* chore(repo): streamline maintenance entrypoints
Centralize agent guidance by directory and reduce root command chains while preserving the existing build scope.
* docs: translate agent guidance to English
* fix(sidecar): tolerate stale IPC sockets
Remove stale Unix socket files only after confirming no listener is active, so tools-dev can restart after unclean shutdowns.