This release represents a comprehensive transformation of the codebase from Codex to LLMX, enhanced with LiteLLM integration to support 100+ LLM providers through a unified API. ## Major Changes ### Phase 1: Repository & Infrastructure Setup - Established new repository structure and branching strategy - Created comprehensive project documentation (CLAUDE.md, LITELLM-SETUP.md) - Set up development environment and tooling configuration ### Phase 2: Rust Workspace Transformation - Renamed all Rust crates from `codex-*` to `llmx-*` (30+ crates) - Updated package names, binary names, and workspace members - Renamed core modules: codex.rs → llmx.rs, codex_delegate.rs → llmx_delegate.rs - Updated all internal references, imports, and type names - Renamed directories: codex-rs/ → llmx-rs/, codex-backend-openapi-models/ → llmx-backend-openapi-models/ - Fixed all Rust compilation errors after mass rename ### Phase 3: LiteLLM Integration - Integrated LiteLLM for multi-provider LLM support (Anthropic, OpenAI, Azure, Google AI, AWS Bedrock, etc.) - Implemented OpenAI-compatible Chat Completions API support - Added model family detection and provider-specific handling - Updated authentication to support LiteLLM API keys - Renamed environment variables: OPENAI_BASE_URL → LLMX_BASE_URL - Added LLMX_API_KEY for unified authentication - Enhanced error handling for Chat Completions API responses - Implemented fallback mechanisms between Responses API and Chat Completions API ### Phase 4: TypeScript/Node.js Components - Renamed npm package: @codex/codex-cli → @valknar/llmx - Updated TypeScript SDK to use new LLMX APIs and endpoints - Fixed all TypeScript compilation and linting errors - Updated SDK tests to support both API backends - Enhanced mock server to handle multiple API formats - Updated build scripts for cross-platform packaging ### Phase 5: Configuration & Documentation - Updated all configuration files to use LLMX naming - Rewrote README and documentation for LLMX branding - Updated config paths: ~/.codex/ → ~/.llmx/ - Added comprehensive LiteLLM setup guide - Updated all user-facing strings and help text - Created release plan and migration documentation ### Phase 6: Testing & Validation - Fixed all Rust tests for new naming scheme - Updated snapshot tests in TUI (36 frame files) - Fixed authentication storage tests - Updated Chat Completions payload and SSE tests - Fixed SDK tests for new API endpoints - Ensured compatibility with Claude Sonnet 4.5 model - Fixed test environment variables (LLMX_API_KEY, LLMX_BASE_URL) ### Phase 7: Build & Release Pipeline - Updated GitHub Actions workflows for LLMX binary names - Fixed rust-release.yml to reference llmx-rs/ instead of codex-rs/ - Updated CI/CD pipelines for new package names - Made Apple code signing optional in release workflow - Enhanced npm packaging resilience for partial platform builds - Added Windows sandbox support to workspace - Updated dotslash configuration for new binary names ### Phase 8: Final Polish - Renamed all assets (.github images, labels, templates) - Updated VSCode and DevContainer configurations - Fixed all clippy warnings and formatting issues - Applied cargo fmt and prettier formatting across codebase - Updated issue templates and pull request templates - Fixed all remaining UI text references ## Technical Details **Breaking Changes:** - Binary name changed from `codex` to `llmx` - Config directory changed from `~/.codex/` to `~/.llmx/` - Environment variables renamed (CODEX_* → LLMX_*) - npm package renamed to `@valknar/llmx` **New Features:** - Support for 100+ LLM providers via LiteLLM - Unified authentication with LLMX_API_KEY - Enhanced model provider detection and handling - Improved error handling and fallback mechanisms **Files Changed:** - 578 files modified across Rust, TypeScript, and documentation - 30+ Rust crates renamed and updated - Complete rebrand of UI, CLI, and documentation - All tests updated and passing **Dependencies:** - Updated Cargo.lock with new package names - Updated npm dependencies in llmx-cli - Enhanced OpenAPI models for LLMX backend This release establishes LLMX as a standalone project with comprehensive LiteLLM integration, maintaining full backward compatibility with existing functionality while opening support for a wide ecosystem of LLM providers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Sebastian Krüger <support@pivoine.art>
11 KiB
llmx-app-server
llmx app-server is the interface LLMX uses to power rich interfaces such as the LLMX VS Code extension. The message schema is currently unstable, but those who wish to build experimental UIs on top of LLMX may find it valuable.
Protocol
Similar to MCP, llmx app-server supports bidirectional communication, streaming JSONL over stdio. The protocol is JSON-RPC 2.0, though the "jsonrpc":"2.0" header is omitted.
Message Schema
Currently, you can dump a TypeScript version of the schema using llmx app-server generate-ts, or a JSON Schema bundle via llmx app-server generate-json-schema. Each output is specific to the version of LLMX you used to run the command, so the generated artifacts are guaranteed to match that version.
llmx app-server generate-ts --out DIR
llmx app-server generate-json-schema --out DIR
Initialization
Clients must send a single initialize request before invoking any other method, then acknowledge with an initialized notification. The server returns the user agent string it will present to upstream services; subsequent requests issued before initialization receive a "Not initialized" error, and repeated initialize calls receive an "Already initialized" error.
Example:
{ "method": "initialize", "id": 0, "params": {
"clientInfo": { "name": "llmx-vscode", "title": "LLMX VS Code Extension", "version": "0.1.0" }
} }
{ "id": 0, "result": { "userAgent": "llmx-app-server/0.1.0 llmx-vscode/0.1.0" } }
{ "method": "initialized" }
Core primitives
We have 3 top level primitives:
- Thread - a conversation between the LLMX agent and a user. Each thread contains multiple turns.
- Turn - one turn of the conversation, typically starting with a user message and finishing with an agent message. Each turn contains multiple items.
- Item - represents user inputs and agent outputs as part of the turn, persisted and used as the context for future conversations.
Thread & turn endpoints
The JSON-RPC API exposes dedicated methods for managing LLMX conversations. Threads store long-lived conversation metadata, and turns store the per-message exchange (input → LLMX output, including streamed items). Use the thread APIs to create, list, or archive sessions, then drive the conversation with turn APIs and notifications.
Quick reference
thread/start— create a new thread; emitsthread/startedand auto-subscribes you to turn/item events for that thread.thread/resume— reopen an existing thread by id so subsequentturn/startcalls append to it.thread/list— page through stored rollouts; supports cursor-based pagination and optionalmodelProvidersfiltering.thread/archive— move a thread’s rollout file into the archived directory; returns{}on success.turn/start— add user input to a thread and begin LLMX generation; responds with the initialturnobject and streamsturn/started,item/*, andturn/completednotifications.turn/interrupt— request cancellation of an in-flight turn by(thread_id, turn_id); success is an empty{}response and the turn finishes withstatus: "interrupted".
1) Start or resume a thread
Start a fresh thread when you need a new LLMX conversation.
{ "method": "thread/start", "id": 10, "params": {
// Optionally set config settings. If not specified, will use the user's
// current config settings.
"model": "gpt-5-llmx",
"cwd": "/Users/me/project",
"approvalPolicy": "never",
"sandbox": "workspaceWrite",
} }
{ "id": 10, "result": {
"thread": {
"id": "thr_123",
"preview": "",
"modelProvider": "openai",
"createdAt": 1730910000
}
} }
{ "method": "thread/started", "params": { "thread": { … } } }
To continue a stored session, call thread/resume with the thread.id you previously recorded. The response shape matches thread/start, and no additional notifications are emitted:
{ "method": "thread/resume", "id": 11, "params": { "threadId": "thr_123" } }
{ "id": 11, "result": { "thread": { "id": "thr_123", … } } }
2) List threads (pagination & filters)
thread/list lets you render a history UI. Pass any combination of:
cursor— opaque string from a prior response; omit for the first page.limit— server defaults to a reasonable page size if unset.modelProviders— restrict results to specific providers; unset, null, or an empty array will include all providers.
Example:
{ "method": "thread/list", "id": 20, "params": {
"cursor": null,
"limit": 25,
} }
{ "id": 20, "result": {
"data": [
{ "id": "thr_a", "preview": "Create a TUI", "modelProvider": "openai", "createdAt": 1730831111 },
{ "id": "thr_b", "preview": "Fix tests", "modelProvider": "openai", "createdAt": 1730750000 }
],
"nextCursor": "opaque-token-or-null"
} }
When nextCursor is null, you’ve reached the final page.
3) Archive a thread
Use thread/archive to move the persisted rollout (stored as a JSONL file on disk) into the archived sessions directory.
{ "method": "thread/archive", "id": 21, "params": { "threadId": "thr_b" } }
{ "id": 21, "result": {} }
An archived thread will not appear in future calls to thread/list.
4) Start a turn (send user input)
Turns attach user input (text or images) to a thread and trigger LLMX generation. The input field is a list of discriminated unions:
{"type":"text","text":"Explain this diff"}{"type":"image","url":"https://…png"}{"type":"localImage","path":"/tmp/screenshot.png"}
You can optionally specify config overrides on the new turn. If specified, these settings become the default for subsequent turns on the same thread.
{ "method": "turn/start", "id": 30, "params": {
"threadId": "thr_123",
"input": [ { "type": "text", "text": "Run tests" } ],
// Below are optional config overrides
"cwd": "/Users/me/project",
"approvalPolicy": "unlessTrusted",
"sandboxPolicy": {
"mode": "workspaceWrite",
"writableRoots": ["/Users/me/project"],
"networkAccess": true
},
"model": "gpt-5-llmx",
"effort": "medium",
"summary": "concise"
} }
{ "id": 30, "result": { "turn": {
"id": "turn_456",
"status": "inProgress",
"items": [],
"error": null
} } }
5) Interrupt an active turn
You can cancel a running Turn with turn/interrupt.
{ "method": "turn/interrupt", "id": 31, "params": {
"threadId": "thr_123",
"turnId": "turn_456"
} }
{ "id": 31, "result": {} }
The server requests cancellations for running subprocesses, then emits a turn/completed event with status: "interrupted". Rely on the turn/completed to know when LLMX-side cleanup is done.
Auth endpoints
The JSON-RPC auth/account surface exposes request/response methods plus server-initiated notifications (no id). Use these to determine auth state, start or cancel logins, logout, and inspect ChatGPT rate limits.
Quick reference
account/read— fetch current account info; optionally refresh tokens.account/login/start— begin login (apiKeyorchatgpt).account/login/completed(notify) — emitted when a login attempt finishes (success or error).account/login/cancel— cancel a pending ChatGPT login byloginId.account/logout— sign out; triggersaccount/updated.account/updated(notify) — emitted whenever auth mode changes (authMode:apikey,chatgpt, ornull).account/rateLimits/read— fetch ChatGPT rate limits; updates arrive viaaccount/rateLimits/updated(notify).
1) Check auth state
Request:
{ "method": "account/read", "id": 1, "params": { "refreshToken": false } }
Response examples:
{ "id": 1, "result": { "account": null, "requiresOpenaiAuth": false } } // No OpenAI auth needed (e.g., OSS/local models)
{ "id": 1, "result": { "account": null, "requiresOpenaiAuth": true } } // OpenAI auth required (typical for OpenAI-hosted models)
{ "id": 1, "result": { "account": { "type": "apiKey" }, "requiresOpenaiAuth": true } }
{ "id": 1, "result": { "account": { "type": "chatgpt", "email": "user@example.com", "planType": "pro" }, "requiresOpenaiAuth": true } }
Field notes:
refreshToken(bool): settrueto force a token refresh.requiresOpenaiAuthreflects the active provider; whenfalse, LLMX can run without OpenAI credentials.
2) Log in with an API key
- Send:
{ "method": "account/login/start", "id": 2, "params": { "type": "apiKey", "apiKey": "sk-…" } } - Expect:
{ "id": 2, "result": { "type": "apiKey" } } - Notifications:
{ "method": "account/login/completed", "params": { "loginId": null, "success": true, "error": null } } { "method": "account/updated", "params": { "authMode": "apikey" } }
3) Log in with ChatGPT (browser flow)
- Start:
{ "method": "account/login/start", "id": 3, "params": { "type": "chatgpt" } } { "id": 3, "result": { "type": "chatgpt", "loginId": "<uuid>", "authUrl": "https://chatgpt.com/…&redirect_uri=http%3A%2F%2Flocalhost%3A<port>%2Fauth%2Fcallback" } } - Open
authUrlin a browser; the app-server hosts the local callback. - Wait for notifications:
{ "method": "account/login/completed", "params": { "loginId": "<uuid>", "success": true, "error": null } } { "method": "account/updated", "params": { "authMode": "chatgpt" } }
4) Cancel a ChatGPT login
{ "method": "account/login/cancel", "id": 4, "params": { "loginId": "<uuid>" } }
{ "method": "account/login/completed", "params": { "loginId": "<uuid>", "success": false, "error": "…" } }
5) Logout
{ "method": "account/logout", "id": 5 }
{ "id": 5, "result": {} }
{ "method": "account/updated", "params": { "authMode": null } }
6) Rate limits (ChatGPT)
{ "method": "account/rateLimits/read", "id": 6 }
{ "id": 6, "result": { "rateLimits": { "primary": { "usedPercent": 25, "windowDurationMins": 15, "resetsAt": 1730947200 }, "secondary": null } } }
{ "method": "account/rateLimits/updated", "params": { "rateLimits": { … } } }
Field notes:
usedPercentis current usage within the OpenAI quota window.windowDurationMinsis the quota window length.resetsAtis a Unix timestamp (seconds) for the next reset.
Dev notes
llmx app-server generate-ts --out <dir>emits v2 types underv2/.llmx app-server generate-json-schema --out <dir>outputsllmx_app_server_protocol.schemas.json.- See “Authentication and authorization” in the config docs for configuration knobs.