Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Pi provider (your chosen model, in Pi's harness)

A local coding-agent CLI that talks to model providers directly, so a Pi session is your chosen model in Pi's harness rather than Claude in a wrapper — including a local model running on your own machine through Ollama, free and offline. Set up with the binary plus your own keys; text-only in this first version.

What it is

Omniscio can spawn Claude-Code-style sessions backed by Pi (pi.dev — the @earendil-works/pi-coding-agent CLI, run as pi --mode rpc) as an alternative provider, alongside claude (the default), codex, gemini, antigravity, the Anthropic-compatible deepseek / kimi / glm / minimax / meta, opencode, cursor, hermes, grok, and openclaw (the registry defines 26 session-provider keys, 20 of them user-selectable/pickable; the non-pickable ones include the managed-PTY terminal engine and the internal-only openclaw). Pi is a local CLI coding agent that talks to model providers directly and runs any model you pick — Claude, GPT, Gemini, or anything reachable through OpenRouter — so a Pi session is "your chosen model in Pi's harness," not Claude in a different wrapper. Pi can even run a local model on your own machine via Ollama (ollama/<tag>) — free, private, and offline, with no API key (see "Local models" below).

Where to find it

Settings → Accounts — the Pi section stays hidden until you flip Show alternative AI providers. Pi has no sidebar project of its own, so you spawn a Pi session inside any real project.

How it behaves

What the user sees

A Pi session looks almost identical in the sidebar and main pane to a Claude session — same status dots, same streaming bubbles, same Ctrl+Enter to send, same archive / pause / snooze. The differences:

  • Pi glyph (a small π mark) in the session header next to the title. ProviderBadge is icon-only (no text pill) by product decision; Pi is one of the monochrome providers (like Codex and Kimi), so its glyph renders in the neutral surface color rather than a tinted pill — colored marks (Gemini, Anti-Gravity, Cursor) keep their brand color. The header shows every started session's engine mark next to the title — Claude included (in its brand orange), on desktop and mobile alike.
  • Token-by-token streaming. pi --mode rpc emits a JSON event stream, so the reply appears incrementally; one final push replaces the bubble with the full text when the turn completes.
  • Tool-use auto-approval is ON. Pi runs its tools (file writes, shell commands) without per-call prompts. The "Allow Pi sessions" toggle gates whether you can spawn it at all; once spawned it does not ask before acting. (This is the v1 scope — Pi's interactive approval protocol is deferred; see "v1 scope" below.)
  • Real per-session dollar cost. Pi reports authoritative cost and token counts for every turn, so the Cost line on a Pi session shows actual spend — unlike Codex (records tokens but $0) and Cursor (records $0). Spend rolls up into the Stats virtual project the same way Claude's does.

How to enable

Master toggle required first. Pi is an alternative provider — on a fresh install Pi is already visible (the master toggle is ON by default since 2026-10-03); on a profile that last saved before that setting existed the entire Pi setup section stays hidden in Settings → Accounts until you flip it on. Flip Settings → Accounts → Show alternative AI providers to ON and the Pi (CLI provider) panel appears. With the master off, the per-launch provider switcher hides Pi — it effectively does not exist in the UI even if every gate below is configured. Your "Allow Pi sessions" toggle and model choice are preserved across master-toggle flips.

Fastest path — "Set it up for me." The Pi panel leads with a Set it up for me button: one click turns Pi on, installs the pi CLI for you (via Omniscio's toolchain installer), and — because Pi is model-agnostic — sets it up with whichever provider key you already have saved (Anthropic, OpenAI, Google, or OpenRouter), choosing that provider's default model so Pi is usable immediately. Keys are checked in priority order (Anthropic → OpenAI → Google → OpenRouter) entirely in the main process — the key value never crosses to the UI. The outcome shows in place, right in the Pi card: "Pi is set up to use GPT-4o — ready," an installing or error state, or — when you have no provider key at all — a clear "Pi needs an API key from Anthropic, OpenAI, Google, or OpenRouter; add one in the sections above" note. Pi's own toggle stays off until a usable key exists, so once you add one, click the button again to finish. (Your Claude subscription sign-in can't be used by Pi — it needs an actual API key.) The three manual gates below are exactly what that button automates — governed by provider-registry-contract.md.

Three gates must all pass (getProviderSpawnReadiness enforces them in order: toggle → binary → model/key resolver):

The toggle leads and gates the panel UI. The Pi panel shows ONLY the Allow Pi sessions toggle until you turn it on; the binary-install note and the model picker appear once it's enabled — so a model can't be picked for a switched-off feature. When off, a one-line hint stands in and still carries the Settings-search anchors (pi-binary, pi-model) so deep-links land.

  1. Allow Pi sessions toggle — off by default. Same security stance as the other auto-approving providers (Gemini, Cursor): enable only because you intend to use it. Lives in the Pi panel as Allow Pi sessions in any project, and it's the first thing you see (revealing the steps below).
  2. Pi CLI binary — the pi binary must be on your PATH. The Set it up for me button installs it for you (npm install -g @earendil-works/pi-coding-agent under the hood, clearing the binary-resolver cache so the install is seen immediately); to do it by hand, run that command and restart Omniscio so the running process picks up your updated PATH. (Pi is registered in Omniscio's toolchain installer with defaultSelected:false, so it's installable on demand but never auto-installed in the first-run "install recommended tools" sweep.)
  3. Model + API key — pick a model in the panel's Model dropdown (or type any provider/model id in the Custom box). Pi reuses the provider keys you already entered above — there is no separate Pi key. The required key depends on the model you pick (see "Model routing" below); if that provider's key is missing, the gate reports key-missing and deep-links to the right field.

Once all three are green, you launch a Pi session three ways:

  • Per-project default — the Edit-Project "Default provider" radios and the "+ New Session" split-button include Pi (it's a pickable provider). New sessions in that project spawn Pi automatically.
  • Per-launch override — on a fresh (zero-message) session, the Change-provider button in the new session's main panel (the launch-config pickers on a fresh session) lets you pick Pi before sending the first message. Non-ready providers appear disabled with a "Set up… / Install… / Add key" suffix that deep-links into the relevant Settings panel.
  • Programmatically — anything that creates a session with provider: 'pi' (recipes, the CLI control server, agent-driven sessions). The same three readiness gates apply on the backend.

Model routing — any model, your own keys

Pi's chosen model is the piModel setting, a provider/model id (e.g. anthropic/claude-sonnet-4-5, openai/gpt-4o, google/gemini-2.5-pro, openrouter/moonshotai/kimi-k2). The provider prefix (first path segment) selects which env var carries the credential and which stored key Omniscio injects. An unset / empty piModel resolves to the concrete default anthropic/claude-sonnet-4-5.

Prefix Env var Pi reads Key Omniscio injects (reused from)
anthropic ANTHROPIC_API_KEY the OpenCode (Anthropic) key
openai OPENAI_API_KEY the OpenAI key (shared with Codex)
google GEMINI_API_KEY the Gemini key — note GEMINI_API_KEY, not GOOGLE_* (Pi-specific gotcha)
openrouter OPENROUTER_API_KEY the raw user OpenRouter key (cost guard — never Omniscio's internal-fallback key, so your full coding session can't bill Omniscio's shared account)
ollama — (none) none — a LOCAL model served by Ollama; Omniscio writes a managed models.json and injects PI_CODING_AGENT_DIR instead of a key (see "Local models" below)

The key always rides the spawn environment, never the command line. Verified against Pi's own source (packages/ai/src/env-api-keys.ts): Pi reads these vars straight from process.env with no /login flow and no ~/.pi config file required, so injecting the key at spawn is sufficient for headless operation. A model whose prefix isn't one of the four cloud providers or the local ollama prefix reports model-unsupported (the v1 scope; Bedrock/Vertex/Mistral/Groq env vars are documented in Pi but unverified here).

Local models (Ollama) — free, private, offline

Pick a model prefixed ollama/ (e.g. ollama/qwen2.5-coder:7b) to run it on your own machine through Ollama instead of a cloud provider — no API key, no dollar cost, nothing leaving your computer. The Pi Model dropdown lists the models you've pulled in Ollama automatically (fetched live via the ungated pi:list-local-models IPC → /api/tags); you can also type any ollama/<tag> in the Custom box.

How it works. Pi has no base-URL flag — it learns about a custom provider only from a models.json under its config dir. So Omniscio writes its OWN models.json (a custom OpenAI-compatible provider pointed at Ollama's local endpoint http://127.0.0.1:11434/v1, with a dummy keyless apiKey) into an Omniscio-managed home (<userData>/pi-local-home/), and injects PI_CODING_AGENT_DIR at spawn for local sessions only — so your real ~/.pi config is never touched. Verified against pi 0.79.1: PI_CODING_AGENT_DIR=<D> makes pi read <D>/models.json. The file lists a few seed coding models, but any model you've pulled runs — pi clones a seed's routing for an unlisted tag — so the file is static (written once, never refreshed from your installed set). The model resolver adds a keyless ollama/<tag> route (kept out of the keyed cloud prefixes) and allows : in tags.

Pre-flight + errors. A synchronous readiness gate can't probe the network, so Omniscio checks Ollama at spawn (reusing Local Chat's listInstalledModels): if Ollama isn't running you get "Ollama isn't running — start Ollama…", and if the model isn't pulled, "…isn't pulled yet. Run: ollama pull <tag>" — both as clear turn errors that deep-link to the Pi settings, never a cryptic pi/Ollama failure mid-turn.

Cost + quality. Local turns record a truthful $0 — Ollama reports real token counts but no dollar cost, so the turn_end.message.usage cost is 0 (cost-is-real-and-accumulated-per-turn path unchanged). Local models are meaningfully weaker coders than Claude: best for light edits, offline work, and privacy-sensitive tasks — pick a coding-tuned model like qwen2.5-coder, not a chat model.

Caveats (v1): the local picker is Settings-only (the per-session pre-first-message picker and accurate per-model context-window sizing are fast-follows); the endpoint is fixed to Ollama's default port; reasoning models (e.g. gpt-oss) that need extra compat flags aren't specially handled. See provider-registry-contract a-local-model-routes-keyless-in-an-owned-home.

Error states and fixes

Three gap codes (same shape as Codex / Gemini / OpenCode — toggle, binary, then a model/key resolver gate):

Gap What it means Click-to-fix lands you at…
toggle-off "Allow Pi sessions" is OFF in Settings Settings → Accounts → Allow Pi sessions toggle
binary-missing pi CLI not found on PATH Settings → Accounts → Pi CLI binary note
key-missing The chosen model's provider has no key configured Settings → Accounts → that provider's key field
model-unsupported The piModel id isn't a routable provider/model Settings → Accounts → Pi Model picker

No virtual project

Pi has no dedicated __pi__ sidebar entry (unlike __codex__ / __gemini__ / __antigravity__), so the "Allow Pi sessions" toggle always applies — you spawn Pi inside any real project. (A PI_PROJECT_ID = '__pi__' sentinel is reserved in the readiness config for a future virtual project, but none is created in v1, so the sentinel bypass never fires.) Pi IS a per-project default and IS switch-to-able, which makes it "first-class" like Cursor — unlike OpenCode.

v1 scope (what's deferred)

Pi v1 keeps the surface small and is { images: false, mcp: false, cloud: false, sshRemote: false, permissionPrompts: false, multiTurn: true }:

  • No image input — text-only messages.
  • No MCP — Pi sessions don't get Omniscio's MCP servers, and Pi is absent from the provider-config-sync targets.
  • No approval prompts — tools auto-execute. Pi does have an interactive extension_ui_request protocol; wiring it through Omniscio's shared permission UI (like Codex) is future work.
  • Resume is Omniscio context-replay, not Pi's native session restore — Pi's switch_session + sessionPath resume-by-file-path exists in the protocol but isn't wired. Instead Omniscio resumes a stopped Pi session by re-spawning it and replaying the stored conversation as a context prefix on the next message (the shared base helper), so you continue seamlessly even though Pi's own file-path resume is unused.
  • Protocol verified against source, not a live binary. The exact RPC message field names were confirmed against the badlogic/pi-mono TypeScript during the build, not a live pi --mode rpc run. A live end-to-end smoke test with the real binary is the recommended next verification. See the contract's "Known gaps."

For agents

How it works under the hood

One long-lived child process per session. Pi is a persistent-external provider (the same runtime class as Codex). For each session Omniscio spawns pi --mode rpc --model <provider/model> once and keeps it alive; multi-turn is native — the same process handles every message, no --resume flag needed during the session's lifetime. Spawning is lazy by default: launch() registers an in-memory session (status='ready') with no child, and the process starts on the first message (kill switch: AMC_DISABLE_PI_LAZY_SPAWN=1 spawns eagerly at launch). When Omniscio quits, the child dies (Windows: via the orphan-kill Job Object). On Windows pi launches only INSIDE that job — the job-gate launcher holds it until Omniscio has put it there — and a launch that cannot be contained is refused ("Failed to start Pi session: Windows process-tree ownership …") rather than run outside it. A forced stop kills the whole process tree. Pi keeps no on-disk transcript to natively rehydrate, but — like Codex and OpenClaw — sending a message to a stopped Pi session re-spawns it and replays Omniscio's stored conversation as context (the shared applyResumeContextPrefix helper on BaseExternalSessionManager), so you continue where you left off (2026-06-10).

Strict JSONL framing (the load-bearing gotcha). Pi speaks newline-delimited JSON over stdin/stdout. The client (pi-rpc-client.ts) splits stdout on \n only, strips a trailing \r, buffers a partial trailing line across chunks, and tolerates non-JSON lines — it must never use a generic line reader like Node's readline, which also splits on Unicode separators (U+2028) that can occur inside a JSON string payload and would corrupt the stream. Pi's protocol differs from Codex's JSON-RPC in two ways the client relies on: there is no startup handshake (the process is usable the moment it spawns — the session comes from the --model flag), and agent events are forwarded verbatim, keyed by a top-level type, with no envelope. Only command results are wrapped as { type: 'response', command, success, id? }, correlated by an optional id the client echoes.

Streaming translation. Assistant text arrives as message_update events whose assistantMessageEvent.delta (when its type is text_delta) is the chunk to append — streamed to the bubble via SESSION_OUTPUT with streaming: true. Each completed tool action (tool_execution_end) emits the same ▸ call / ← result marker pair Claude and Codex use, so the shared real-conversation layout folds a Pi turn's intermediate activity exactly the way it folds Claude's. The run settles on a single agent_end event → Omniscio finalizes the message (one SESSION_OUTPUT with streaming: false carrying the full text, REPLACE semantics) and moves the session to Needs You.

Real cost tracking. Pi computes actual dollar cost from its own pricing table and reports it on every turn_end inside message.usage ({ input, output, totalTokens, cost: { total } }). The manager accumulates per-turn usage across a run and writes the real costUSD + token counts to api_cost_log via trackApiCostRaw — source: 'pi', synthetic account pi-shared, model label = the resolved provider/model id. This is why Pi sessions show a non-zero Cost line where Codex/Cursor show $0.

Crash recovery. A pi process that exits without a graceful stop (crash / external kill) fires the client's exit handler; the manager surfaces a "Pi process exited unexpectedly" system message, sets the session to error, and drops it from the map — so a dead Pi session is never stranded running (the liveness reconciler skips non-Claude providers). Status transitions, the stall watchdog, the first-response timer, the terminal-status resurrect guard, and the resume-context replay helper are all inherited from BaseExternalSessionManager (shared with Codex / OpenClaw).

Routing. A SESSION_LAUNCH with provider: 'pi' resolves piSessionManager from the session-manager registry (getAltSessionManager('pi')) and goes through the shared gated-launch path — no per-handler branch. The one hand-written Pi branch is the send path in session-service.ts (provider === 'pi'): it persists the operator row and routes to piSessionManager.sendMessage (text-only, no QuestionWidget hint). Interrupt / terminate / restart / change-provider all dispatch through the same registry, so Pi inherits the full lifecycle with no extra wiring. Telemetry: every successful Pi spawn fires the spawn_non_claude_session feature event recording { provider: 'pi' } only — no session id, project id, or prompt content.

Files

  • src/main/services/engines/pi-session-manager.ts — multi-turn lifecycle, lazy spawn, real-cost accumulation, crash recovery (singleton piSessionManager); extends BaseExternalSessionManager
  • src/main/services/engines/pi-rpc-client.ts — the pi --mode rpc stdio JSONL client; first place to look on output drift (strict \n-split framing, event→callback mapping, usage extraction)
  • src/main/services/engines/pi-model-resolver.ts — pure provider/model → --model arg + env var + reused user key router (plus the keyless local ollama/<tag> route)
  • src/main/services/engines/pi-local-home.ts — the Omniscio-managed local home + static models.json writer + the Ollama reachable/pulled pre-flight (preparePiLocalHome, reusing Local Chat's ollama-client)
  • src/main/services/engines/pi-binary-resolver.ts — locate pi on PATH (cached)
  • src/main/services/providers/main-registry.ts — Pi readiness entry (toggle + binary + the model-resolver gate)
  • src/main/services/providers/session-manager-registry.ts — SESSION_MANAGERS.pi = piSessionManager dispatch
  • src/shared/providers/registry.ts — PROVIDERS.pi descriptor (persistent-external, pickable, pickerOrder 8)
  • src/shared/types/provider-readiness.ts (ProviderId union), src/shared/types.ts (PI_PROJECT_ID), src/shared/types/settings/accounts-providers-settings.ts (allowPiSessionSpawn), src/shared/types/settings/ai-features-settings.ts (piModel)
  • src/main/services/session/session-service.ts — the one provider === 'pi' send branch
  • src/renderer/src/components/ui/PiIcon.tsx (π mark), src/renderer/src/features/sessions/ProviderBadge.tsx (icon-only badge; pi registered as a monochrome colored: false entry), src/renderer/src/features/sessions/ChangeProviderButton.tsx (per-launch switcher)
  • src/renderer/src/features/settings/sections/accounts/AccountSettings.tsx — the Pi (CLI provider) Settings section: allow-toggle, binary note, and the model picker (cloud quick-picks + a live "local (Ollama)" list + Custom box)
  • .claude/memory/contracts/provider-registry-contract.md — the feature contract (9 invariants incl. a-local-model-routes-keyless-in-an-owned-home local-Ollama routing + residual-branch note + known gaps)
  • .claude/memory/contracts/provider-registry-contract.md — the one-click "Set it up for me" flow (toolchain install + key reuse in main + arm-toggle-last) that automates the three gates above

Comparison with other providers

Concern Pi Codex OpenCode
Auth Your provider's own key (env, reused) OpenAI API key Anthropic API key (ANTHROPIC_API_KEY)
Underlying model Any (Anthropic / OpenAI / Google / OpenRouter) OpenAI Codex models Claude (Sonnet) via your key
Process model Long-lived pi --mode rpc (JSONL stdio) Long-lived JSON-RPC app-server Shared persistent opencode serve
Multi-turn Native; stopped-session resume by context-replay Persistent process Native (HTTP to shared server)
Cost emitted Yes — real $ per turn Tokens only (records $0) Yes — authoritative, to api_cost_log
Approval prompts No (auto-exec; protocol exists, deferred) Yes (shared permission UI) No
Readiness gates 3 (toggle + binary + model/key resolver) 3 (toggle + binary + key) 3 (toggle + binary + key)
Per-project default Yes Yes No (excluded from radios)

Related

Related

  • codex-provider.md — the closest analog: same persistent-external long-lived child + per-project default, but OpenAI-only and with real approval prompts
  • opencode-provider.md — the closest analog for the any-model credential routing (same provider/model → env-var pattern)
  • cursor-provider.md — another first-class provider (pill + per-project default), but a per-turn one-shot runner
  • ai-providers.md — landing page for the full provider matrix

Last verified 2026-10-06