@xynogen/pix-runtime
Pix shared runtime — versioned pix.json config, atomic persistence, typed change events
Package details
Install @xynogen/pix-runtime from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@xynogen/pix-runtime- Package
@xynogen/pix-runtime- Version
0.8.0- Published
- Aug 22, 2026
- Downloads
- 8,330/mo · 1,265/wk
- Author
- xynogen
- License
- MIT
- Types
- extension
- Size
- 106.7 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"src/extension.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pix-runtime
Pix's small shared runtime layer. It owns the process-wide config contract:
~/.pi/agent/pix.json as a single, sparse, versioned user config file, plus the
lifecycle that keeps it coherent.
It is not an aggregator, renderer, model-data package, or service locator.
See DESIGN.md for the full contract.
What it does
- Versioned, sparse
pix.json($version: 1) — defaults resolve in code. - Typed sections:
collapse,pretty,io,compaction,optimizer,gate. - Atomic writes behind a serialized in-process queue and a short-lived cross-process lock. A failed write leaves the old file intact.
- Immutable, deeply frozen config snapshots with a monotonic revision.
- Typed, path-filtered change events.
- One-time migration of legacy unversioned config and the
optimizer.jsonsidecar. - The
/pixshared-settings command.
Install
pi install npm:@xynogen/pix-runtime
Standalone-installable: importing an accessor lazily creates the singleton even
without the extension factory. Installed via pix-core it registers /pix and
session hooks once.
Usage
import { config, updateConfig, onConfigChange } from "@xynogen/pix-runtime/config";
import { prettySection } from "@xynogen/pix-runtime/sections";
const icons = config(prettySection).icons; // synchronous read
await updateConfig(prettySection, { icons: "ascii" });
const off = onConfigChange((c) => render(), { paths: ["pretty.icons"] });
import { ioTimeoutMs, ioTimeoutSignal } from "@xynogen/pix-runtime/io";
const timeoutMs = ioTimeoutMs(); // shared network timeout
const signal = ioTimeoutSignal(toolSignal); // timeout + cancellation
Set pretty.maxRenderWidth and pretty.maxRenderHeight in ~/.pi/agent/pix.json,
or change Pretty → max modal width/height with /pix. Values accept terminal
percentages such as "65%"/"80%" or fixed columns/rows such as 96/20.
Percentage choices in /pix move in 5% steps. Width is the rendered frame width;
height is the threshold where modal content starts paging.
Set io.timeoutSec in ~/.pi/agent/pix.json, or change Network → timeout (sec)
with /pix. The default is 30 seconds. It applies to Pix network operations,
including remote skills, web fetch/search/transcription, MCP requests and
connection bootstrap, background model-data refreshes, and update downloads.
Set compaction.triggerPercent in ~/.pi/agent/pix.json, or change Compaction →
Trigger (% ctx) with /pix. It is the context-window usage percent (0–100) used
to calculate the trigger; the default is 60 and 0 disables the self-trigger
(pi decides when to compact). The /pix picker offers 0, 5, 10, 15, 20, 25, 30,
40, 50, 60, 70, 80, 90.
compaction.minimumTokens is the absolute floor for that calculation. The
effective threshold is max(contextWindow × triggerPercent, minimumTokens), so
a 300K-context model at 10% waits for 100K tokens instead of compacting at 30K.
The default floor is 100K and the hard minimum is 25K (values below clamp up);
/pix offers 25K, 50K, 100K, 150K, 200K, 300K, 400K, 600K, 800K, and 1M.
pix-core consumes both settings.
Collapse policy helpers:
import { shouldCollapse, collapseDelayMs } from "@xynogen/pix-runtime/collapse";
Agent state and herdr notifications
Agent-state coordinator
src/herdr-state.ts (exported from the package index) is a process-wide
coordinator that tracks whether the agent is working, blocked, or idle,
keyed per Pi EventBus. On every transition it emits a pix:agent-state event:
{ state: "working" | "blocked" | "idle", message?: string, activities: number, blocks: number }
Two lease primitives drive it. Both return an idempotent release function:
beginAgentActivity(events, source, message?)marks asynchronous work in progress (for example a running subagent). State reportsworkingwhile any activity lease is open.withAgentBlock(events, source, message, prompt)holdsblockedstate for the duration of an awaitedprompt()and always releases it, even on throw. Blocks take priority over activities, so state isblockedwhenever any block lease is open.
bindAgentStateEvents(events) replays the current state and answers
pix:agent-state:request. resetAgentState(events) clears all leases on session
shutdown.
Nested leases collapse to a single state: two open blocks still report blocked
until both release.
Consumers that open a block today: pix-ask (ask_user, "Waiting for user
answer"), pix-gate (approval prompts), and pix-sudo (root approval).
pix-subagent opens activity leases for running background agents.
import { withAgentBlock, beginAgentActivity } from "@xynogen/pix-runtime";
// Hold blocked state while waiting on the user:
await withAgentBlock(pi.events, "ask_user", "Waiting for user answer", () => promptUser());
// Mark background work:
const done = beginAgentActivity(pi.events, "subagent", "Agent running");
// ... later ...
done();
herdr notification bridge
src/herdr-notify.ts (exported as bindHerdrNotify) is a leaf subscriber on
pix:agent-state. When the agent transitions INTO blocked it spawns:
herdr notification show <message> --sound request
so a user away from their terminal gets a popup and sound. request is herdr's
built-in "needs attention" cue. The trigger is edge-triggered: it fires once per
entry into blocked, not repeatedly, and nested blocks stay a single
notification.
The bridge is fire-and-forget. The child is detached, unref'd, and
stdio: "ignore", and a missing herdr binary is swallowed, so it never blocks
the prompt path or throws. herdr owns the toast's color, position, and sound via
its own server config ([toast] / [notification]); pix only reports the moment
and the message. Compaction and other autonomous work never enter blocked, so
they never notify.
It is wired automatically by the runtime extension: bound at session start, unbound at shutdown. No manual setup beyond running inside a herdr pane.
Two environment variables control it:
HERDR_ENV— herdr sets this to1inside its own pane. The bridge only runs whenHERDR_ENV === "1"; outside a herdr pane it is a no-op and spawns nothing.PIX_HERDR_NOTIFY— set to0to silence notifications even inside a herdr pane.
Testing
import { createIsolatedRuntime } from "@xynogen/pix-runtime/testing";
const { runtime, cleanup } = createIsolatedRuntime();
// ... exercise runtime against a temp agent dir ...
cleanup();