@comput/pi-background
Session-owned one-shot and recurring scheduling for Pi prompts and Python scripts, without execution backlogs.
Package details
Install @comput/pi-background from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@comput/pi-background- Package
@comput/pi-background- Version
0.4.0- Published
- Nov 12, 2026
- Downloads
- 551/mo · 64/wk
- Author
- comput
- License
- MIT
- Types
- extension
- Size
- 39.7 KB
- Dependencies
- 1 dependency · 2 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 Background — session scheduler
Schedule Pi coding-agent prompts and Python scripts in the owning live session, without execution backlogs.
Version 0.4.0 — scheduler only: removes background Pi subagents, the result inbox and completion notifications, replacing the bg_* tools with five schedule_* tools. The npm name @comput/pi-background and repository identity remain unchanged. Version 0.3.0 retains the older background/inbox interface.
Installation
pi install npm:@comput/pi-background
# Or install committed source (not uncommitted checkout changes):
pi install git:github.com/mbundgaard/pi-background
Installation and /reload or starting a new Pi session are separate actions. Pi must be installed and authenticated. Node.js 20.3 or newer is required; development checks use Node.js 22 or 24 and Pi 1.4.0. The package ships TypeScript source loaded directly by Pi, with no build step or standalone executable. Script schedules also require python3 on PATH.
Tools
| Tool | Purpose |
|---|---|
schedule_create |
Create a session-owned one-shot or recurring main prompt or Python script schedule |
schedule_list |
List this session's schedules and latest dispatch/skip outcome; optional offset |
schedule_enable |
Enable or disable using scheduleId and enabled |
schedule_delete |
Delete by scheduleId; does not terminate an already-started script or delete historical files |
schedule_pause |
Temporarily skip launches in this session using an absolute pausedUntil timestamp |
There are no bg_* aliases, bg_start, or bg_inbox tools. Schedules belong to the persistent session ID and project directory. Another session or fork does not inherit ownership. Resume the original session to manage its schedules. Tool responses are bounded to 10,000 characters; list pages contain up to 20 records.
Main prompt scheduling
Ask Pi, for example:
In one minute, then every five minutes, check the test status in this session. Do not change files.
Pi can call schedule_create:
{
"name": "test-status",
"prompt": "Check test status without changing files; return a concise result.",
"target": "main",
"when": "+1m",
"intervalSeconds": 300
}
Omit intervalSeconds for a one-shot. when accepts a future ISO timestamp with an explicit timezone or +30s, +10m, +1h, +1d. Fixed intervals range from one second to one year; cron is not supported. At most 100 schedules are stored per session, including disabled and legacy records.
Main prompts dispatch only while the session is idle, with no pending messages or blocking UI prompt. Otherwise that trigger is skipped, never queued for later. Main uses its current model and settings. prompt is required; timeoutSeconds, script options and worker model/provider/thinking options are not accepted for main schedules.
Python script scheduling
{
"name": "check-alerts",
"target": "script",
"scriptPath": "/home/me/project/check_alerts.py",
"args": ["--mode", "scheduled"],
"envFiles": { "API_TOKEN": "/home/me/.secrets/api-token" },
"when": "+1m",
"intervalSeconds": 300,
"timeoutSeconds": 120
}
Scripts run as python3 with an absolute scriptPath, optional string-array args, no shell, and the project working directory. Omit prompt; worker model, provider and thinking options are removed. Optional timeoutSeconds requests termination of the immediate child, not guaranteed termination of its process tree. A process that does not close can retain the overlap guard indefinitely.
envFiles maps environment variable names to absolute file paths. Each execution rereads those files into a copy of the parent environment for that child only. Exactly one trailing LF or CRLF is removed; all other contents are preserved. File rotation takes effect on the next run. The parent's environment is never modified.
Output contract: any nonempty captured stdout is sent as exactly one unchanged user message to the owning live session: ordinary delivery when idle, deliverAs: "steer" when busy. Whitespace-only output counts. Empty stdout sends nothing. There is no trimming, wrapping, filtering, truncation, retry or exit-code condition. Nonzero exit, stderr, disabling/deleting the schedule, or a pause applied after execution begins do not suppress stdout. A pause can still prevent execution before spawn, including during environment-file reads.
Scripts from the same schedule do not overlap within the runtime; overlapping triggers are consumed and skipped. Read/spawn failures and exit/stderr diagnostics produce generic local warnings, never environment file or stderr contents. Stdout is deliberately unrestricted: scripts must not print secrets unless intended for the model/session transcript. Output is held in memory until process close, so keep it appropriately sized and UTF-8 encoded. Scripts have the user's permissions and inherited environment; this is not a sandbox.
Delivery is live-session-only: closing/replacing/reloading the owning runtime prevents stale callbacks from injecting into another session. There is no durable script-output replay or result inbox. Finish scripts before reload to retain delivery and overlap tracking. Running scripts are not cancelled by schedule deletion, disablement or session pause.
No queueing or catch-up
- Schedules run only while the owning session is open in this project. No daemon or independent scheduler process is started.
- Triggers missed while closed, paused, reloading or asleep are discarded. Recurring schedules advance to their next future deadline while keeping their original phase; expired one-shots are consumed, not replayed.
- A one-second polling timer allows up to 1.5 seconds of scheduling jitter. Longer delays skip the trigger. Multiple simultaneously due main jobs do not become a queue.
- Triggers are durably consumed before dispatch. A crash between consumption and execution can lose that run; it will not be replayed.
- History records dispatch/skip decisions, not proof of model execution or script success. A pause during environment-file reads can prevent spawning after history records
started. - Re-enabling discards missed triggers. Expired one-shots cannot be restarted; create a new schedule instead.
- Only resume an owning session in one process at a time. Separate sessions can each have schedules.
Temporary session pause
schedule_pause sets an optional in-memory, session-local pausedUntil timestamp:
{ "pausedUntil": "2030-01-01T12:30:00Z" }
nullor"": clear the pause.- Timestamp in the past or exactly now: normal scheduling.
- Timestamp in the future: skip scheduled launches; due triggers are discarded, not deferred.
Use an absolute ISO timestamp with explicit timezone, not a duration. Each valid request replaces the value. There is no default duration, pause file or catch-up queue. Session replacement, reload and restart clear it. Already-running scripts continue and their nonempty stdout is still delivered. Schedule management remains available.
Cooperative extension interface
The existing event names remain stable for compatibility. Another extension in the same Pi session may use pi.events without a model turn. Register the response listener before emitting; acknowledgment can be synchronous.
Request channel: pi-background:pause
{
requestId: "unique-correlation-id",
sessionId: ctx.sessionManager.getSessionId(),
pausedUntil: "2030-01-01T12:30:00Z" // or null / "" to clear
}
Response channel: pi-background:pause:result
{
requestId: "unique-correlation-id",
sessionId: "the-matching-session-id",
ok: true,
pausedUntil: "2030-01-01T12:30:00.000Z" // normalized UTC, or null
}
Invalid timestamps return ok: false with a generic error without changing the previous pause. Missing/blank/oversized request IDs, different session IDs and requests without an active owner are ignored. Correlate both IDs and require acknowledgment; emitting alone is not proof of compatibility. An acknowledgment means the timestamp was applied, not that processes were cancelled. This cooperative in-process API is not an access-control boundary.
Upgrade and historical data
Storage stays at <project>/.pi/background/schedules.json, version 1, with the existing .state.lock heartbeat/atomic-replacement protocol. No path or ownership migration is required for existing main and script schedules. Add .pi/background/ to the project's .gitignore.
Existing target: "background" records are accepted for historical compatibility but never consumed or executed, regardless of the stored enabled flag. schedule_list shows active: false and an explicit unsupportedReason, retaining the original target, settings and last-run history. Enabling these records fails. They may be explicitly disabled or deleted; to replace one, explicitly create a main or script schedule. They are never silently converted.
Old registry files, result files, prompts, metadata, event logs and stderr logs remain untouched on disk. The new code neither reads nor updates the worker registry, reaps old tasks, acknowledges results, nor announces completions. Historical files may contain sensitive prompts, thinking, outputs, credentials or request identifiers; inspect them manually only as authorized. No automatic deletion or cleanup occurs.
Before adopting the refactor, finish outstanding workers/scripts and close or reload old extension instances deliberately. This checkout does not stop old processes or change already-loaded extensions. An old runtime can still execute its old background schedules or write completion history; do not run mixed versions against the same project. Abandoned storage locks become recoverable after two minutes; do not manually remove live locks.
Architecture and development
src/index.ts: extension entry point, registering only scheduler tools/lifecycle.src/runtime.ts: session ownership, timer dispatch, pause, script stdout delivery and tool definitions.src/schedules.ts: validation, durable schedule format and no-catch-up trigger consumption.src/storage.ts: locked read-modify-write and atomic schedule persistence.src/script.ts: Python execution and per-run child environment injection.src/pause.ts: timestamp validation and stable cooperative event names.
npm ci
npm run validate
npm run pack:check
npm audit --audit-level=low
Try the checkout in Pi with pi -e . only when ready to load it. CI checks type safety, tests and package contents on Windows/Linux with Node.js 22/24. Tests cover exact tool registration, ownership, lifecycle, pause, no-catch-up behavior, concurrent storage, inactive legacy targets, historical-file preservation and script delivery without model calls. Real-Python tests require python3 on PATH and skip when unavailable. Only the six listed source modules, package metadata, README and license are published.
Publishing
Releases of @comput/pi-background publish automatically only when a version tag is pushed. Ordinary commits and GitHub Release publication do not publish; manual dispatch is disabled. Obtain explicit authorization before committing, pushing, tagging or publishing.
Trusted Publishing uses GitHub Actions owner mbundgaard, repository pi-background, workflow publish.yml (no GitHub environment), OIDC and provenance, not a stored npm token.
- Review changes and choose a stable
x.y.zversion. Updatepackage.json, both version fields inpackage-lock.json, and the changelog. This refactor intentionally has no version bump yet. - Run
npm ci,npm run validate,npm audit --audit-level=low, andnpm run pack:check. Verify the exact allowlist in.github/workflows/publish.ymlmatches package contents. - Confirm the proposed version is unused on npm and its tag absent locally/remotely. Authentication or network errors are not proof of absence.
- With authorization, commit and push to
main; verify CI for that exact release commit. - Create an annotated
vX.Y.Ztag pointing to that commit and push only that tag. The publishing workflow verifies matching package/lockfile versions, validates and checks package contents before publishing. Do not trigger a second publication. - Verify the workflow's commit, npm version/dist-tag, downloaded tarball file list and hashes,
dist.integrityand provenance attestation against the release. Workflow success alone is not artifact/provenance verification. On uncertainty, inspect first; never blindly retry publication, move a release tag or reuse a version.
Publishing does not install the extension or reload running sessions. Those remain separate actions.