aloita-extensions
A Pi extension: one Pi session connects to an Aloita server and works on tickets natively.
Package details
Install aloita-extensions from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:aloita-extensions- Package
aloita-extensions- Version
0.4.16- Published
- Aug 17, 2026
- Downloads
- 496/mo · 360/wk
- Author
- frankvl76
- License
- MIT
- Types
- extension
- Size
- 558.4 KB
- Dependencies
- 1 dependency · 4 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
aloita-extensions
A Pi extension. One long-running Pi session connects to your Aloita server and works on tickets natively — no separate MCP server or subprocesses.
Install
Requires Pi (@earendil-works/pi-coding-agent) on your PATH and Node 18+.
pi install npm:aloita-extensions
Pi auto-loads the extension on every startup. Manage it with:
pi list # confirm it's registered
pi remove npm:aloita-extensions # unregister
pi update --extensions # update installed packages
Configure
Set your server URL and API key via environment variables:
export ALOITA_URL="https://your-aloita-server"
export ALOITA_API_KEY="kai_your_key_here"
Or run /aloita-configure inside Pi to set them (and other options) in an
interactive settings screen — values persist to ~/.pi/aloita.json and apply
to every Pi session on the machine.
A config.example.json ships with the package; copy it to ~/.pi/aloita.json
(global) or <cwd>/.pi/aloita.json (project-local) and edit.
Launch pi. The footer shows the connection status when the socket is up.
Commands
| Command | Action |
|---|---|
/aloita-status |
Show connection state, active ticket, queue. |
/aloita-connect |
Force reconnect. |
/aloita-disconnect |
Disconnect. |
/aloita-next |
Drain the next queued ticket. |
/aloita-configure |
Interactive settings UI. |
Auto-start: how the settings interact
Two independent things exist — ticket pickup and the auto-start supervisor. They do not affect each other:
- Ticket pickup happens whenever a Pi instance with this extension is running in (or resolves to) a project's folder. It works regardless of any auto-start setting. If you
cdinto a project folder and startpiyourself, it picks up that project's tickets even with every auto-start option off. - The auto-start supervisor (
autoStartProjects, the "Auto-start supervisor" master switch) only controls whether this Pi session additionally launches childpiinstances for other projects. Turning it off never stops ticket pickup — it only stops the launching of new instances.
Per setting:
| Setting | What it actually does |
|---|---|
autoStartProjects (master switch) |
ON: this session polls /api/projects and spawns a child pi per allow-listed project. OFF: this session launches nothing. Ticket pickup is unaffected either way. |
Project allow-list (autoStartProjectIds, the per-project auto-start on/off in /aloita-configure) |
Only projects in this list are ever launched. A project that is OFF is simply never launched automatically — but a manually started pi in its folder still picks up its tickets. |
autoStartSeedExisting ("Launch for all existing projects") |
ON (default): when the supervisor starts, it launches every allow-listed project immediately — start one Pi and the rest of the fleet comes online. OFF: only projects created after the supervisor started are launched. |
autoStartLinuxTerminal |
Linux only: auto (default) opens each child in a visible terminal window (parity with the console windows on Windows); none = invisible background process; any other value = explicit terminal command. Headless sessions (no DISPLAY/WAYLAND_DISPLAY) always fall back to invisible. |
Every spawned child also runs the supervisor loop; duplicate launches are prevented by per-project pidfiles (<folder>/.pi/aloita.autostart.pid) — a project whose pidfile holds a live pid is never launched again.
Error recovery
When a run ends errored — an assistant turn finished with stopReason: "error", almost always a transient model-provider failure (rate limiting / overload) — the plugin automatically requeues the ticket at the front of the queue and re-runs it once (erroredAutoRetry, default true). The failed attempt still posts its usage (flagged as an error run) so the cost view shows one row per attempt; the ticket's final status and run diff come from the retry's outcome. A ticket that fails on the retry too is left in errorStatus (default In Progress) for manual triage — there is no retry loop, and runs cancelled via StopTask are never requeued. Set "erroredAutoRetry": false to restore the immediate-finalize behaviour. See docs/investigation-errored-runs-2026-08-17.md for the incident that motivated this.
Backpressure between runs (drainDelayMs, default 0 = off): when > 0, the automatic drain waits that many milliseconds before starting the next queued ticket after a run ends, so burst-triaged tickets don't hit the model provider back-to-back (useful under rate limiting; suggested 5–15 s). Only the automatic agent_end drain is delayed — the first ticket arriving on an idle session and manual /aloita-next remain immediate, and a StopTask during the window still drops the queued ticket.
Assigned model (complexity tiers)
When Aloita's complexity scan stamps a model on a ticket (custom fields assigned_tool / assigned_provider / assigned_model), the extension switches Pi to that model right before the ticket's prompt is delivered — so the run uses the tier's model, not whatever the session started with. If the assignment targets a different tool, or the model isn't in Pi's registry (check the model list and API keys on this machine), the current model is kept and the reason is logged/toasted.
Note: the previous model is not restored when the ticket finishes — the session stays on the tier's model until another ticket assigns a different one (or you switch manually via /model). This is intentional: a restore would add a second model switch per ticket for no functional gain, and the next assigned ticket would switch again anyway. Changing this would be a deliberate behavior change, not a bug fix.
Logging
Diagnostics go to ~/.pi/aloita.log (never stdout/stderr, which would corrupt
Pi's TUI). Set the level via ALOITA_LOG_LEVEL (debug/info/warn/error/off,
default info) and the path via ALOITA_LOG_FILE.
Developer notes
Import extensions: .ts for runtime imports, .js for import type
Cross-module imports inside src/ follow one rule:
Runtime value imports (anything that loads code, including mixed value + type imports) use the
.tsextension:import { mergeConfigPatch } from "./config-store.ts"; // ✅ runtime — .tsimport typestatements use the conventional.jsextension (they are erased at compile time, so only tsc ever resolves them):import type { AloitaConfig } from "./config.js"; // ✅ type-only — .jsimport { mergeConfigPatch } from "./config-store.js"; // ❌ runtime import with .js
Why: the test suite runs with Node 22's --experimental-strip-types loader (npm test → node --test src/*.test.ts), which resolves specifiers exactly as written — only the .ts sources exist on disk, so a runtime .js specifier fails with a confusing Cannot find module only when a test transitively loads that module. Pi's extension loader (jiti) is lenient and resolves either form, which is why such an import can survive unnoticed in production while breaking the moment a test touches it. tsc accepts both via allowImportingTsExtensions.
Enforced by npm run lint:imports (and CI): it fails on any non-import type cross-module import in src/ that uses .js.
Verify scripts
scripts/verify/ holds one-shot investigation scripts from past tickets — see scripts/verify/README.md. They are not part of the build, tests, or CI.
License
MIT