npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pi-agent-observer

v0.1.0-alpha.1

Published

Prompt-neutral runtime observability for pi agents

Readme

pi-agent-observer

read-only, prompt-neutral local observability for pi runtimes.

pi-agent-observer is a general-purpose observability package for users and pi package authors who need to see which pi agents are running, how spawned agents relate to their parents, and what high-level lifecycle events occurred without retaining model-visible payloads.

use it to answer questions like:

  • what child agents did this session spawn?
  • which runtime is active, stale, completed, failed, or aborted?
  • which package/orchestrator launched this runtime?
  • what model/session/cwd/runtime metadata is attached?
  • what bounded event previews exist for debugging a run?

it is intentionally not a tracing/profiling/security/audit system for prompt contents. it stores structured runtime metadata and bounded previews only.

compatibility

this alpha targets pi 0.80.6 exactly. unknown observer state/event schema versions are ignored rather than interpreted. macos and linux are supported; storage and discovery are host-local.

install and local development

this alpha is not published to npm yet. install from a local checkout:

git clone https://github.com/almogdepaz/pi-agent-observer.git
cd pi-agent-observer
bun install
bun run build
pi install "$PWD"

local development without installing globally:

bun install
bun run check
pi -e src/extension.ts

ordinary package installs load dist/extension.js. the executable is dist/cli.js and is exposed as pi-agents. if you install from a source checkout, build first so dist/ exists.

user-facing tools

interactive agent browser

in interactive pi, type / and select /agents. use / to choose a descendant runtime and enter to open its durable event tail. escape returns to the picker, then closes it.

/agents all includes unrelated local roots; /agents <runtime-prefix> opens one matching descendant directly. the browser remains read-only and shows the same bounded previews as the cli—full payloads are unavailable.

after at least one descendant is observed, interactive sessions show a, c, f, and s counts for active, completed, failed/aborted, and stale descendants. sessions with no descendants show no observer status; unrelated roots are excluded.

cli

pi-agents list [--json] [--runtime ID] [--session ID] [--cwd PATH] [--launcher pi|edc|pi-subagent] [--origin NAME] [--parent ID|none] [--status STATUS]
pi-agents tree [--json] [--launcher pi|edc|pi-subagent] [--origin NAME] [--parent ID|none] [--status STATUS]
pi-agents tail <runtime-or-session-prefix> [--json] [--follow] [--event TYPE] [--since ISO_TIME]

missing tail targets exit 2; ambiguous targets exit 3. json list/tree output includes both persisted state and heartbeat-derived observed status. tail json is jsonl. full prompt/tool payloads are not retained and therefore cannot be requested by the cli.

observability data model

for each observed pi runtime, agent observer stores prompt-neutral runtime state:

  • runtime id, parent runtime id, pid, cwd, session id, and optional session file
  • pi session display name, if set through --name, /name, rpc, or pi.setSessionName()
  • model provider/id after selection events
  • launcher classification (pi, edc, or pi-subagent)
  • structured launcher hints such as EDC_PI_SUBPROCESS or PI_SUBAGENT_*
  • optional explicit runtime origin for orchestrators/packages
  • start, heartbeat, end timestamps, terminal status, shutdown reason, and bounded error preview

it also writes durable jsonl events for session, agent, turn, message, tool, model, compaction, and tree/info changes. message/tool events retain only bounded previews and structured metadata such as tool name, tool-call id, error flag, and argument key names.

agent observer does not retain:

  • full prompts
  • full messages
  • full tool arguments
  • full tool results
  • provider request payloads
  • credentials
  • model-visible resources

if a preview is truncated, the full payload is intentionally unavailable.

integration contract for third-party tools

third-party tools means pi packages, cli wrappers, ci jobs, custom uis, rpc clients, and sdk applications that launch or embed pi. treat pi-agent-observer as an optional observability layer: your tool must keep working when it is not installed.

stable integration surface:

  • inherit observer environment variables into child pi processes
  • set pi session names for human-readable task labels
  • set PI_OBSERVER_RUNTIME_ORIGIN for orchestrator attribution
  • consume pi-agents ... --json output for automation

non-contract surface:

  • private files under the observer directory
  • human text from the interactive /agents browser
  • prompt text, tool arguments, or tool results; full payloads are intentionally not retained

integration levels

| level | use case | required work | |---|---|---| | zero-config | your tool spawns normal pi children | install observer globally, inherit process.env, do not pass --no-extensions | | labeled children | users need readable task names | pass --name <task-name> or use rpc/sdk session-name APIs | | origin attribution | users need to filter by orchestrator | set PI_OBSERVER_RUNTIME_ORIGIN=<package-name> | | sdk embedding | your app creates AgentSession directly | use pi's normal resource loader and bind extensions for each active session | | automation dashboards | another process reads observer state | call pi-agents list --json, tree --json, or tail --json |

child processes launched with pi

when customizing child process environments, preserve the inherited observer variables instead of rebuilding a tiny env map.

import { spawn } from "node:child_process";

