@agimon-ai/po-notification

Desktop notifications and an animated shell-tab title for Po sessions.

Packages

Package details

extension

Install @agimon-ai/po-notification from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@agimon-ai/po-notification
Package
@agimon-ai/po-notification
Version
0.0.0-alpha.40
Published
Oct 21, 2026
Downloads
3,933/mo · 3,933/wk
Author
agiflow-ai
License
BUSL-1.1
Types
extension
Size
139.7 KB
Dependencies
2 dependencies · 1 peer

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@agimon-ai/po-notification

Native push delivery, notification text helpers, and shell-tab title utilities for Po sessions.

Long agent runs are worth walking away from. This package supplies the native push sender and /notify command. Hosts own notification routing through the shared po/notification service and live session entries.

What it does

Surface Behavior
Shell tab title π - <session or first prompt> - <repository>, with a braille spinner while the agent works
Notification API caller-authored notices through the shared po/notification Cordis service
Mid-run server-derived native push for each dialog during a run, parent sessions only
At rest server-derived native push for settled parent runs with no queued follow-up; requires host execution state, skipped for sandbox sessions

The package exports shell-title controllers and a system-notification helper (cmux notify, then osascript on macOS), but does not install a terminal router or shell-title lifecycle itself. The /notify command calls the active headless client's notification API, which appends a versioned po-notification session entry for live clients and the native push subscriber.

Service usage

Callers should discover the optional service through Cordis and request delivery without depending on a particular host:

import { PO_NOTIFICATION_SERVICE, readPoNotificationService } from '@agimon-ai/po-core/notification';

ctx.inject([PO_NOTIFICATION_SERVICE], (notificationContext) => {
  void readPoNotificationService(notificationContext)?.request({
    body: 'Deployment needs approval',
    level: 'warning',
  });
});

body is required. title, subtitle, and level are optional, and level accepts info, warning, or error. Entry normalization defaults title and subtitle to empty strings and level to info. A host must provide the optional service. This package does not wrap Pi's ui.notify API.

Browser delivery

po-web shows full notification text only for entries received by a currently open page. Every connected page receives live notification entries for every attached session, not only the focused session. Permission is requested only from an explicit settings action.

Browser Web Push is not supported. Closed-app delivery uses the paired native iOS or Android app, with explicit notification permission and native push enrollment. The server subscribes once to live notification entries across all sessions, even with no client sockets connected. It also derives "Input needed" (Po needs your input) and "Run finished" (Po finished) pushes from live hub events, without adding transcript entries. It never reads history for push, and has no durable outbox or replay. Enrollment is persisted on the trusted device record; revocation, token rotation and remote disable are rechecked before queued work is dispatched.

Native push server credentials

Add these four variables to the server user's ~/.zshrc (use absolute paths):

export PO_APNS_KEY_FILE="$HOME/.po-secrets/AuthKey.p8"
export PO_APNS_KEY_ID="ABCDEFGHIJ"
export PO_APNS_TEAM_ID="KLMNOPQRST"
export PO_FCM_SERVICE_ACCOUNT_FILE="$HOME/.po-secrets/firebase-service-account.json"

Restrict the directory to the server user (chmod 700) and credential files to owner read/write (chmod 600). Never commit, paste into logs, or distribute these files with the app. Start Po from a shell that inherited these variables. GUI or service launches may not read ~/.zshrc; configure their launch environment explicitly. Restart the server after changing credentials or variables: files and key material are validated once at startup. Missing or invalid credentials disable only that provider, with a redacted diagnostic. No provider credentials are read from a session's environment.

For Apple, provision the company ai.po.app App ID with Push Notifications, sign the native app with the matching entitlement and provisioning profile, and create an APNs signing key in the Apple Developer team. The server topic is fixed to ai.po.app. The native build-only variable PO_NATIVE_APNS_ENVIRONMENT selects development or production to match the signed entitlement; it is not a server credential. At runtime the native app reads the provisioning profile through expo-application, falling back to production for App Store/TestFlight installs without a profile, and registers its token with that environment for server routing. iOS push enrollment refuses a different application ID, including the previous ai.agimon.po development app. Rebuild and pair the company iOS app with its matching profile; old iOS installs are a separate app identity.

For Firebase, register Android package ai.agimon.po, enable the FCM HTTP v1 API, and grant a service account permission to send FCM messages. The server reads client_email, private_key, and project_id from its service-account JSON, not from a separate project environment variable. The native build-only PO_NATIVE_ANDROID_GOOGLE_SERVICES_FILE points to the matching Android google-services.json. This client configuration is not the server service-account private key. Rebuild and reinstall the app when changing native build configuration.

Provider rejections produce redacted Native push <provider>: <outcome> <status> <reason> notices; status and allowlisted APNs reason are included when available, never tokens, text, or keys.

Privacy: native push carries the full notification title, subtitle and body, subject to provider payload limits. Apple or Google and the device lock screen can receive that text, including sensitive agent output. This is not generic-copy-only delivery. Do not enable it for content you cannot share with those providers or display on the lock screen.

Delivery is bounded, best effort, and timeout-limited, not guaranteed. FCM uses TTL 0 and APNs uses expiration 0, so providers are asked not to retain notifications for offline delivery. This is an offline-retention ceiling, not a guarantee that every background or terminated app receives a notice. Excess pending work is dropped. Invalid endpoints are conditionally cleared without deleting a rotated token. Physical-device verification remains blocked until signed Apple/Firebase builds, credentials, and paired devices are available; source tests do not establish real-device delivery.

Forgetting a paired host first requires successful remote device revocation. If the host is unreachable or revocation fails, Po keeps local keys so you can reconnect and retry. A confirmed revocation is checkpointed so interrupted local key deletion can finish after a restart. Revocation cannot recall notifications already submitted to Apple or Google.

The shell title

The title is animated from a worker thread, so a busy agent turn never stalls the spinner. The worker is unreferenced, so a pending frame cannot hold the process open. If worker threads are unavailable, or the worker fails or exits, the last command replays on the main thread.

Titles are only written to an attached terminal: rpc sessions and headless ones get none, so escape sequences never end up in machine-read output.

Quiet by design

  • Server-derived pushes are silent for child sessions.
  • Dialogs notify only during a run, once per dialog id. A new run resets deduplication.
  • A finished push requires the host to report idle with no pending follow-up messages.
  • Missing execution-state readers, including sandbox mirrors, skip completion pushes and log one redacted notice per session. Dialog pushes still work.

Installation

Po depends on this package and activates it as fixed host core, so a Po install already has it. It is not selectable from .po/modes.yaml.

The server loader uses /extensions/server, built from src/extensions. The root export contains reusable notification helpers and types. The session command delegates to the host client; this package has no Pi entry, notification wrapper, or automatically installed shell-title controller.

License

Business Source License 1.1: free for personal use, organisations under USD 1M revenue, non-profits and education. See LICENSE and https://agimon.ai/licensing.