Skip to content

[Java] Embed Rust CLI runtime 4.6: Transport integration with CopilotClient #2148

Description

@edburns

Overview

Create the RuntimeConnection sealed class hierarchy, add setConnection() to CopilotClientOptions, and implement the InProcess code path in CopilotClient that uses FfiRuntimeHost instead of CliServerManager. Do NOT create a Transport enum or setTransport() method — that approach was explicitly rejected in favor of the RuntimeConnection type hierarchy.

This is task 4.6 of 9 in the implementation plan. Tasks are assigned, completed, and merged serially in this listed order. Tasks 4.1–4.5 are complete on the base branch before this task begins.

Branch: edburns/1917-java-embed-rust-cli-runtime-dd-3039924-agentic-run-02 on upstream

Plan and supporting resources

On the edburns/1917-java-embed-rust-cli-runtime-dd-3039924-agentic-run-02 branch, the directory 1917-java-embed-rust-cli-runtime-remove-before-merge contains the plan (1917-embed-cli-runtime-ignorance-reduction-plan.md) and supporting resources (spikes, prototypes, diagrams).

Read the entire plan before working.

Relevant plan sections to carefully re-read

  • Section 3.5.1 — How is InProcess transport selected — Resolution: RECOMMENDATION SUPERSEDED. Transport enum rejected. Use RuntimeConnection sealed class hierarchy with factory methods mirroring .NET 1:1. Four concrete subtypes: StdioRuntimeConnection, TcpRuntimeConnection, UriRuntimeConnection, InProcessRuntimeConnection. Backward compatibility via nullable connection field (null → legacy fields). IllegalArgumentException when both connection and legacy fields are set.
  • Section 3.5.2 — What replaces CliServerManager for InProcess — Resolution: FfiRuntimeHost (from task 4.5). CopilotClient.startCoreBody() dispatches on connection instanceof InProcessRuntimeConnection.
  • Section 3.14 — @CopilotExperimental annotation — Resolution: Annotate InProcess transport with @CopilotExperimental.
  • Section 4.6 — Transport integration with CopilotClient (the primary task description).
  • TDD discipline for all implementation steps — write tests first, implement until green, refactor, gate before proceeding.

Critical implementation requirements (from plan ✅✅ markers)

  1. COPILOT_SDK_DEFAULT_CONNECTION env var resolution: CopilotClient must implement resolveDefaultConnection() when no connection is set. See .NET dotnet/src/Client.cs — search for ResolveDefaultConnection; Rust rust/src/lib.rs — search for fn resolve_default_transport and constant DEFAULT_CONNECTION_ENV_VAR. When the env var is "inprocess", automatically select InProcessRuntimeConnection.

  2. ValidateEnvironmentOptions — reject incompatible options for InProcess: environment, telemetry, workingDirectory, extraArgs must be rejected with IllegalArgumentException when InProcess is selected. Without this, users set options that silently do nothing in-process. See .NET dotnet/src/Client.cs — search for ValidateEnvironmentOptions; Rust rust/src/lib.rs — search for fn validate_inprocess_options.

  3. Backward-compatibility bridge: When connection is null, existing useStdio/cliUrl/cliPath logic runs unchanged. Test this bridge explicitly.

Resolved decisions that constrain this task

  • Package for RuntimeConnection hierarchy: com.github.copilot.rpc (alongside CopilotClientOptions).

  • Sealed class design:

    public abstract sealed class RuntimeConnection
        permits StdioRuntimeConnection, TcpRuntimeConnection,
                UriRuntimeConnection, InProcessRuntimeConnection {
        RuntimeConnection() {} // package-private
        public static StdioRuntimeConnection forStdio() { ... }
        public static StdioRuntimeConnection forStdio(String path) { ... }
        public static TcpRuntimeConnection forTcp() { ... }
        public static UriRuntimeConnection forUri(String url) { ... }
        public static InProcessRuntimeConnection forInProcess() { ... }
    }
  • Four concrete subtypes:

    Java subclass Transport Config fields
    StdioRuntimeConnection stdin/stdout pipe to subprocess path, args
    TcpRuntimeConnection TCP socket to subprocess path, port, connectionToken, args
    UriRuntimeConnection TCP to external server url (required), connectionToken
    InProcessRuntimeConnection FFI via JNA C ABI (none — uses bundled native library)
  • CopilotClientOptions.connection: Nullable, default null. When non-null, takes precedence over legacy fields. If both connection and legacy fields are set → IllegalArgumentException.

  • CopilotClient.startCoreBody() dispatch:

    if (connection instanceof InProcessRuntimeConnection) {
        ffiHost = new FfiRuntimeHost(...);
        ffiHost.start();
        rpc = JsonRpcClient.fromStreams(ffiHost.getReceiveStream(), ffiHost.getSendStream());
    } else if (optionsHost != null) {
        rpc = serverManager.connectToServer(null, optionsHost, optionsPort);
    } else {
        // existing subprocess path — unchanged
    }
  • Connection record update: Add FfiRuntimeHost field so stop()/forceStop() can call ffiHost.close().

  • @CopilotExperimental: Annotate InProcessRuntimeConnection and RuntimeConnection.forInProcess().

Deliverables

Files to create

  1. java/sdk/src/main/java/com/github/copilot/rpc/RuntimeConnection.java — Sealed class with factory methods.
  2. java/sdk/src/main/java/com/github/copilot/rpc/StdioRuntimeConnection.java
  3. java/sdk/src/main/java/com/github/copilot/rpc/TcpRuntimeConnection.java
  4. java/sdk/src/main/java/com/github/copilot/rpc/UriRuntimeConnection.java
  5. java/sdk/src/main/java/com/github/copilot/rpc/InProcessRuntimeConnection.java — annotated @CopilotExperimental.

Files to modify

  1. java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java — Add connection field (type RuntimeConnection, nullable, default null), setConnection()/getConnection().
  2. java/sdk/src/main/java/com/github/copilot/CopilotClient.java — InProcess connection path via RuntimeConnection dispatch. Add resolveDefaultConnection() and validateEnvironmentOptions(). Update Connection record with FfiRuntimeHost field.

Test files to create

  1. java/sdk/src/test/java/com/github/copilot/CopilotClientTransportTest.java — Unit tests:
    • RuntimeConnection.forInProcess() routes through FfiRuntimeHost.
    • COPILOT_SDK_DEFAULT_CONNECTION=inprocess env var selects InProcess.
    • Backward-compatibility bridge: legacy useStdio/cliUrl/cliPath fields → correct RuntimeConnection inference.
    • IllegalArgumentException when both connection and legacy fields are set.
    • validateEnvironmentOptions() rejects environment, telemetry, workingDirectory, extraArgs for InProcess.
    • CLI transport remains unchanged when connection is null.

Gating tests and criteria

  1. Unit tests pass: All tests in CopilotClientTransportTest pass.
  2. InProcess routing: new CopilotClientOptions().setConnection(RuntimeConnection.forInProcess()) routes through FFI host.
  3. Env var resolution: COPILOT_SDK_DEFAULT_CONNECTION=inprocess works.
  4. Backward compat: Existing CLI transport paths work identically.
  5. Option validation: Incompatible options rejected for InProcess.
  6. All prior tests pass: mvn verify from java/ passes.
  7. Spotless compliance: mvn spotless:check passes.

Out of scope

  • copilot-native module or native binary downloading (task 4.7).
  • E2E testing with real runtime.node (task 4.8).
  • CI workflow changes (task 4.9).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementjavaPull requests that update java code

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions