> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getpioneer.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Client Architecture

> How desktop, mobile, shared Rust client code, FFI, and generated schemas fit around the gateway protocol.

Pioneer clients are shells around a gateway-owned runtime. They render state, collect input, store local preferences, and call the gateway protocol. Assistant execution remains in the gateway.

The current client stack has three layers:

| Layer              | Code                            | Responsibility                                                                                                                                                                                                                                                                                         |
| ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Shared client core | `crates/client`                 | Shell-neutral Rust logic for gateway transport, protocol requests, notification reduction, read models, selectors, timeline rows, composer and voice turn planning, permission-mode display, provider/model helpers, MCP/skill presentation, settings, artifacts, AGENTS.md, tasks, and DTO contracts. |
| Native boundary    | `crates/client-ffi`             | C ABI and JSON method boundary for shells that cannot link Rust APIs directly. It wraps `pioneer-client`, catches panics, serializes tagged JSON responses, exposes generated DTO schemas, and records client diagnostics.                                                                             |
| Shells             | `crates/desktop`, `pioneer-app` | UI rendering, navigation, storage, native dialogs, local gateway process management where supported, platform permissions, localization, and OS integration.                                                                                                                                           |

## Runtime boundary

The gateway remains authoritative for:

* workspaces, threads, turns, tasks, artifacts, skills, MCP, provider keys, and settings;
* tool execution, MCP server processes, CLI agent runtime sessions, provider calls, and task scheduling;
* persistence through `gateway.db`, secret storage through `keystore.db`, and notification fanout to connected sessions.

Client shells should not duplicate those decisions. They should use shared client helpers when the behavior is protocol-facing or reusable across shells, and keep only platform-specific behavior in the shell.

## Shared state flow

The client architecture uses protocol events and reducers:

1. A shell starts or selects a gateway connection.
2. `pioneer-client` opens the WebSocket transport and sends typed JSON-RPC requests.
3. Gateway notifications arrive as protocol events.
4. The shared client core reduces those events into shell-friendly state: connection status, workspace catalog, thread tree, active timeline, composer draft, selected permission mode, provider/model options, MCP/skill picker rows, settings snapshots, and task review state.
5. The shell renders those projections and sends user actions back through shared helpers.

When behavior should match across clients, the shell should not parse raw gateway events into its own UI state. Put reducers, selectors, display names, validation, and request planning in `crates/client` unless the code touches GPUI, React Native, OS APIs, app storage, or native dialogs.

## Desktop shell

`crates/desktop` is the native GPUI desktop app. It can start and manage a local gateway, connect to remote gateways, store desktop gateway bearer tokens through the desktop secret layer, and render the full conversation/workspace UI.

Desktop-specific code owns windows, menus, GPUI views, local gateway install/start flows, OS open/reveal actions, desktop registry files, and other native UI concerns.

Important desktop areas:

| Area                 | Code                                                | Notes                                                                                                                                    |
| -------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| App flow             | `crates/desktop/src/app/flow/*`                     | Bootstrap, connection event pump, workspace bootstrap/switch, thread start queues, turn resume queues, and gateway lifecycle operations. |
| Root state           | `crates/desktop/src/app/root/*`                     | Cross-screen state, model selection, root queries, and mutations.                                                                        |
| Conversation UI      | `crates/desktop/src/app/thread/view/*`              | Composer, timeline, approvals, artifacts panel, header, running state, and timeline item views.                                          |
| Providers/MCP/Skills | `crates/desktop/src/app/providers`, `mcp`, `skills` | Shell UI around shared client queries and gateway protocol calls.                                                                        |
| Settings             | `crates/desktop/src/app/settings/*`                 | Gateway-owned settings, remote access controls, memory/thread-context toggles, and local presentation.                                   |

Desktop may manage a local gateway process because it runs on a workstation-class OS. That responsibility should not move into `pioneer-client`; it is shell/OS integration.

## Mobile shell

`pioneer-app` is the Expo/React Native mobile app. It uses:

* Expo Router routes under `src/routes`;
* screens under `src/screens`;
* Zustand stores for gateway, workspace, thread tree, active thread, editor, and CLI runtime state;
* generated TypeScript contracts under `src/client/generated`;
* schema exports under `src/client/schema`;
* `@pioneer/client-nitro` for the Rust FFI bridge.

Mobile stores its gateway registry in MMKV-backed local storage and bearer tokens in Expo SecureStore. It connects to remote gateways; it does not currently supervise a local gateway process on the phone.

