@monotykamary/pi-localterm0.5.1
localterm <-> pi integration: Kitty graphics for the browser renderer, scrubbing localterm-managed secret env vars from the agent's bash-tool children, and OSC 9 desktop notifications when the agent finishes a long turn.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@monotykamary/pi-localterm
A pi extension that integrates localterm with pi. Three features, all inert outside localterm:
- Kitty graphics + OSC 8 links — localterm renders xterm.js with Kitty graphics and web-link support, but sets
TERM=xterm-256colorand strips terminal-identity env vars so applications do not infer unrelated emulator features. pi-tui therefore reports images/hyperlinks as unsupported. This extension detectsLOCALTERM=1(injected into every localterm PTY) and defaults Pi v1'sPI_IMAGE_PROTOCOLandPI_HYPERLINKSoverrides to Kitty and enabled. Explicit environment overrides and Pi'sterminal.images/terminal.hyperlinkssettings remain authoritative; it does not spoof another terminal's identity. Localterm handles ordinary Kitty placements, direct/chunked payloads, temp-root file media, andU=1Unicode virtual placements used by pi-math and Neovim image integrations. - Secret scrubbing for the agent's bash tool — localterm injects a secret only into the shimmed process's env (pi's), not its parent shell. But pi's bash tool spawns commands with
{ ...process.env }, so without this the agent's commands would inherit every secret pi received. This extension overrides thebashtool with a spawn hook that deletes thepiprocess's localterm-managed secret env vars from each command's child env only — pi's ownprocess.env(and its provider calls) keep them. - Desktop notifications on agent completion — pi's only notification primitive is
ctx.ui.notify, an in-TUI banner invisible once you switch away from the pi tab. localterm already has an OSC 9 (ESC ] 9 ; MESSAGE BEL) → browser desktop-notification pipeline (opt-in via "Desktop alerts" in Settings). The extension writes an OSC 9 onagent_settled, reusing that pipeline so a user who stepped away gets an OS notification only after the agent has no automatic retry, compaction, or queued continuation left. It also coordinates with@monotykamary/pi-retrythrough Pi's shared extension event bus so that extension's delayed hidden retries do not produce intermediate notifications. Threshold-gated (turns ≥ 30 s) so quick back-and-forth doesn't spam a focused user; TUI-mode-guarded sojson/rpc/-pstdout isn't polluted with OSC bytes. Note: emitting OSC 9 also fires localterm'snotificationautomation trigger, so anotification-event automation will fire on agent completion.
Install
From npm (published package):
pi install npm:@monotykamary/pi-localterm
From source (this monorepo, for a one-off run):
git clone https://github.com/monotykamary/localterm
pi -e ./localterm/packages/pi-extension
Manual install (load on every session)
Clone and add the package to pi's global settings so it loads automatically:
git clone https://github.com/monotykamary/localterm ~/src/localterm
Then edit ~/.pi/agent/settings.json:
{
"packages": ["/Users/you/src/localterm/packages/pi-extension"]
}
Or, for a project-only install, add the same path to <cwd>/.pi/settings.json instead. Then /reload in pi.
The extension auto-activates only inside localterm (LOCALTERM=1); outside localterm it registers nothing and pi behaves exactly as default.
Pi v1 fullscreen images
Pi v1 defaults to fullscreen mode and reuses uploaded Kitty images when scrolling.
LocalTerm's patched image addon preserves uploads across placement-only deletion,
keeps image tiles through line clears, and separates normal/alternate-buffer
state. The fix ships in LocalTerm's terminal UI, not only this extension. Update
LocalTerm as well as the extension, reload the browser UI, and start a fresh Pi
process so both bundled and extension-visible capability caches use the new
overrides. Existing Pi settings such as terminal.showImages: false are unchanged.
For an older LocalTerm UI, pi --tui-mode regular avoids the fullscreen redraw
path while you arrange the update. The browser regression probe is documented in
harness/pi-images.
How the scrub works
localterm stores secret policy (names + the env var each exports) in ~/.localterm/secrets.json and per-process wiring (pi → which secret names it receives) in ~/.localterm/processes.json. Only names and env vars — never values (values live in the macOS Keychain). The extension reads those two files to find the env-var names the pi process is wired to, and strips exactly those from each bash-tool child's environment.
Resolution mirrors the shim 1:1: the shim injects the pi process's requestedSecrets (resolved to env vars); the scrub strips exactly that set. The strip set is recomputed on session_start (new / resume / fork / reload), so a policy change is picked up on the next session transition.
This extends localterm's existing least-privilege property — "the shimmed binary sees the key, its parent shell doesn't" — to "pi sees the key, the agent's bash commands don't." It converts silent env inheritance into an explicit, greppable localterm secret get <name> call when an agent's command genuinely needs a key.
What this is not
This is defense-in-depth, not a security boundary. The keys still live in pi's own process.env, so a command the agent generates can recover them via parent-process introspection (ps eww $PPID on macOS, /proc/$PPID/environ on Linux) or by shelling out to security find-generic-password directly (the shim's own resolution path). The scrub stops passive/accidental leakage (env, printenv, a script reading $VAR), not active exfiltration by a prompt-injected or adversarial agent.
For untrusted or unmonitored agents, don't wire secrets to the pi process at all — give pi its provider keys through pi's own config, and run pi in a container/VM/micro-VM with short-lived credentials.
Overriding the bash tool
The scrub overrides pi's built-in bash tool by name (extensions apply after the built-in in pi's tool registry). It reconstructs the tool via createBashToolDefinition and preserves a user's configured shellPath and shellCommandPrefix (read from ~/.pi/agent/settings.json + <cwd>/.pi/settings.json) so the override is behavior-identical to the built-in apart from the env scrub. If another extension also overrides bash, only the first-registered one wins — that's a pi-level constraint.
Fabric background-shell integration
With pi-fabric 0.92.33 or newer, nested pi.bash calls use Fabric's local process
runner while retaining this extension's environment scrub and streaming output
redaction. Fabric owns automatic backgrounding, background: true, cwd, PIDs,
hard timeouts, cancellation, and session cleanup. Output is redacted before
Fabric records previews or background logs; Pi's own credentials remain intact.
The registered bash definition carries the host-local
Symbol.for("pi-fabric:bash-middleware:v1") capability. No Fabric dependency,
second tool registration, or load-order handshake is needed. Without compatible
Fabric, the same standalone scrubbed/redacted bash implementation runs as before.
Policy refresh on session_start applies to future commands through either path.
Both packages must be updated, then /reload Pi to load the rebuilt extensions.
This remains defense-in-depth, not protection against deliberate secret recovery.
Requirements
- pi ≥ 1.0.0 (uses the documented Pi capability overrides,
agent_settled, the shared extension event bus, thespawnHooktool option, andcreateBashToolDefinition). Older Pi installations should keep pi-localterm 0.4.x. - localterm with
LOCALTERM=1in the PTY environment (v0.7+). The scrub additionally needs localterm's secrets/processes policy files on the same machine.
License
MIT