monotonic-pi-extensions/plans/pi-notifications-version-0.md
Willem van den Ende 45a13fd08c feat(pi-notifications): add PI_NOTIFICATION_DEBUG mode with visible steer signal
- Add PI_NOTIFICATION_DEBUG=true env var
- When enabled, calls ctx.ui.steer() instead of desktop notification
- Lets you verify trigger logic in the agent loop without actual notifications
- Synced to both monorepo and auto-discovery extension paths
2026-04-28 12:09:06 +01:00

6.4 KiB

Plan: pi-notifications v0 — Desktop Notifications for Agent Events

Goal

Make the pi-notifications extension reliably show macOS Notification Center alerts when the agent finishes a turn, so the user gets alerted without needing to watch the screen.

Current State

  • Extension exists at packages/pi-notifications/src/index.ts (monorepo) and ~/.pi/agent/extensions/pi-notifications.ts (auto-discovery)
  • Extension loads correctly (appears in /reload extension list)
  • console.log from extensions is NOT visible in /reload output
  • osascript works when run directly in bash, but notification doesn't appear when called from the extension
  • The session_start handler fires on reload, agent_end fires when prompts complete

Debugging Strategy (split into two orthogonal problems)

Problem A: "Does the trigger fire?" — visible debug signal

console.log from extensions is invisible in pi's TUI output. To debug the trigger logic in a fast loop, add a debug mode (PI_NOTIFICATION_DEBUG=true) that emits a visible signal via ctx.ui.steer() (or similar) right before calling notify(). This surfaces in the chat/TUI so you can verify the handler fires without needing actual desktop notifications.

Problem B: "Does osascript actually deliver?" — isolated tester

Create a standalone script (test-notify.ts) that you run from bash independently of the agent loop. This verifies osascript works in the extension's import context, decoupled from event handlers.

1. Verify osascript works in extension context

The extension uses execSync from node:child_process. Test that it works inside the extension:

// In the extension, add this to session_start handler:
try {
  const output = execSync('osascript -e "display notification \\"test\\" with title \\"test\\""').toString();
  console.log("[pi-notifications] osascript output:", output);
} catch (e: any) {
  console.log("[pi-notifications] osascript error:", e.message, e.stderr?.toString());
}

If execSync fails silently, try:

  • Using { stdio: ["pipe", "pipe", "pipe"] } to capture stderr
  • Checking if node:child_process is available in the extension sandbox

2. Check macOS notification settings

The notification might be suppressed by macOS settings:

  • System Settings → Notifications → check that "Terminal" or "pi" is allowed
  • Check if "Show Notifications on Lock Screen" is enabled
  • Check if "Focus Modes" are suppressing notifications

3. Try alternative notification methods

If osascript doesn't work from the extension, try:

  • notify-send (Linux-only, not relevant for macOS)
  • A custom TUI widget that shows a persistent banner
  • Using ctx.ui.notify() (but this only shows in pi's TUI, not system notification)

4. Verify event handlers fire

Add a session_start handler that definitely fires:

pi.on("session_start", async (_event, ctx) => {
  console.log("[pi-notifications] session_start fired");
  ctx.ui.notify("pi-notifications active", "info");  // Shows in TUI
});

If ctx.ui.notify() works but osascript doesn't, the issue is macOS notification permissions, not the extension.

Implementation Plan

Step 0A: Add debug mode with visible signal (PI_NOTIFICATION_DEBUG)

Add a PI_NOTIFICATION_DEBUG=true env var. When enabled, the extension calls ctx.ui.steer() (or a visible TUI signal) right before each notification, so you see "notification triggered" in the chat output during the agent loop. This lets you verify trigger logic without needing actual desktop notifications.

  • In agent_end handler: if PI_NOTIFICATION_DEBUG=true, call ctx.ui.steer("[pi-notifications] notification triggered") before notify()
  • In session_start handler: same pattern
  • This is purely for debugging — no desktop notification shown when debug is on (or both are shown)

Step 0B: Create isolated notification tester

Create packages/pi-notifications/src/test-notify.ts — a standalone script runnable via npx jiti that fires a test notification. Run it from bash to verify osascript works in the extension's context, completely separate from the agent loop.

Step 1: Fix notification delivery (priority)

Once the root cause is identified:

If osascript works but notification is suppressed:

  • Add a PI_NOTIFICATION_SOUND env var (already in design)
  • Add PI_NOTIFICATIONS_ENABLED toggle (already in design)
  • Consider adding a "first-run" notification that asks user to enable notifications

If osascript doesn't work from extension:

  • Fall back to ctx.ui.notify() which shows in pi's TUI
  • Or use a different approach (e.g., write to a file that a separate process monitors)

Step 2: Add turn-limit notification

In packages/pi-turn-limit/src/turn-limit.ts, add notification when the limit is reached:

// In the turn-limit extension, when the limit fires:
if (shouldNotify) {
  execSync('osascript -e \'display notification "Turn limit reached" with title "pi" subtitle "Turns: ' + turnCount + '/' + maxTurns + '"\'');
}

Configuration via env var:

  • PI_NOTIFICATION_TURN_LIMIT — default true, set to false to disable

Step 3: Add sound option

Already designed in the extension:

  • PI_NOTIFICATION_SOUND env var (default: default)
  • macOS sounds: Bottle, Ping, Pop, Submarine, Sosumi, Tink
  • Set to "" for silent

Step 4: Update README

Document the extension with:

  • What it does
  • Configuration options
  • How to enable macOS notifications
  • Troubleshooting tips

Files to Modify

File Action
~/.pi/agent/extensions/pi-notifications.ts Debug and fix notification delivery
packages/pi-notifications/src/index.ts Sync fixes from auto-discovery version
packages/pi-turn-limit/src/turn-limit.ts Add turn-limit notification
packages/pi-notifications/README.md Update with notification docs

Success Criteria

  1. Extension loads and appears in /reload output
  2. macOS Notification Center shows "pi-notifications active" on reload
  3. macOS Notification Center shows "Agent finished — N turns" when agent completes a prompt
  4. Turn-limit notification shows when turn limit is exceeded
  5. PI_NOTIFICATIONS_ENABLED=false disables all notifications
  6. README documents all configuration options
  7. PI_NOTIFICATION_DEBUG=true shows visible signal in TUI when handlers fire
  8. test-notify.ts fires a notification when run standalone