Mobile should call through the Nitro module for shared behavior rather than reimplementing Rust reducers in TypeScript. TypeScript owns screens, navigation, local store orchestration, platform permissions, localization, file picker behavior, microphone capture, and presentation glue.

## Shared client core

`pioneer-client` is intentionally shell-neutral. Code belongs there when it can run without GPUI, React Native, native dialogs, or OS-specific process management.

Good candidates for `pioneer-client` include:

* JSON-RPC request helpers and WebSocket command sender abstractions;
* protocol-to-read-model reducers for conversations and timelines;
* gateway registry planning and remote endpoint validation;
* workspace bootstrap/switch/create/rename workflows;
* provider and CLI runtime model selector helpers;
* reasoning effort option normalization and model selector effort rows;
* composer permission mode options and `TurnPermissionProfileSelection` planning;
* composer attachment, skill, and MCP capability planning;
* artifact transfer/cache helpers behind platform traits;
* voice session command wrappers, frozen voice composer snapshot preparation, result reductions, and binary voice frame helpers;
* AGENTS.md state machines and save/archive helpers;
* public DTOs and schema export.

Keep code in a shell when it needs a window, native picker, app storage, secure storage, localization resources, OS open/reveal behavior, mobile permissions, or local gateway service management.

## Client runtime

`ClientRuntime` is the long-lived shared object behind client operations. It owns connection-oriented workers, request helpers, reducer state, and diagnostic/event streams. Shells should treat it as the local client engine, not as a gateway substitute.

The runtime can validate and plan gateway registry mutations without writing secrets directly. For example, remote gateway add/update planning produces token refs and registry plans; the shell then writes the raw bearer token through its platform secret store. This keeps secret handling platform-specific while keeping registry semantics shared.

## Transport

`crates/client/src/transport/ws/*` contains WebSocket connection machinery:

* connection backoff;
* command sending;
* frame decoding;
* RPC request/response tracking;
* event worker loops;
* download helpers;
* runtime client/command sender abstractions.

Transport code should stay unaware of GPUI and React Native. It is allowed to know protocol envelopes and connection state, but not UI layout.

## Timeline projection

The timeline projection is shared because every client needs the same interpretation of gateway events. `crates/client/src/conversation`, `timeline`, and `threads` turn protocol items into rows such as agent messages, reasoning, tool calls, downloads, system events, task rows, and final statuses.

This layer is where UI-adjacent normalization belongs. For example, a shell should not decide independently how to coalesce tool rows or label a final turn status if the behavior should match desktop and mobile.

The current semantic timeline surface uses `thread/timeline/page` for top-level blocks, `turn/work/page` for paginated work inside a turn block, and `turn/work/items/get` for targeted item refreshes. Live updates such as `thread/timeline/blocks/changed`, `turn/work/items/changed`, and `turn/work/state/changed` invalidate shared caches so shells can reload pages consistently instead of rebuilding timeline layout from raw events.

Collaborative Composer submissions add detached task blocks to that same projection. The shared client must keep the parent message and its task card distinct, update the card from task and timeline notifications, and expose the child conversation for inspection or follow-up without placing it in the normal sidebar tree. When the task reaches a terminal state, the client renders the delivered result in the originating thread and reconciles the card without duplicating the assistant message.

## Composer planning

Composer helpers live in `crates/client/src/composer`. They convert user-selected attachments, skills, MCP capabilities, model selections, permission mode, and draft state into turn-ready protocol inputs. This is where capability rejection rows, permission-mode labels, and picker filtering belong.

The gateway still enforces policy. Client composer planning is a presentation and preflight layer that exposes invalid choices early; it is not an authorization boundary.

Model selection state includes provider, model, optional selected reasoning effort, and whether the selection was manually chosen. Permission mode state is per composer draft and maps directly to `TurnPermissionProfileSelection`. When the selected provider or model changes, shared client logic clears the selected reasoning effort so stale effort values from a previous model are not sent to the gateway. If the selection is resolved from an existing thread, the resolved effort can be carried forward.

The thread origin also determines Composer execution. A `Collaborative` thread launches detached task work, so the client keeps the Composer available and treats the launch settings as immutable for that request. `DirectMessage` and compatibility/internal foreground origins continue to render a normal in-thread turn. This decision comes from the gateway thread contract; client shells must not infer it from sidebar visibility or implement separate desktop and mobile rules.

## Voice planning

Voice input uses the same gateway-owned turn model as text input. The shell owns platform microphone permission and audio capture. The shared client core owns the voice protocol helpers:

