feat: add experimental JSON-RPC IPC server for external tool access#113
feat: add experimental JSON-RPC IPC server for external tool access#113
Conversation
Add a lightweight JSON-RPC 2.0 server that allows external tools (AI agents, vscode-R, etc.) to interact with a running arf R session via Unix sockets. MVP methods: - evaluate: run R code with captured output (invisible to REPL) - user_input: inject code into REPL as if typed by user New features: - --with-ipc flag to start IPC server on launch - arf ipc list/eval/send/status subcommands (client mode) - :ipc start/stop/status meta commands - Session discovery via ~/.cache/arf/sessions/<pid>.json - tokio (current_thread) for async socket handling Co-Authored-By: Claude Opus 4.6 <[email protected]>
Fixes for roborev review 365: - capture.rs: use length-prefixed binary format instead of R-side JSON construction. Rust handles all JSON serialization, eliminating escaping issues with control characters. Temp files use PID+counter for unique names (no more race conditions). - server.rs: add CancellationToken + JoinHandle for graceful shutdown. :ipc stop now properly signals the server thread to exit, allowing :ipc start to work again. - mod.rs: set r_is_at_prompt=false during evaluate to prevent concurrent evaluate requests from both thinking R is idle. - server.rs: use PID-unique temp file for R version extraction. Additional fixes: - Add "ipc" to meta command autocomplete list (completion/meta.rs) - Support :ipc stop then :ipc start by using Option<Receiver> Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Use nchar(type = "bytes") consistently in R capture code guards (previously mixed nchar() and nchar(type = "bytes")) - Remove misleading "(with timeout)" from stop_server join comment - Add log message about in-flight connections on shutdown Co-Authored-By: Claude Opus 4.6 <[email protected]>
Use reedline's new ExternalBreak signal to interrupt read_line() when IPC user_input is received. The injected code is echoed on the prompt line, saved to history, and triggers spinner/duration tracking just like normal user input. - Add break_signal Arc<AtomicBool> to IPC module, shared with reedline - Fire break signal when user_input stores pending input - Handle Signal::ExternalBreak in both REPL loops (R mainloop + standalone) - Switch reedline dependency to git rev with break signal support - Add PTY integration test for IPC user_input flow Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Fix malformed HTTP body in IPC test (leading spaces from format! indentation) - Add PID filtering to find_socket_path to avoid wrong session in parallel runs - Add Windows CR strip to ExternalBreak handler for consistency with Success path Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace the TCP localhost fallback on Windows with proper named pipes
(\\.\pipe\arf-ipc-{pid}), which are more secure and semantically
closer to Unix sockets.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Collapse nested if statements in pty_ipc_tests (collapsible_if) - Suppress dead_code warning for socket_path on Windows - Move sessions_dir import into cfg(unix) block Co-Authored-By: Claude Opus 4.6 <[email protected]>
Use tokio NamedPipeClient with tokio::time::timeout (300s) instead of synchronous File I/O, matching the Unix socket timeout behavior. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace sink/textConnection stdout/stderr capture with WriteConsoleEx callback approach. The IPC evaluate method now captures stdout/stderr via arf_libr::start_ipc_capture() callback, while value/error are still written to a temp file (simplified from 4 fields to 2). Add --visible flag to `arf ipc eval` that shows output in the REPL terminal while still capturing and returning it in the JSON response. Co-Authored-By: Claude Opus 4.6 <[email protected]>
capture.output(print()) bypasses WriteConsoleEx, so with visible=true the value never appeared in the REPL terminal. Now R code explicitly cat()s the value and message()s the error when visible mode is active. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add PTY-based tests for the evaluate IPC method: - test_ipc_evaluate_capture: stdout via cat(), value, error, mixed - test_ipc_evaluate_visible: visible=true shows output in REPL - test_ipc_evaluate_silent: visible=false (default) hides from REPL Co-Authored-By: Claude Opus 4.6 <[email protected]>
Rework visible=true to act as a blocking version of send/user_input: - Code is injected into the REPL prompt (appears like user-typed input) - R evaluates normally with output shown in the terminal - WriteConsoleEx callback captures output for the JSON response - IPC client blocks until evaluation completes (R returns to prompt) The previous approach evaluated silently in the idle callback and printed results via cat(), but the code never appeared at the prompt. Introduces PENDING_VISIBLE_EVAL state + check_visible_eval_completion() which detects when R returns to the prompt and sends the deferred reply. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Review 378 (2d13303): - Fix Windows compile: use cfg-gated eprint for strip_cr/format_error_output - Release RwLock before terminal I/O in ipc_capture_callback - Restore special chars test for 2-field capture file format Review 380 (10d3add): - Replace flaky sleep in test_ipc_evaluate_silent with sentinel pattern Review 381 (1dd200b): - Fix race: hold r_is_at_prompt=false during finish_ipc_capture - Use unwrap_or_else for poisoned mutex in handle_visible_evaluate - Add 300s staleness timeout for pending visible eval - Use Acquire/Release ordering for at_prompt flag Co-Authored-By: Claude Opus 4.6 <[email protected]>
Upgrade Relaxed loads/stores to Acquire/Release in handle_request to pair correctly with Release stores elsewhere. Document the timeout path's best-effort race with active R evaluation. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add ipc_tests.rs that verifies IPC JSON-RPC functionality without depending on terminal output verification. Uses portable-pty for process spawning with platform-specific transport (Unix sockets / Windows named pipes), enabling IPC testing on Windows where PTY-based terminal output capture is not available. Tests: evaluate (value, stdout, error, mixed, visible, sequential), user_input acceptance. Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Set shutdown flag before sleep in Drop so reader thread can exit during grace period, then join the thread (best-effort) - Document simplistic HTTP response parser relies on Connection: close - Document PID=None cross-talk risk and --test-threads=1 recommendation - Add module-level note about per-test process spawn cost Co-Authored-By: Claude Opus 4.6 <[email protected]>
cargo fmt fixes and mark all IPC tests with cfg_attr(windows, ignore) because ConPTY does not support crossterm cursor::position() — reedline cannot initialize, so arf never reaches the prompt. True Windows IPC testing requires a headless mode (no reedline). Co-Authored-By: Claude Opus 4.6 <[email protected]>
Route all IPC operations through reedline's ExternalBreak mechanism to check the editor buffer before accepting. This prevents two bugs: 1. IPC send/eval during console typing no longer corrupts the display - If the user has typed something, IPC returns USER_IS_TYPING error 2. Silent eval during typing is also blocked (previously froze the terminal) Key changes: - Replace IPC_PENDING_INPUT with PENDING_IPC_OPERATION enum - Defer user_input reply until ExternalBreak confirms empty buffer - Route silent evaluate through ExternalBreak instead of idle callback - Show "agent> " prefix consistently for all IPC-injected code - Show "[evaluating...]" indicator during silent evaluate - Clear prompt line before IPC output to prevent display corruption - Add USER_IS_TYPING (-32002) error code, remove unused INPUT_ALREADY_PENDING Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Deduplicate VisibleEvaluate/UserInput arms in ExternalBreak handler by extracting shared post-accept logic (clear prompt, history, spinner) - Add explicit fall-through comment for SilentEvaluate in fast-path - Use INPUT_ALREADY_PENDING (-32002) instead of R_BUSY when another IPC operation is queued, so clients can distinguish the two cases - Document buffer.trim() semantics (whitespace-only treated as empty) Co-Authored-By: Claude Opus 4.6 <[email protected]>
When the REPL is in shell mode, history browser, or help browser, the idle callback is not running and IPC requests would hang in the mpsc queue until the 300s timeout. Add an IN_ALTERNATE_MODE flag that is checked in both dispatch_request (server thread) and handle_request (main thread) to immediately reject with R_NOT_AT_PROMPT instead of waiting. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Consolidate alternate-mode tests into single test functions to avoid flakiness from parallel execution sharing the global AtomicBool. Add comments explaining why RAII guard is not needed for the set_in_alternate_mode calls around browser invocations. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Update reedline to beb43685 which saves suspended_state in the ExternalBreak handler instead of calling move_cursor_to_end(). This prevents the extra blank line that appeared when re-entering read_line() after a rejected or accepted IPC operation. Add two PTY integration tests: - test_ipc_user_input_reject_no_extra_line: verify prompt row stays the same after USER_IS_TYPING rejection - test_ipc_user_input_no_extra_blank_line: verify no blank line between R output and the next prompt after IPC user_input Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
Adds an experimental JSON-RPC 2.0 IPC layer to allow external tools to interact with a live arf R session (evaluate code, inject user input, discover sessions), plus CLI and REPL integrations to manage and safely arbitrate IPC vs. interactive console input.
Changes:
- Introduces an IPC server/client implementation (Unix domain sockets, Windows named pipes) with session discovery metadata.
- Integrates IPC into the REPL loop via reedline
ExternalBreakfor mutual exclusion + alternate-mode rejection. - Adds unit + PTY integration tests and updates CLI help/completions/changelog for the new
--with-ipcflag andarf ipcsubcommand.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/arf-libr/src/sys.rs | Adds WriteConsoleEx-based capture buffering and APIs to start/finish IPC output capture. |
| crates/arf-libr/src/lib.rs | Re-exports new IPC capture/callback functions from sys. |
| crates/arf-console/src/ipc/mod.rs | Main-thread IPC coordination (pending ops, break signal, prompt state, visible-eval tracking). |
| crates/arf-console/src/ipc/server.rs | Background IPC server thread (tokio) + JSON-RPC dispatch/HTTP wrapping. |
| crates/arf-console/src/ipc/protocol.rs | JSON-RPC types and application error codes for IPC. |
| crates/arf-console/src/ipc/client.rs | Implements arf ipc CLI client commands (list/eval/send/status). |
| crates/arf-console/src/ipc/session.rs | Writes/reads ~/.cache/arf/sessions/<pid>.json for discovery and stale cleanup. |
| crates/arf-console/src/ipc/capture.rs | Implements evaluate capture (stdout/stderr via WriteConsoleEx + value/error via temp file). |
| crates/arf-console/src/repl/mod.rs | Hooks reedline break signal + idle polling and implements ExternalBreak IPC handling paths. |
| crates/arf-console/src/repl/state.rs | Marks shell mode as “alternate mode” for IPC rejection. |
| crates/arf-console/src/repl/meta_command.rs | Adds `:ipc start |
| crates/arf-console/src/main.rs | Wires --with-ipc startup/shutdown and adds arf ipc subcommand handling. |
| crates/arf-console/src/cli.rs | Adds --with-ipc flag and ipc subcommands (list/eval/send/status). |
| crates/arf-console/src/completion/meta.rs | Adds :ipc to meta-command completion list. |
| crates/arf-console/tests/ipc_tests.rs | Adds cross-platform IPC integration tests (JSON-RPC response validation). |
| crates/arf-console/tests/pty_ipc_tests.rs | Adds Unix PTY integration tests validating REPL-visible behavior + prompt positioning. |
| crates/arf-console/tests/common/mod.rs | Exposes child PID for tests to select the right session file. |
| crates/arf-console/src/snapshots/arf__cli__tests__help_long.snap | Updates CLI help snapshot for ipc + --with-ipc. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_bash.snap | Updates bash completions snapshot for new flag/subcommand. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_zsh.snap | Updates zsh completions snapshot for new flag/subcommand. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_fish.snap | Updates fish completions snapshot for new flag/subcommand. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_powershell.snap | Updates PowerShell completions snapshot for new flag/subcommand. |
| crates/arf-console/Cargo.toml | Adds tokio / tokio-util deps for the IPC server. |
| Cargo.toml | Pins reedline to a git rev and adds workspace tokio / tokio-util deps. |
| Cargo.lock | Lockfile updates for new deps and the git-sourced reedline. |
| CHANGELOG.md | Documents the experimental IPC server and :ipc meta command. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Always call stop_server() on exit, not only when --with-ipc was passed, so that servers started via :ipc start are also cleaned up - Reject start_server() when already running to prevent leaking threads - Fix is_process_alive: treat EPERM as alive on Unix, use CSV parsing on Windows to avoid false-positive PID substring matches - Set session directory to 0700 and session files to 0600 on Unix - Parse HTTP Content-Length header to avoid blocking on clients that do not half-close the write side - Add TODO comment for bind-before-session-write improvement Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Validate Content-Length against 1MB limit before reading body to prevent memory exhaustion via large Content-Length header - Create session file with mode 0600 atomically via OpenOptions to eliminate TOCTOU permission gap - Use case-insensitive Content-Length header matching per RFC 7230 - Hold SERVER_HANDLE mutex from check through write to eliminate TOCTOU race in start_server double-start guard Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Use checked_add for Content-Length size computation to prevent integer overflow from bypassing the 1MB request limit - Document the known TOCTOU limitation for directory permissions (Rust std lacks mkdir-with-mode) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
Adds an experimental JSON-RPC 2.0 IPC server to let external tools (agents/IDEs) interact with a running arf REPL session over Unix sockets / Windows named pipes, including output capture and REPL-safe input injection.
Changes:
- Introduces IPC server/client, session discovery, and JSON-RPC protocol types (
evaluate,user_input) plus REPL integration via reedlineExternalBreak. - Adds output capture plumbing via
WriteConsoleExinterception for both silent and visible evaluation. - Adds CLI surface area (
--with-ipc,arf ipc ...,:ipc ...) and extensive unit/integration tests + completion/help snapshot updates.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/arf-libr/src/sys.rs | Adds IPC stdout/stderr capture callback and start/finish helpers. |
| crates/arf-libr/src/lib.rs | Re-exports IPC capture helpers. |
| crates/arf-console/tests/pty_ipc_tests.rs | Unix PTY integration tests for IPC behavior and prompt correctness. |
| crates/arf-console/tests/ipc_tests.rs | Cross-platform IPC tests using PTY without terminal parsing. |
| crates/arf-console/tests/common/mod.rs | Exposes child PID for session discovery in tests. |
| crates/arf-console/src/snapshots/arf__cli__tests__help_long.snap | Updates help snapshot for ipc + --with-ipc. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_zsh.snap | Updates zsh completions for ipc + --with-ipc. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_powershell.snap | Updates PowerShell completions for ipc + --with-ipc. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_fish.snap | Updates fish completions for ipc + --with-ipc. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_bash.snap | Updates bash completions for ipc + --with-ipc. |
| crates/arf-console/src/repl/state.rs | Marks alternate modes for IPC rejection when entering shell mode. |
| crates/arf-console/src/repl/mod.rs | Hooks reedline break signal + idle polling; handles IPC ExternalBreak and prompt state tracking. |
| crates/arf-console/src/repl/meta_command.rs | Adds :ipc meta command; flags alternate modes during help/history browsers. |
| crates/arf-console/src/main.rs | Wires in --with-ipc, arf ipc subcommand, and shutdown cleanup call. |
| crates/arf-console/src/ipc/session.rs | Implements session metadata read/write + stale cleanup. |
| crates/arf-console/src/ipc/server.rs | Implements tokio-based IPC server and JSON-RPC dispatch. |
| crates/arf-console/src/ipc/protocol.rs | Defines JSON-RPC request/response types and IPC method/result enums. |
| crates/arf-console/src/ipc/mod.rs | Main-thread IPC coordination, mutual exclusion, visible eval completion tracking. |
| crates/arf-console/src/ipc/client.rs | CLI client for listing/sending/evaluating via IPC. |
| crates/arf-console/src/ipc/capture.rs | Implements silent evaluate capture (stdout/stderr + value/error via temp file). |
| crates/arf-console/src/completion/meta.rs | Adds :ipc to meta-command completion list. |
| crates/arf-console/src/cli.rs | Adds --with-ipc and arf ipc subcommands. |
| crates/arf-console/Cargo.toml | Adds IPC dependencies (tokio, tokio-util, tempfile). |
| Cargo.toml | Switches reedline to git rev; adds tokio/tokio-util workspace deps. |
| Cargo.lock | Locks new IPC dependencies and updated transitive graph. |
| CHANGELOG.md | Documents experimental IPC features and :ipc. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Accept "send" as alias for "user_input" in JSON-RPC dispatch to match PR documentation and CLI client - Ensure ipc::stop_server() runs even when repl.run() returns an error - Do not set r_is_at_prompt(true) on visible eval timeout since R may still be evaluating; let the normal prompt callback restore the flag Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
Adds an experimental IPC layer to let external tools interact with a running arf REPL via JSON-RPC 2.0 over Unix sockets / Windows named pipes, including request routing through reedline’s ExternalBreak for console/IPC mutual exclusion.
Changes:
- Introduces IPC server/client modules (protocol, transport, session discovery, output capture) and wires them into the REPL loop.
- Adds
--with-ipc,:ipcmeta command, andarf ipcCLI subcommand, plus shell completion/help snapshot updates. - Adds cross-platform IPC tests plus Unix PTY integration tests for prompt/buffer behavior.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
crates/arf-libr/src/sys.rs |
Adds WriteConsoleEx-based stdout/stderr capture plumbing for IPC evaluate. |
crates/arf-libr/src/lib.rs |
Re-exports IPC capture helpers from sys. |
crates/arf-console/src/ipc/mod.rs |
Main-thread IPC request routing + break-signal integration + visible-eval completion tracking. |
crates/arf-console/src/ipc/server.rs |
Tokio-based socket/pipe server that accepts JSON-RPC over HTTP-like framing or raw JSON. |
crates/arf-console/src/ipc/protocol.rs |
JSON-RPC types + internal request/response enums and error codes. |
crates/arf-console/src/ipc/session.rs |
Session file discovery (~/.cache/arf/sessions/<pid>.json) and stale cleanup. |
crates/arf-console/src/ipc/client.rs |
Implements arf ipc client commands (list/eval/send/status). |
crates/arf-console/src/ipc/capture.rs |
Silent evaluate implementation with stdout/stderr capture + value/error extraction. |
crates/arf-console/src/repl/mod.rs |
Wires IPC polling into reedline idle callback and handles Signal::ExternalBreak. |
crates/arf-console/src/repl/state.rs |
Marks shell mode as “alternate mode” for IPC rejection behavior. |
crates/arf-console/src/repl/meta_command.rs |
Adds `:ipc {start |
crates/arf-console/src/main.rs |
Adds --with-ipc startup behavior and arf ipc subcommand dispatch + shutdown cleanup. |
crates/arf-console/src/cli.rs |
Adds --with-ipc and ipc subcommands to clap CLI definition. |
crates/arf-console/src/completion/meta.rs |
Adds :ipc to meta-command completion. |
crates/arf-console/tests/ipc_tests.rs |
Cross-platform IPC tests (transport/protocol-level assertions). |
crates/arf-console/tests/pty_ipc_tests.rs |
Unix PTY integration tests for evaluate/user_input + prompt position invariants. |
crates/arf-console/tests/common/mod.rs |
Exposes child PID for session filtering in PTY tests. |
crates/arf-console/Cargo.toml |
Adds tokio/tokio-util/tempfile deps for IPC server and capture. |
Cargo.toml |
Switches reedline to a git rev (and adds tokio/tokio-util workspace deps). |
Cargo.lock |
Locks new async/runtime dependency graph updates. |
crates/arf-console/src/snapshots/arf__cli__tests__help_long.snap |
Updates help snapshot for new IPC command/flag. |
crates/arf-console/src/snapshots/arf__cli__tests__completions_zsh.snap |
Updates zsh completions snapshot for IPC command/flag. |
crates/arf-console/src/snapshots/arf__cli__tests__completions_powershell.snap |
Updates PowerShell completions snapshot for IPC command/flag. |
crates/arf-console/src/snapshots/arf__cli__tests__completions_fish.snap |
Updates fish completions snapshot for IPC command/flag. |
crates/arf-console/src/snapshots/arf__cli__tests__completions_bash.snap |
Updates bash completions snapshot for IPC command/flag. |
CHANGELOG.md |
Documents the new experimental IPC functionality and :ipc. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…socket dir - Remove unused IpcCaptureState.enabled field (capture is active iff callback is installed) - Clear pending IPC operations and finalize active capture in stop_server() to prevent stuck state after :ipc stop - Set sessions directory to 0700 before binding the Unix socket so other users cannot connect during the pre-session-write window - Fix test to set r_at_prompt before testing alternate mode rejection Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…ailure - Use blocking lock instead of try_lock in stop_server visible eval cleanup to ensure capture callback is always removed and reply sent - Log warning when sessions directory permission change fails Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…limitations Add SAFETY documentation for the WRITE_CONSOLE_CALLBACK static mut explaining the R single-threaded invariant. Document that evaluate_with_capture does not support interactive R functions. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
Adds an experimental JSON-RPC 2.0 IPC layer to allow external tools to interact with a running arf REPL session (evaluate code, inject input), including session discovery and a CLI client.
Changes:
- Introduces IPC server/client/protocol modules (Unix socket / Windows named pipe) with session metadata discovery in
~/.cache/arf/sessions. - Adds REPL integration via reedline
ExternalBreak+ idle polling to coordinate IPC requests with console state and capture output. - Adds
--with-ipc,:ipcmeta command,arf ipcsubcommand, and accompanying tests/snapshots/changelog updates.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/arf-libr/src/sys.rs | Adds WriteConsoleEx-based IPC stdout/stderr capture helpers. |
| crates/arf-libr/src/lib.rs | Re-exports IPC capture APIs from sys. |
| crates/arf-console/tests/pty_ipc_tests.rs | Adds Unix-only PTY integration tests for IPC behavior. |
| crates/arf-console/tests/ipc_tests.rs | Adds cross-platform IPC tests (PTY-backed, transport-aware). |
| crates/arf-console/tests/common/mod.rs | Exposes child process PID to help tests bind to the correct session. |
| crates/arf-console/src/snapshots/arf__cli__tests__help_long.snap | Updates CLI help snapshot to include IPC command/flag. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_zsh.snap | Updates zsh completion snapshot for IPC. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_powershell.snap | Updates PowerShell completion snapshot for IPC. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_fish.snap | Updates fish completion snapshot for IPC. |
| crates/arf-console/src/snapshots/arf__cli__tests__completions_bash.snap | Updates bash completion snapshot for IPC. |
| crates/arf-console/src/repl/state.rs | Marks shell mode as “alternate mode” for IPC rejection logic. |
| crates/arf-console/src/repl/mod.rs | Hooks reedline break signal + idle polling; processes pending IPC ops in REPL loop. |
| crates/arf-console/src/repl/meta_command.rs | Adds :ipc and sets alternate-mode flags around help/history browsers. |
| crates/arf-console/src/main.rs | Wires --with-ipc startup, shutdown cleanup, and arf ipc subcommand handling. |
| crates/arf-console/src/ipc/session.rs | Implements session file write/remove/list + stale cleanup. |
| crates/arf-console/src/ipc/server.rs | Implements socket/pipe server thread, request parsing, and dispatch. |
| crates/arf-console/src/ipc/protocol.rs | Defines JSON-RPC and internal IPC request/response types and error codes. |
| crates/arf-console/src/ipc/mod.rs | Main-thread IPC orchestration, pending ops, break signal, visible-eval tracking. |
| crates/arf-console/src/ipc/client.rs | Adds arf ipc CLI client (list/eval/send/status). |
| crates/arf-console/src/ipc/capture.rs | Implements silent evaluate capture (stdout/stderr + value/error via temp file). |
| crates/arf-console/src/completion/meta.rs | Adds :ipc to meta-command completion list. |
| crates/arf-console/src/cli.rs | Adds --with-ipc and ipc subcommand definitions. |
| crates/arf-console/Cargo.toml | Adds tokio/tokio-util/tempfile deps needed by IPC. |
| Cargo.toml | Switches reedline to a git rev and adds workspace tokio/tokio-util deps. |
| Cargo.lock | Updates lockfile for new deps and reedline git source. |
| CHANGELOG.md | Documents experimental IPC server and :ipc meta command. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Save/restore alternate mode flag in help and history browsers to avoid clobbering nested alternate modes (e.g., help inside shell mode) - Wait for bind confirmation from server thread via sync_channel before writing session file, preventing stale session entries on bind failure - Skip response for JSON-RPC 2.0 notifications (id null/absent) per spec - Leak PTY reader thread in test Drop instead of joining to prevent hangs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Document intentional spec deviation for treating id:null as notification - Restore panic safety rationale in alternate mode save/restore comments Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
id:null is a valid request id per JSON-RPC 2.0 spec and should receive a response. Only skip response when id is truly absent. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
Adds an experimental JSON-RPC 2.0 IPC layer to let external tools (agents/IDEs/CLI) interact with a running arf REPL, including session discovery and client commands. This integrates with reedline via ExternalBreak to enforce mutual exclusion with interactive typing and rejects requests during alternate-screen modes.
Changes:
- Introduces IPC server/client/protocol/session-discovery modules plus
--with-ipc,arf ipc …CLI, and:ipcmeta command. - Adds output capture plumbing in
arf-libr(WriteConsoleEx interception) to supportevaluate(silent + visible) with captured stdout/stderr/value/error. - Adds cross-platform (non-PTY) IPC tests and Unix PTY integration tests; updates CLI help/completions snapshots.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/arf-libr/src/sys.rs | Adds IPC output capture state + callback and start/finish helpers. |
| crates/arf-libr/src/lib.rs | Re-exports IPC capture helpers. |
| crates/arf-console/src/ipc/mod.rs | Main-thread IPC coordination (pending op, break signal, prompt/alternate-mode state). |
| crates/arf-console/src/ipc/server.rs | Tokio-based Unix socket / Windows named pipe server + JSON-RPC dispatch. |
| crates/arf-console/src/ipc/capture.rs | Silent evaluate capture (WriteConsoleEx + temp-file value/error protocol). |
| crates/arf-console/src/ipc/client.rs | Implements arf ipc subcommands (list/eval/send/status). |
| crates/arf-console/src/ipc/session.rs | Session metadata discovery via cache dir files + permissions. |
| crates/arf-console/src/repl/mod.rs | Hooks reedline break signal + idle polling + ExternalBreak handler for IPC. |
| crates/arf-console/src/repl/meta_command.rs | Adds :ipc start/stop/status; marks help/history browsers as alternate mode. |
| crates/arf-console/src/main.rs | Adds --with-ipc startup + shutdown cleanup; wires arf ipc subcommand. |
| crates/arf-console/src/cli.rs | Adds --with-ipc and ipc subcommand parsing. |
| crates/arf-console/tests/* | Adds IPC integration tests (PTY + cross-platform). |
| Cargo.toml / crates/arf-console/Cargo.toml / Cargo.lock | Adds tokio/tokio-util/tempfile deps; pins reedline to git rev. |
| CHANGELOG.md + snapshots | Documents feature; updates help/completions snapshots. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…on, and read overshoot - Create per-process directory with mode 0700 for fallback Unix socket path so the socket is not accessible to other users in /tmp - Treat truncated capture files as errors instead of silently returning empty strings, so clients get a clear "Malformed capture file" message - Truncate read buffer to Content-Length boundary to prevent overshoot bytes from corrupting JSON parsing Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…et directories Replace create_dir_all + set_permissions two-step with DirBuilderExt::mode(0o700) so the directory is created with correct permissions atomically. Applied to both the sessions_dir and fallback /tmp paths. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR introduces an experimental JSON-RPC 2.0 IPC layer to let external tools (AI agents, IDE extensions, CLI client) interact with a running arf R session over Unix domain sockets / Windows named pipes, with guardrails to avoid interfering with interactive console input.
Changes:
- Added IPC server/client implementation with session discovery, request dispatch, and REPL integration via reedline
ExternalBreak. - Added output capture plumbing for
evaluate(silent + visible) viaWriteConsoleExinterception. - Added CLI surface area (
--with-ipc,arf ipc ...,:ipc ...) plus unit + PTY integration tests and updated completion/help snapshots.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/arf-libr/src/sys.rs | Adds WriteConsoleEx capture state and start/finish APIs for IPC evaluation output buffering. |
| crates/arf-libr/src/lib.rs | Re-exports new IPC capture/callback helper functions. |
| crates/arf-console/src/ipc/mod.rs | Main-thread IPC coordination: pending op queue, break signal, visible-eval completion tracking, start/stop. |
| crates/arf-console/src/ipc/server.rs | Tokio-based background IPC server for Unix sockets / Windows named pipes with JSON-RPC dispatch + timeouts. |
| crates/arf-console/src/ipc/protocol.rs | JSON-RPC protocol structs + error codes + internal request/response types. |
| crates/arf-console/src/ipc/session.rs | Session file discovery (~/.cache/arf/sessions/<pid>.json) for locating active servers. |
| crates/arf-console/src/ipc/client.rs | Implements arf ipc subcommand client (list/eval/send/status). |
| crates/arf-console/src/ipc/capture.rs | Implements silent evaluate value/error capture via temp file + console output capture. |
| crates/arf-console/src/repl/mod.rs | Integrates IPC into reedline (break signal + idle polling) and handles ExternalBreak to accept/reject ops. |
| crates/arf-console/src/repl/state.rs | Marks shell mode as alternate mode for IPC rejection. |
| crates/arf-console/src/repl/meta_command.rs | Adds `:ipc start |
| crates/arf-console/src/main.rs | Wires --with-ipc startup, arf ipc subcommand, and ensures IPC cleanup on exit. |
| crates/arf-console/src/cli.rs | Adds --with-ipc flag and ipc subcommand definitions. |
| crates/arf-console/src/completion/meta.rs | Adds :ipc meta command to completion definitions. |
| crates/arf-console/tests/ipc_tests.rs | Cross-platform IPC tests validating JSON-RPC behavior (mostly ignored on Windows due to ConPTY limitations). |
| crates/arf-console/tests/pty_ipc_tests.rs | Unix PTY integration tests validating terminal-visible behavior and prompt positioning. |
| crates/arf-console/tests/common/mod.rs | Exposes spawned process PID to help tests target the correct session. |
| crates/arf-console/src/snapshots/* | Updates CLI help + shell completion snapshots to include IPC commands/flags. |
| crates/arf-console/Cargo.toml | Adds tokio/tokio-util/tempfile deps needed for IPC implementation. |
| Cargo.toml | Switches reedline to a pinned git rev; adds workspace tokio/tokio-util deps. |
| Cargo.lock | Updates lockfile for added deps and reedline source change. |
| CHANGELOG.md | Documents experimental IPC server and :ipc meta command. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… on non-command prompts - Only update IPC_RECEIVER after start_server succeeds so a failed restart does not break an already-running server's channel - Set r_at_prompt unconditionally based on is_r_command_prompt() so the flag is cleared during menu/selection prompts, preventing IPC requests from being accepted when R is not at the top-level prompt Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…ion dir TOCTOU - Replace expect() with early return of EvaluateResult error when temp file creation fails (e.g., disk full, permissions) - Use DirBuilder::mode(0o700) in write_session for atomic directory creation, consistent with get_socket_path Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Summary
Add an experimental JSON-RPC 2.0 IPC server for AI agents, IDE extensions, and other external tools to interact with a running arf R session.
Features
evaluatemethod: Silent or visible evaluation with captured stdout/value/error via WriteConsoleEx interceptionuser_inputmethod: Inject code into the REPL via reedline'sExternalBreaksignal (feat: add external break signal forread_lineinterruption and markSignalasnon_exhaustivenushell/reedline#1035), with full echo, history, spinner, and duration trackingsendmethod: Alias foruser_input:ipcmeta command: Start/stop/check server status at runtimearf ipcsubcommand: CLI client for eval/send/list/status from another terminal~/.cache/arf/sessions/<pid>.json, cleaned up on exitSafety
USER_IS_TYPING(-32003) when the user has typed something in the bufferR_NOT_AT_PROMPT(-32001)Tests