Skip to content

feat: add remote server/client mode over TCP - #20

Merged
apriendeau merged 13 commits into
mainfrom
austin/add-remote-server
May 25, 2026
Merged

apriendeau merged 13 commits into
mainfrom
austin/add-remote-server

Conversation

@apriendeau

@apriendeau apriendeau commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Remote TCP server mode (--host + --port) — headless phx that accepts TCP connections and delegates to the existing RPC server
  • Remote client mode (--remote host:port) — TUI connects to a remote phx server over TCP instead of running locally
  • Shared HTTP/SSE infrastructure — extracts duplicated HTTP request/response/SSE parsing from all 4 providers into src/http/
  • Retry with backoff — all provider calls retry up to 10 times on transient errors (429, 5xx, timeouts) with exponential backoff
  • Session resume over RPC — new session.resume method + remote /sessions command
  • Provider::send() takes &SendOptions — enables retry without cloning

Test plan

  • just check passes (lint, build, test, lockfile)
  • CLI arg validation: --host requires --port and vice versa
  • --remote conflicts with --host/--port
  • TCP roundtrip test: client connects, calls config.get, gets response
  • Session resume test: create session with messages, resume via RPC, verify messages returned
  • SSE parser tests: single/double newline, partial chunks, multi-line data concatenation, UTF-8 split
  • Retry logic: backoff schedule, retryable vs non-retryable errors, format helpers
  • Config: remote config defaults, merge, roundtrip serialization

🤖 Generated with Claude Code

apriendeau and others added 11 commits May 24, 2026 18:09
Every provider send() now logs: debug before request, error on HTTP
failure (with URL, status, response body), error on stream read errors,
and debug on stream start. Adds logging standard to CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Priority: PHX_LOG env var > project .phx/phx.json > global ~/.phx/phx.json > "info" default.
Add "log_level" field to Config schema with merge support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract duplicated HTTP calling code from all four providers (Anthropic,
OpenAI, Google, Ollama) into a shared src/http/ module. Each provider now
uses http::send_streaming() instead of manual reqwest calls, eliminating
~100 lines of duplicated client creation, status checking, and error logging.

Add retry with exponential backoff (1,2,5,10,15,20,25,30,30,30s) for
transient failures (429, 500, 502, 503, 504, connection errors). Max 10
attempts. Non-retryable errors (400, 401, 403) fail immediately. Retry
status is shown inline in the TUI chat as system messages. The retry wait
is interruptible via Esc/Ctrl-C.

Changes:
- New src/http/mod.rs with send_streaming, is_retryable, backoff_delay
- Provider::send() now takes &SendOptions (was by-value)
- Retry loop in session/conversation.rs and session/agent_loop.rs
- ConvEvent::Retrying and ConvEvent::RetryRecovered for TUI feedback
- Msg::ConvRetrying and Msg::ConvRetryRecovered with update handlers
- Version bump to 0.6.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared SSE stream parser (SseParser) into src/http/sse.rs used
by all four providers. Centralize Message-to-ProviderMessage conversion
into session::message::to_provider_messages, removing duplicate mapping
from conversation.rs and agent_loop.rs. Add ToolSchema::to_openai_json
and to_simple_json to eliminate repeated tool JSON construction. Extract
render_picker_list into picker.rs, consolidating modal_picker and
command_completion rendering. Fix MockProvider to use non-retryable error
so tests don't hang on retry backoff.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… per spec

Previously, next_double() tried to parse each data: line individually as
JSON. Per the SSE spec, multiple data: lines in a block should be joined
with newlines before interpretation. This caused split JSON payloads to
fall through as Raw instead of Data.

Adds regression test: double_newline_concatenates_multiline_data

Closes PHX-507
Add --host/--port flags to run phx as a headless TCP server, and
--remote flag to connect the TUI to a remote server. Designed for
running across tailscale-connected machines.

Server mode reuses rpc::server::run over TCP with per-connection
sessions. Client mode produces ConvEvent from the TCP stream so
the TUI rendering layer is unchanged.

- Add RemoteConfig to config schema (default host 127.0.0.1, port 4200)
- Wire SESSION_RESUME handler and session_id param in rpc::server
- New src/remote/ module with TCP server and RemoteClient with
  id-demuxing read task
- Branch Cmd::StartConversation/ResumeSession on app.remote.is_some()
- Add spawn_remote_conversation() and resume_remote_session()
- Status bar shows "remote: <endpoint>" when connected remotely

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ConvEvent::RemoteSessionId so the server's session_id event
  flows into app.remote_session_id (was only logged before)
- Intercept /sessions and /resume in remote mode to query the server
  via session.list RPC instead of reading the local (empty) store
- These are required for session resume across reconnects to work

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@apriendeau apriendeau self-assigned this May 25, 2026
apriendeau and others added 2 commits May 25, 2026 12:48
# Conflicts:
#	Cargo.toml
#	src/config/schema.rs
- Remove eprintln! in remote server (use tracing only)
- Enforce --host/--port co-requirement via clap `requires`
- Extract restore_session helper to deduplicate rpc/server.rs
- Add shared retry helpers (should_retry, format_retry_msg, format_recovered_msg)
- Switch RemoteClient to bounded channel with backpressure
- Notify pending requests on server disconnect instead of silent drop
- Add HomeOverrideGuard for safer test home dir management
- Sync Cargo.lock version to 0.7.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@apriendeau
apriendeau merged commit eff3a29 into main May 25, 2026
3 checks passed
@apriendeau
apriendeau deleted the austin/add-remote-server branch May 25, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant