@nredd/pi-session-kit
Session-scoped pi tools and commands, offline cross-machine usage stats and a portable privacy-only stats CLI. No daemon.
Package details
Install @nredd/pi-session-kit from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@nredd/pi-session-kit- Package
@nredd/pi-session-kit- Version
0.6.0- Published
- Oct 20, 2026
- Downloads
- 163/mo · 163/wk
- Author
- nredd
- License
- MIT
- Types
- extension
- Size
- 310.8 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-session-kit
Published to npm as @nredd/pi-session-kit (public). From 0.4.0, each version is cut by the monorepo Publish workflow from a signed @nredd/pi-session-kit@<version> tag, with npm provenance and a GitHub release carrying the tarball, checksums and manifest (docs/releases.md); earlier versions were published manually without provenance. 0.4.0 adds createSessionKit(options) (explicit config, agentDir, monitorRoot, injectable clock and spawnMonitor; the default export is unchanged) and fixes leaderless orphan-group sweeps at ps start-time resolution. 0.5.0 redesigns /stats around the full pane (pinned header, aligned stat cells, a Models table on wide panes) and gives every chart a clock- or calendar-aligned time axis, including the 5h and 24h hourly views.
Use the monorepo development commands. The Git install and standalone harness
references below describe the legacy deployment, not installation or candidate verification of
this package. 0.6.0 keeps passive monitor output only in the monitor ring buffer rather than adding it to the transcript or model context; monitor_read is the explicit output surface, and match/exit/timeout wakes are compact summaries with a cursor.
Standalone use
The packed tarball is self-contained: Pi host modules (@earendil-works/pi-*, typebox) are * peers supplied by Pi, there are no runtime dependencies and no sibling plugin is required. Verified by installing the tarball alone and running it in exact stock Pi 1.3.0 (see the repository's docs/package-contracts.md). Optional fork integrations (subagents, Plan mode) are absent in that setup; the exercised behavior does not need them.
Session-scoped pi extensions for tmux users. No daemon, supervisor, core patch or cross-pane registry. Each runtime owns its goal, loops and monitors; persisted goal state follows the active session branch. Closing pi stops its work.
Install and config
pi install npm:@nredd/pi-session-kit@0.6.0
Optional ~/.pi/agent/pi-session-kit.json (respects PI_CODING_AGENT_DIR):
{
"titles": { "enabled": true, "model": "anthropic/claude-haiku-5-5" },
"rules": { "enabled": true },
"goal": { "enabled": true },
"loop": { "enabled": true },
"monitor": { "enabled": true },
"diff": { "enabled": true },
"stats": { "enabled": true }
}
Every module defaults on. Malformed config reports a diagnostic and uses defaults. SDK hosts can use
createSessionKit({ config, agentDir, monitorRoot }) from src/index.ts: explicit config avoids
loading the config file, agentDir scopes config/rules/title aliases, and monitorRoot isolates
monitor pidfiles and orphan sweeps. Optional clock (now, cancellable setTimeout/setInterval)
and spawnMonitor seams control scheduling and child lifetime in SDK tests; the real manager,
ring, coalescer, regex worker and host follow-up queue still run. Controlled stdout callbacks resolve
after matching/ingestion; native pipe events need not await them. Omitted options keep the CLI defaults. No daemon or
persistent cron is installed. Deferred-tool configurations need goal_update in neverDefer
(without forcing it active), and monitor_read in alwaysActive and neverDefer for Plan mode.
Goal and loop
/goal <objective> [--turns N] [--tokens N]: keep working toward an explicit objective. Default budget: 30 parent turns. Token accounting uses reported usage.status,pause,resumeandclearmanage it; the footer chip shows turn and token consumption.goal_update({status: "complete" | "blocked", evidence}): complete requires nonempty evidence. This is a self-report, not an independent verifier; it does not replace tests or todo evidence.- The settle nudge respects existing continuations, background-agent delivery and Plan permissions. Abort, settled provider error, budgets and a Plan-mode excursion pause/stop it. Resume restores a goal paused. Quota recovery is left to the router; a parked dispatch or direct refusal pauses goals and loops, cancels goal wake timers, and requires explicit resume. Goal resume is refused while a quota-parked dispatch remains. A settled provider failure also pauses rather than spinning.
/loop <interval> <prompt> [--max N]: repeat a prompt in the current conversation, unlikeAgent({schedule: ...}), which launches fresh children. Minimum interval 30s; maximum 50 runs.listandstop [id|all]manage loops. Busy ticks are skipped, never queued. Plan mode and an active goal suspend ticks; settled errors pause loops. Loops are not persisted across restarts.
Monitor
monitor_start({argv, name, match?, wake?, timeout_s?}): run an argv command with stdin closed, no implicit shell. Five active monitors maximum, one-hour default timeout. Explicitly passing a shell remains arbitrary command execution; this is not a sandbox.monitor_read({id, since?}): numbered output since a cursor. Ring buffers retain 2000 lines.monitor_stop({id}),/monitor list,/monitor stop <id|all>: stop and inspect owned monitors.- Output batches every two seconds into the bounded ring buffer (with dropped-line reporting). Passive batches neither start turns nor create transcript/model-context entries; call
monitor_readto fetch them. A regex match, natural exit, timeout orwake: trueemits one compact visible wake with a cursor after the output the model last saw through a wake or read, not a replay of buffered output. A bounded batch preserves its first and latest dropped matching lines as wake evidence even if later output evicts them from the ring. - Children run in process groups: SIGTERM, then SIGKILL. Exit cleanup and pidfiles cover orderly quit
and later recovery after SIGKILL. Files live at
$TMPDIR/pi-monitors-<uid>/<sessionId>.<ownerPid>/<monitorId>.json. Startup checks dead-owner records, including a resumed session, and validates process identity before signalling. It never sweeps a live owner's monitors. Two panes resuming the same session have separate directories. A group whose leader already exited is reaped only when a member started at spawn anchors it: itsps lstartmust read under one second after, or under two before, the recorded spawn (lstartis a whole-second floor). A member that reads a second or more after the spawn cannot authenticate a possibly reused group, so its record is kept, not signalled. Within that window a later-born member in a reused group is indistinguishable; the dead leader, matching group and session, and no prior members bound that case, they do not exclude it. - Plan mode admits only
monitor_read; start and stop are mutating tools. Ordinary tool hooks apply to the tool call, but a custom argv tool does not automatically inherit Bash-specific policies.
Diff
/diff [ref] opens a read-only file/patch overlay. Default: staged and working changes against HEAD,
with untracked files listed. j/k or arrows scroll, n/p select files, g/G and PageUp/PageDown
navigate, q or Escape closes. Output is capped. Invalid refs and non-repositories produce clear
errors. Print mode emits a stat (pi currently redirects extension stdout to stderr); JSON emits a
non-triggering display message. The overlay is TUI-only and works in Plan mode.
Stats
/stats is Claude Code's /stats pane over fresh local sessions plus imported machine snapshots,
split by provider and model. It NEVER performs network I/O. Machine coverage and the last successful
remote import are shown; unsynced machines show local-only / never synced. Invalid or missing
snapshots retain last-good data and show incomplete coverage.
Pinned header: tabs and the coverage note, then the date ranges and the model filter, then a rule.
Overview: an activity heatmap (one column per week) with the headline numbers (favorite model,
total tokens, sessions, longest session, active days, streaks, cache tokens, notional cost) beside
it from about 105 columns and under it below that, then the token chart. Models: the chart, then every
model grouped under its provider. From about 100 columns that is a table (tokens, share, in, out, cache
read, cache write, notional cost; a subtotal line per provider), else three-line cards. Tab switches
tabs, r cycles All / 7d / 30d / 5h / 24h (default All), m/M cycle the filter (All, each provider,
each provider/model), j/k or arrows scroll, q or Escape closes.
- The chart is an asciichart-style line plot (
╭╮╰╯), one line per provider under All, else the top three models. Where lines overlap they merge into junctions (┴┬├┤┼) so every line stays connected; the larger series keeps the colour. Line colours start at the theme'saccenthue, saturated, with hues spread evenly over the lines drawn (two providers get opposite hues), so muted themes still separate them. The title row carries the legend on its right (it wraps under the title on narrow panes). Every block spans the whole pane, except the heatmap, which shows as many weeks as fit up to two years. Days sum into 1/2/3/7/14/30-day buckets (longer when needed) so the range fits the width; short ranges are interpolated, not stepped. Y ticks land on round values with little headroom. Height follows the pane in whole tick steps (5 to 40 rows): Overview grows the chart into the rows it has left, Models gives it what a short list leaves, at least half. - X axis: a baseline with a mark under every tick, labels centred on their bucket's column (the first and
last clamp to the plot edges). Ticks are evenly spaced, at most 13, never overlap and are never cut; the
finest set whose labels fit wins, so a wider pane gets more of them. 5h and 24h label hours starting on
the clock (
09:00, or9a/12pwhen narrow; every hour, 2, 3, 4, 6, 12 or 24 hours apart; across a DST change they stay evenly spaced, so later labels shift by the hour) with a row of dates where they fit: each new local day first, then the first bucket. Day buckets: a week of single days getsWed 7(orWed), longer ranges get dates on every day, 2 or 3 days, every Monday or every other Monday (counted back from today for multi-day buckets), then month names, quarters, half years and years, with'26only where the ticks span more than one year. A pane too narrow for any label draws the baseline alone. - Rolling 5h/24h use the original call timestamps in zero-filled one-hour UTC buckets, with partial edges labeled. Labels use viewer-local time and UTC offsets, including repeated DST hours. The same rolling filter feeds chart/model/overview totals; future calls are ignored in every range. Long ranges retain the daily chart and activity heatmap.
- Tokens = input + output + cache read + cache write. Anthropic reports almost every prompt token as a cache read or write while Codex reports it as input, so anything less undercounts agentic use by orders of magnitude. Cost is pi's recorded list price: notional on a subscription.
- Counted: assistant messages, under the provider/model that answered. Not counted: tool-result
usage (pi-subagents' parent-side report; its child sessions already hold those calls), compaction
usage,
router/*rows,fauxand zero-usage rows. A/forkor cross-machine copy counts once. Preserved assistant entry IDs distinguish equal-value events. Legacy records use the old tuple identity and are discarded when an identified counterpart exists. Raw names drive dedup; only display text is sanitized. - Subagent children (parent link plus a
<type>#<8 hex>name, or the matching persisted child marker, including renamed children) count toward tokens but not Sessions. A filter narrows Sessions to the sessions that used the filtered models. - Source:
*.jsonlunder$PI_CODING_AGENT_SESSION_DIRor<agentDir>/sessions. Cache:<agentDir>/pi-session-kit/stats-cache.json, re-parsing only files whose size or mtime changed; deleted sessions keep counting; machine views also retain history from previously configured session roots. Version 1 caches migrate without dropping deleted-file records; available files are reparsed for preserved IDs. A corrupt record is dropped and rebuilt. Lines that are not JSON objects show as! N unreadable lines. - Print mode prints a per-model table and coverage; JSON emits a display message. Works in Plan mode.
- After
make installat the monorepo root,npm run build:stats-export --workspace @nredd/pi-session-kitreproducesdist/pi-stats-export.mjsinside this package. Copy this standalone Node 22+ artifact to another machine: neither Pi nor the loaded session-kit extension is needed at runtime.node ARTIFACT export --agent-dir DIR --output OUTwrites ONLYOUT/machines/<UUID>/.node ARTIFACT import --agent-dir DIR --input REPOvalidates all machines and atomically publishes the local global cache. Both emit one JSON object on success; partial imports emit JSON and diagnostics with a nonzero exit. No Git/network operations are part of the CLI. Schema, identity, atomicity and harness API details: here.
Titles and rules
- Titles: one attempt after the first eligible TUI run of an unnamed persisted session. Default
candidates are Haiku 5.5, validated enterprise alias candidates (including the legacy Haiku 4.5
logical key), then GPT 6 Luna.
titles.modeloverrides the preferred candidate. No silent retry after provider failure; explicit names and session switches win races. Children, headless runs, no-session runs and active planning are skipped. Output is capped at six words/60 characters; shorter names are accepted. - Rules: Markdown files in
.pi/rules/and the agent directory'srules/, with YAML frontmatterpaths: ["src/**/*.ts"]. Nopaths, no injection. Successful read/edit/write results receive matching rules once per context lifetime. Compaction resets deduplication. Eight rules per turn, 4096 UTF-8 bytes per appended block. The system prompt is not rewritten. Rules injected after an edit inform subsequent work, not the edit already performed.
Subagents and verification
pi-subagents exposes no public child-context marker. The kit recognises its generated
<type>#<8 hex> session names; this is a compatibility heuristic, not a security boundary.
Commands/continuations are guarded. At session_start, parent-only goal/monitor tools are
re-registered with Pi's hidden exposure: child scoping and deferred promotion cannot reactivate
their schemas or nested-call capabilities. Tool-call and execute guards also deny child calls,
even after a child renames itself and reloads. Initial child binding persists a
session-kit:child entry with its session id; reload reads the active branch, not a generic
parent-session link (fresh Plan sessions also have parents). Replacement sessions do not inherit
cached identity or another session's marker. Goal/monitor factories register the shared identity
hook before their rebuild hooks, without querying an unbound runtime during factory loading.
No private subagent marker is imported. Existing tool permissions are not widened.
A prior settle handler's continuation (including a todo reminder) suppresses the goal nudge; the kit does not append a second reminder or request another continuation.
test/scenarios/ drives the real kit through core's in-process session harness under Vitest and Bun,
covering goals, file-backed restore, child rename/reload, read-rule injection, titles,
monitor output/wakes/cursors/caps/timeouts and loop scheduling with same-cwd session isolation.
The interactive host/VirtualTerminal covers /diff, /stats and real /name, including width/resize
matrices, chart glyph ports/colours and file-backed titles. Stats histories use a fixed clock;
createSessionKit({ agentDir, clock }) scopes both offline loading and displayed time ranges.
Terminal fixtures route input chunks through pi-tui's real StdinBuffer, matching ProcessTerminal's
keypress splitting (complete keys only; lone Escape is flushed immediately). Stats scroll-to-clamp
checks batch input and settle once; intermediate-frame checks still settle per step. Both hourly
tabs and exact provider totals remain covered at every width. Each stats-view body owns its fixture
and disposes it in finally, so runner timeout teardown cannot race a still-running callback.
Monitor scenarios use a shared fake clock and controlled children; native test/monitor/ keeps real
workers/processes, and process-group kill/orphan recovery stays in the assembled integration tier.
npm run check runs Biome, TypeScript, the full Vitest suite and bun test test/scenarios (Bun 1.3.14).
Cross-extension Plan/subagent/todo interactions and OS terminal boundaries remain in the integration tier;
docs/verification-rebuild.md at the monorepo root tracks their destinations.