Picophony

Picophony is an additive, first-party Rust layer for driving a headless Pi agent over Pi's documented JSON-RPC mode (pi --mode rpc). It is a new mode, not a replacement: it never modifies the existing tmux/pi launch, messaging, or attach paths used by managed agent runtimes. Pi-equivalence is preserved by construction — Picophony runs the real pi binary with the caller's program/args/env (same flags, same plugin ecosystem) and only consumes Pi's published RPC and session-file contracts. Pi remains the sole writer of session files.

Tracked under epic.

Why Picophony

The managed runtime launches pi inside a Herdr workspace or tmux session and attaches operators to that pane. Picophony adds a second, structured way to drive the same pi process: a typed JSON-RPC channel plus a read-only session reader. That structured seam is what makes a single agent core renderable across native and web UIs — the standalone pico TUI, the caco TUI and daemon, the Apple and Android companion apps, and the browser — without each surface reimplementing Pi orchestration.

Architecture

Picophony is one shared core crate (caco-picophony) plus a thin standalone binary (pico):

Picophony runs the real pi binary in RPC mode and never writes Pi's session files. Inside the caco-picophony crate, the subprocess backend, behind the PiBackend seam, sends typed commands to Pi's stdin and turns its JSONL stdout into typed events, while a read-only reader follows Pi's session files. A host broker on a Unix socket lets several local viewers attach to one driven agent. The same core renders in the standalone pico TUI, the caco TUI and daemon, the Apple apps through a C ABI, Android through a JNI shim and the browser through a wasm binding.
PicophonyOne typed core between the real Pi process and every surface that renders it.

The pico binary

pico is a standalone ratatui TUI that drives a headless pi --mode rpc underneath — a “nicer pi” usable with no Cacophony integration at all. The caco daemon and TUI consume the same caco-picophony library separately, so the standalone tool and the managed surfaces share one rendering core.

Conversation recovery

Use /help or composer completion to discover recovery commands. These are client-side controls, not model prompts, and remain usable above 100% context or after failed compaction.

Rewind, new and resume clear queued steering/follow-up input before requesting the change. Extensions may cancel; Pico reports cancellation and keeps the current view. Only a confirmed change clears stale transcript/context state and refreshes messages, statistics and identity. The previous file is shown for returning with /resume. Cancel browsing by not submitting a rewind command.

Pi remains the only session writer. After a standalone restart, use the new backend-confirmed file with pico --session <file>, not the original launch path. Managed recreate --fresh and automatic relaunch selection have a separately tracked regression; Running is not proof of a fresh conversation. Verify session ID/file and context, especially for empty sessions which Pi may not yet have written to disk.

The shared composer/reducer exposes these controls to TUI, FFI/JNI and WASM consumers. Run python3 scripts/test-pico-recovery.py /absolute/path/to/pi with Pi 0.84.4 or compatible RPC for isolated real-runtime coverage: over-limit context, compaction failure, active and abandoned branch forks, a model/read-tool turn, fresh recovery, preserved history, switching and reopening the confirmed file.

Cargo features

caco-picophony keeps its surface bindings behind cargo features so each consumer compiles only what it needs.

FeatureDefaultPurpose
runtimeyestokio runtime: subprocess backend, host broker, unix socket, attach client
ratatuinoshared ratatui AgentView renderer (the pico binary and caco-tui)
wsnoWebSocket session source: attach over the daemon agent session endpoint for remote/mesh attach
ffinoC ABI surface for native bindings (Apple C ABI plus the symbol base for Android JNI shims)
wasmnowasm-bindgen web binding (the PicoView JS class)

Building with --no-default-features yields the pure protocol + view core (serde-only, no tokio) for wasm32 targets.

Cross-surface consumers

One core, many front-ends:

Surface integrations are landing incrementally under the Picophony epic; not every front-end exposes the full agent experience yet. See the companion app guides for per-platform status.

Relationship to managed Pi agents

Picophony is strictly additive. Managed pi, pico, and on-demand worker agents retain their existing lifecycle, and Picophony never rewrites Pi's session files. The structured RPC channel is an alternative driver for surfaces that need a typed, renderable agent stream rather than a raw terminal pane.

Logical pi-daemon agents are intentionally different: they have no tmux pane or per-agent Pi process, and they do not speak the Pico protocol. Each is the AHP session ahp-session:/<agent id> on its node's pi-daemon 1.0 host (pi_daemon.ahp). /api/v1/agents/<id>/culture and /session both pipe the viewer to that session exactly as /culture does for Paratenic agents: the owning daemon creates the session if needed, holds the host bearer, and limits the pipe to the agent's own session and chat. The TUI and Android open these agents in their Culture view; the web Pico pane speaks AHP for them (ahp-chat-view.js). Closing a viewer closes only its pipe, never the resident session. A raw terminal attach remains refused; there is no fallback that silently spawns a new local Pi runtime.

Pi Daemon host capacity

agents.pi_daemon (or the selected node's complete override) configures the separately supervised shared host. Cacophony remains a client: these settings become neutral host CLI flags, not per-agent implementation state. A node-local object replaces the global fallback; add limits to the existing object rather than dropping its socket, path or security declarations.

An explicit desktop/server profile can add:

max_sessions: 512
max_concurrent_turns: 256
max_session_queue_depth: 4
max_connections: 1024
max_api_connections: 1024
max_rpc_hubs: 512
max_rpc_replay_bytes: 2097152
max_rpc_total_replay_bytes: 1073741824
max_residency_leases: 2048
idle_session_ttl_ms: 1800000
recovery_open_timeout_ms: 30000
recovery_total_timeout_ms: 600000
node_max_old_space_size_mb: 16384

These settings are optional; omission preserves standalone defaults. Zero is permitted only for queue depth and idle eviction, and forwarded numbers must be JavaScript safe integers. The aggregate RPC replay budget must cover the hub count multiplied by the per-hub bytes, including omitted defaults. Raising only hubs above 32 without a matching budget is rejected at config validation, not deferred to a crash loop.

A synchronous wake holds a Unix connection while waiting, including queue time. Allow for active and queued workers plus control traffic; HTTP/WebSocket connections have a separate allowance. RPC hubs and residency leases concern attachments, not all headless wakes. max_line_bytes and max_events_per_request remain client decoding/retention bounds. Agent spawn ceilings and heavy build/test admission remain independent.

The CLI launch plan, native supervisor and capability preflight use one capacity argument mapping. New configured flags must appear in the selected host's help before an explicit restart can stop anything. Original session controls remain compatible with old incomplete help output. The new API/RPC/lease settings require Pi Daemon's PD-020 support; released 0.3.20 is too old. Do not silently omit requested settings to start an older host.

node_max_old_space_size_mb appends a numeric --max-old-space-size option to NODE_OPTIONS for the Pi Daemon service launch. Existing options are preserved, with the explicit heap setting taking precedence, without mutating the supervisor or sibling-service environment. It is not a logical-session environment declaration; Node child tools may inherit it normally. Old-space is not total RSS or preallocated memory: native allocations, buffers and tools require headroom. The native launcher already raises the child's file-descriptor allowance; no CPU quota or tool/build concurrency increase is implied.

After compatible binaries and source are installed, apply through the existing lifecycle owner during an approved safe restart window. Inspect the running service: Unix handshake/status report resident, turn, queue and lease limits; /v1/capabilities reports RPC budgets; startup capacity records also include Unix and HTTP connection limits. Preserve accepted and indeterminate tickets rather than resending prompts after timeouts or restarts. Config validation never restarts or reconfigures a running host.