const child = spawn("pi", ["--name", "workflow-review-analysis", "-p", "review this workflow"], {
  env: {
    ...process.env,
    PI_OBSERVER_RUNTIME_ORIGIN: "looper-ai",
  },
  stdio: ["ignore", "pipe", "pipe"],
});

await new Promise((resolve, reject) => {
  child.once("error", reject);
  child.once("exit", resolve);
});

shell wrappers can do the same thing:

export PI_OBSERVER_RUNTIME_ORIGIN=my-tool
pi --name "my-tool: dependency review" -p "review dependencies"

python subprocess wrappers should copy os.environ:

import os
import subprocess

env = os.environ.copy()
env["PI_OBSERVER_RUNTIME_ORIGIN"] = "my-python-runner"
subprocess.run(["pi", "--name", "my-python-runner: task", "-p", "do the task"], env=env, check=True)

be real: env={"PATH": ...} breaks ancestry unless you also copy PI_OBSERVER_CURRENT_ID. prefer copying the full environment, then overriding only your tool's own keys.

sdk-created sessions

sdk-created sessions are visible only after extensions are loaded and bound. sessions that never bind extensions are invisible to the observer.

recommended sdk behavior:

  • use pi's DefaultResourceLoader or equivalent package-aware loader so globally installed packages are discovered
  • bind extensions for the initial session
  • after newSession(), switchSession(), fork(), or reload-style replacement, bind extensions again for the new active session
  • set a useful name with pi.setSessionName(), rpc set_session_name, or cli --name

browser labels prefer:

  1. session display name
  2. explicit runtime origin
  3. safe cwd basename/runtime id fallback

machine-readable consumption

use json output; do not parse human tree/list rows.

pi-agents list --json --origin looper-ai
pi-agents tree --json --parent <runtime-id>
pi-agents tail <runtime-or-session-prefix> --json --event agent --since 2026-01-01T00:00:00Z

list --json returns an array of records with:

  • state: persisted runtime metadata, including runtimeId, parentId, sessionId, sessionName, cwd, launcher, origin, timestamps, and terminal outcome
  • observedStatus: heartbeat/event-derived status: active, completed, failed, aborted, or stale
  • observedStatusReason: why that status was chosen

tree --json returns the same runtime records nested by parentId, with relation markers for roots, orphans, cycles, and children. tail --json emits observer events as jsonl.

prefix matching is supported only for tail targets. if a prefix is missing, exit code is 2; if ambiguous, exit code is 3.

runtime origin metadata

set PI_OBSERVER_RUNTIME_ORIGIN=<your-package-name> when your package is the orchestrator. accepted values are a-z0-9._-, must start with a-z0-9, and are limited to 64 chars.

examples:

PI_OBSERVER_RUNTIME_ORIGIN=looper-ai
PI_OBSERVER_RUNTIME_ORIGIN=branchout
PI_OBSERVER_RUNTIME_ORIGIN=my-package.subrunner

runtime origin is not launcher classification. launcher kind is low-level process context (pi, edc, or pi-subagent); origin tells users which package intentionally created the runtime.

edc and compatibility hints

EDC_PI_SUBPROCESS=1 is treated as an edc launcher hint for compatibility. if your package sets EDC_PI_SUBPROCESS=1 only to disable an edc extension inside isolated child calls, also set PI_OBSERVER_RUNTIME_ORIGIN. then the runtime remains queryable with the edc hint without being semantically labeled as edc-owned.

package author checklist

  • inherit process.env / os.environ when spawning child pi processes
  • do not pass --no-extensions for children you want observed
  • set --name, rpc set_session_name, or pi.setSessionName() for readable labels
  • set PI_OBSERVER_RUNTIME_ORIGIN for package/orchestrator attribution
  • bind extensions for sdk-created sessions, and re-bind after session replacement
  • consume pi-agents --json; do not scrape display text or private storage
  • never rely on retained prompt/tool payloads; only bounded previews exist
  • treat EDC_PI_SUBPROCESS as a compatibility hint, not ownership metadata

privacy and retention defaults

observer data is stored under ~/.pi/agent/observer unless PI_OBSERVER_DIR is set. directories are owner-only (0700) and files are owner-only (0600). traces retain structured metadata and bounded previews only; full prompts, tool arguments, and tool results are unavailable.

heartbeats default to 1 second and become stale after 5 seconds. cleanup at session startup removes terminal/stale records older than 7 days and trims eligible records toward a 100 mib total ceiling. these defaults can be changed with PI_OBSERVER_HEARTBEAT_MS, PI_OBSERVER_STALE_MS, PI_OBSERVER_PREVIEW_BYTES, PI_OBSERVER_RETENTION_AGE_MS, PI_OBSERVER_RETENTION_BYTES, and PI_OBSERVER_STATUS_MS.

this package registers no tools, commands, skills, prompt templates, or model-visible resources. it has no install or postinstall scripts.

alpha limits

  • no visibility into --no-extensions or sdk sessions that never bind extensions
  • no standalone tui, sockets, control, abort/steer/follow-up commands, or resume
  • no remote host aggregation or semantic phase inference
  • filesystem ownership is the privacy boundary, not a sandbox