Architecture
How Parallax works under the hood.
Overview
Most AI CLIs are thin wrappers around the Vercel AI SDK. We aren't.
Vercel AI SDK is phenomenal for web applications, but it abstracts away the exact thought_signature and function-calling schemas that models like Gemini rely on for reasoning loops. To build a genuinely agentic tool that can iteratively read, think, and rewrite local files without losing context, we built the entire stack from the ground up targeting purely Gemini APIs.
Parallax is divided simply into a React-based UI, a manual ReAct engine, and a set of local buffers.
Directory Layout
The core logic lives in src/:
parallax-cli/
├── src/
│ ├── app.tsx # Real-time UI and Keyboard I/O (Ink)
│ ├── agent/ # ToolLoopAgent + Gemini mapping payloads
│ ├── mcp.ts # Model Context Protocol integration
│ ├── tools.ts # Hand-rolled implementations of fs/spawn
│ └── patch-console.js # Muting noisy stdout imports to prevent TUI tearingThe ToolLoopAgent
Instead of hoping a generic generateText wrapper executes tools properly, ToolLoopAgent runs a strict while-loop.
- Serialize Context — Grabs every previous message and system instruction and builds an exact Gemini
contentarray. - Stream — Fetches the stream. The React UI gets fed chunks instantly so it feels blazing fast.
- Intercept — If Gemini decides it needs to invoke
readFile, the loop captures the payload. - Execute & Append — The core executor fires all tool invocations concurrently via a non-blocking
Promise.race()handler that yields UI block updates immediately as they complete while dynamically preserving array ordering for the model. The synchronous result block is injected back into the history array as afunctionResponse. - Escape — It exits only when Gemini provides pure markdown text without trailing tool requests.
The Native IDE Connection
The CLI doesn't just pretend to know where files are; it communicates directly with any connected environment via @google/gemini-cli-core's internal IdeClient. When the agent edits a file using editFile, instead of blindly printing diffs to your terminal, it natively opens an inline diff block directly inside your active IDE editor context. You review and apply (or revert) changes visually using standard VS Code/IntelliJ keystrokes.
The TUI
Because CLI interfaces break easily when rendering async markdown alongside spinning loading bars, we use Ink.
Instead of drawing lines manually, we write actual React components: <ListPicker>, <StatusBar>, <MarkdownBlock>. It renders to the active TTY layer and handles re-draws automatically. We also apply a global patch-console.js override specifically to mute third-party dependencies from printing random debug logs that would permanently smear our CLI output.
Storage and Persistence
Every message turn is saved inside ~/.parallax/[session-id].json. There is no cloud storage, there is no database. It's just a raw dump of the JSON state machine, making it trivial to /load back into memory a week later and resume the exact ReAct loop logic. When the JSON payload starts exceeding 100,000 tokens, the /compact command is specifically engineered to squish it down while retaining the logical checkpoints.