Arbe’s LLM driver is @earendil-works’s
pi-mono stack. At runtime we use pi-ai, pi-agent-core and the pi CLI binary (pi-coding-agent). Upstream
READMEs are the source of truth for each API — read one with gh api repos/earendil-works/pi-mono/contents/packages/<pkg>/README.md --jq .content | base64 -d
(<pkg>: ai, agent, coding-agent).
- pi-ai — one LLM API across providers (Anthropic, OpenAI, Google, …), and arbe’s provider
boundary: import
@earendil-works/pi-ai/built-in provider factories, never a provider SDK like@anthropic-ai/sdk. AContextis plain serialisable data, so a conversation can move between providers mid-run. - pi-agent-core — the tool-calling loop over pi-ai. In-thread replies run on its
Agent. - pi-coding-agent — the
piCLI binary. Coding runs drive it in RPC mode on the box. To resume on a fresh box, arbe writes a pi session file (version: 3) rebuilt from the thread. Its entries form anid/parentIdtree, and compaction replaces old entries with one summary, so a rebuild that skips an entry type silently drops conversation state.
Two engines, one per kind of work:
bot reply (Absurd dispatch task) pi-agent-core Agent, a checkpointed step per model call and tool (packages/core/dispatch/agent-turn.ts), run in the backstagedelegated coding run arbe-pi-runner drives `pi --mode rpc` on the box → threadBoth share the rest. A human message sent while either works steers it: the reply takes it
in after its current tool round, pi after its current step. Both end through one decider,
decideAgentOutcome (core/pi/outcome.ts), which reads the last assistant message, a stop,
the process exit or a refused key as completed, cancelled or failed with one plain message.
Both call the same arbe tools: a reply directly, a coding run through pi’s arbe-tools
extension and /api/threads/:id/tools, as the bot that delegated it
(dispatch). Env-bound replies reach the sandbox through
run_command; a coding run is stopped, steered and resumed as described in
daytona runtime.
Sessions and decoding
Arbe owns the per-thread session directory, scoped by arbe thread id:
arbe-sessions/<threadId>/ under the sandbox’s home. A resumed run on the same box passes
--continue; a fresh box gets a session rebuilt from the thread and --session.
pi’s JSON wire decodes through decodePiEvent in packages/core/pi/events.ts into
pi.assistant, pi.tool_result and pi.compaction envelopes, plus signal.thread.pi_retry when pi retries a failed model
call (the failed attempt’s pi.assistant is dropped). arbe-pi-runner reads the same wire
from pi’s RPC stdout in the box and posts it to the thread; a run ends at agent_settled,
not agent_end, since pi can still retry, compact or take a queued steer after agent_end.
There is no live typing text. Each finished message lands as it completes: an in-process
reply per tool round, a coding run per pi message_end; meanwhile the thread’s
current view shows the bot’s phase (“writing…”) and any tool still
running, for a reply and a coding run alike. A
few old threads still hold typing text (pi.message_start, pi.message_update, pi.chunk);
it parses and is never shown.
pi is a process boundary
@arbe/core and @arbe/cli must not import pi-coding-agent at runtime. Its module init
reads its own package.json, which fails under bun build --compile, and its heavy deps
(a TUI, WASM, zip/file-type/glob, and more) would balloon every @arbe/core import. So
treat pi as a process boundary and decode its wire or file output at arbe-owned seams.
Auth
OPENROUTER_API_KEY is the base key for in-process bot replies
and sandbox pi turns. Direct provider keys such as ANTHROPIC_API_KEY work only when the
chosen model ref uses that provider. Both are server-side only: the browser talks to route
handlers, never pi-ai. Bots use model refs (agent.model, defaulting to
DEFAULT_BOT_MODEL in @arbe/core/schemas/agent-model). A person who connected their ChatGPT plan runs their own turns on it, through
pi-ai’s OpenAI provider with a plan access token (pi auth).
Code: packages/core/pi/events.ts,
packages/core/pi/models.ts, packages/core/dispatch/. Proof:
bun run apps/www/scripts/prove-pi-ai.ts. See system/dispatch,
system/auth, system/secrets.