* `voice/status`, `voice/session/start`, `voice/session/finalize`, and `voice/session/cancel` command wrappers;
* `VOC1` binary audio chunk frame encoding;
* frozen `VoiceTurnContext` preparation for attachments, capabilities, model selection, reasoning, permission profile, and CLI runtime options;
* result reductions that tell shells whether to keep finalizing, clear state, show no-speech, or show an error.

The client must not insert gateway transcription text into the draft. Successful voice sessions are rendered from normal turn notifications plus thread timeline-page and turn work-page APIs after the gateway starts the turn.

## FFI and schemas

`pioneer-client-ffi` exposes JSON methods such as:

* `gateway_connect`, `gateway_next_events`, `gateway_disconnect`;
* `workspace_bootstrap`, `workspace_switch`, `workspace_create`, `workspace_rename`;
* `provider_list`, `provider_list_models`, `provider_model_display`;
* `reasoning_effort_rows` for model-specific reasoning effort picker rows;
* `cli_runtime_list`, `cli_runtime_list_models`, `cli_runtime_thread_binding_get`, `cli_runtime_turn_steer`;
* `voice_status`, `voice_session_start`, `voice_audio_chunk`, `voice_session_finalize`, `voice_session_cancel`, and `prepare_voice_composer_snapshot`;
* `composer_*` helpers for attachments, skills, MCP capabilities, permission modes, and turn preparation;
* `thread_tree_refresh`, `agents_doc_get`, `agents_doc_save`, `active_thread_*`.

The mobile module wraps those as Nitro methods such as `gatewayConnectJson` and parses tagged JSON responses in TypeScript. Generated schemas make the shell boundary reviewable without making the shell understand gateway internals.

The FFI boundary uses tagged responses:

* `{"status":"ok","value":...}`
* `{"status":"error","message":"...","code":...}`

This keeps the C ABI narrow and avoids exposing Rust panic/unwind behavior across the boundary. `pioneer-client-ffi` catches panics around exported operations and records diagnostics.

Generated client schemas have two jobs. First, they make TypeScript bindings reviewable and reproducible. Second, they make accidental shell-only DTO changes visible during contract review. If a mobile screen needs a new field that is derived from protocol state, prefer adding it to shared client DTOs rather than constructing it ad hoc in TypeScript.

## Generated mobile contracts

`pioneer-app/src/client/generated` and `pioneer-app/src/client/schema` are generated from the Rust client/FFI contract. They include DTOs for gateway registry plans, active-thread snapshots, composer rows, permission mode options, provider/model selection, reasoning effort rows, CLI runtime pending requests, settings, MCP catalog rows, skill diagnostics, task review display, timeline rows, and workspace bootstrap.

Current model-selection, reasoning, security, and voice DTOs include `composer_model_selection`, `composer_model_selection_candidate`, `composer_model_selection_state`, `provider_model_reasoning_capabilities`, `reasoning_capability_source`, `reasoning_effort_row`, `reasoning_effort_rows_request`, `reasoning_effort_rows_response`, `turn_reasoning_selection`, `client_turn_security_summary`, `client_security_diagnostic`, `voice_composer_lock_state`, `voice_finalize_response_reduction`, and `voice_session_result_reduction`.

When changing a shared client DTO:

1. Change the Rust DTO or schema source.
2. Regenerate schemas/types.
3. Update mobile code that consumes the generated type.
4. Keep the shell presentation logic thin.

Do not edit generated TypeScript as the source of truth.

## Design rules

* Gateway authority beats client convenience. Do not make a client shell the owner of workspaces, tasks, provider keys, MCP runtime, skills, or turn state.
* Put cross-client behavior in `pioneer-client` before duplicating it in desktop and mobile.
* Keep FFI methods coarse enough to be stable but small enough to test.
* Keep platform secret storage in the shell; keep registry and validation semantics in shared client code.
* Keep protocol DTOs distinct from client presentation DTOs. Protocol is the gateway contract; client DTOs are shell-facing projections.
* When adding a client-visible gateway feature, update `protocol`, gateway handlers, `pioneer-client`, FFI/schema export when mobile needs it, and the shell views together.

## Related pages

* [Gateway](/architecture/gateway) explains the runtime clients connect to.
* [Protocol Layer](/architecture/protocol) explains the public JSON-RPC contract.
* [Permission System](/architecture/permissions) explains the protocol permission profile that composer state sends with a turn.
* [CLI Runtime Architecture](/architecture/cli-runtime) explains runtime projections, approvals, and turn steering.
* [Mobile App Overview](/mobile/overview) explains the current React Native client.
* [Desktop App Overview](/desktop/overview) explains the GPUI desktop client.
