pub struct Client { /* private fields */ }Expand description
Connection to a GitHub Copilot CLI server (stdio, TCP, or external).
Cheaply cloneable — cloning shares the underlying connection. The child process (if any) is killed when the last clone drops.
Implementations§
Source§impl Client
impl Client
Sourcepub async fn create_session(
&self,
config: SessionConfig,
) -> Result<Session, Error>
pub async fn create_session( &self, config: SessionConfig, ) -> Result<Session, Error>
Create a new session on the CLI.
Sends session.create, registers the session on the router,
and spawns an internal event loop that dispatches to the handler.
All callbacks (event handler, hooks, transform) are configured
via SessionConfig using with_handler,
with_hooks, and
with_transform.
If hooks_handler is set, the
wire-level hooks flag is automatically enabled.
If transform is set, the SDK injects
action: "transform" sections into the SystemMessageConfig wire
format and handles systemMessage.transform RPC callbacks during
the session.
If handler is None, the session uses
DenyAllHandler — permission
requests are denied; other events are no-ops.
Sourcepub async fn resume_session(
&self,
config: ResumeSessionConfig,
) -> Result<Session, Error>
pub async fn resume_session( &self, config: ResumeSessionConfig, ) -> Result<Session, Error>
Resume an existing session on the CLI.
Sends session.resume and session.skills.reload, registers the
session on the router, and spawns the event loop.
All callbacks (event handler, hooks, transform) are configured
via ResumeSessionConfig using its with_* builder methods.
See Self::create_session for the defaults applied when callback
fields are unset.
Source§impl Client
impl Client
Sourcepub async fn start(options: ClientOptions) -> Result<Self, Error>
pub async fn start(options: ClientOptions) -> Result<Self, Error>
Start a CLI server process with the given options.
For Transport::Stdio, spawns the CLI with --stdio and communicates
over stdin/stdout pipes. For Transport::Tcp, spawns with --port
and connects via TCP once the server reports it is listening. For
Transport::External, connects to an already-running server.
After establishing the connection, calls verify_protocol_version
to ensure the CLI server speaks a compatible protocol version.
When ClientOptions::session_fs is set, also calls
sessionFs.setProvider to register the SDK as the filesystem
backend.
Sourcepub fn from_streams(
reader: impl AsyncRead + Unpin + Send + 'static,
writer: impl AsyncWrite + Unpin + Send + 'static,
cwd: PathBuf,
) -> Result<Self, Error>
pub fn from_streams( reader: impl AsyncRead + Unpin + Send + 'static, writer: impl AsyncWrite + Unpin + Send + 'static, cwd: PathBuf, ) -> Result<Self, Error>
Create a Client from raw async streams (no child process).
Useful for testing or connecting to a server over a custom transport.
Sourcepub fn from_streams_with_trace_provider(
reader: impl AsyncRead + Unpin + Send + 'static,
writer: impl AsyncWrite + Unpin + Send + 'static,
cwd: PathBuf,
provider: Arc<dyn TraceContextProvider>,
) -> Result<Self, Error>
pub fn from_streams_with_trace_provider( reader: impl AsyncRead + Unpin + Send + 'static, writer: impl AsyncWrite + Unpin + Send + 'static, cwd: PathBuf, provider: Arc<dyn TraceContextProvider>, ) -> Result<Self, Error>
Construct a Client from raw streams with a
TraceContextProvider preset, for integration testing.
Mirrors from_streams but exposes the
on_get_trace_context plumbing so tests can verify outbound
traceparent / tracestate injection on session.create,
session.resume, and session.send.
Sourcepub fn from_streams_with_connection_token(
reader: impl AsyncRead + Unpin + Send + 'static,
writer: impl AsyncWrite + Unpin + Send + 'static,
cwd: PathBuf,
token: Option<String>,
) -> Result<Self, Error>
pub fn from_streams_with_connection_token( reader: impl AsyncRead + Unpin + Send + 'static, writer: impl AsyncWrite + Unpin + Send + 'static, cwd: PathBuf, token: Option<String>, ) -> Result<Self, Error>
Construct a Client from raw streams with a preset
effective_connection_token, for integration testing the
connect handshake’s token-forwarding path.
Sourcepub fn generate_connection_token_for_test() -> String
pub fn generate_connection_token_for_test() -> String
Public test-only wrapper around the random connection-token
generator used by Client::start when the SDK spawns a TCP
server without an explicit token. Lets integration tests
validate the token shape (32-char lowercase hex, 128 bits of
entropy) without re-implementing the helper.
Sourcepub fn rpc(&self) -> ClientRpc<'_>
pub fn rpc(&self) -> ClientRpc<'_>
Typed RPC namespace for server-level methods.
Every protocol method lives here under its schema-aligned path —
e.g. client.rpc().models().list(). Wire method names and request/
response types are generated from the protocol schema, so the typed
namespace can’t drift from the wire contract.
The hand-authored helpers on Client delegate to this namespace
and remain the recommended entry point for everyday use; reach for
rpc() when you want a method without a hand-written wrapper.
Sourcepub async fn call(
&self,
method: &str,
params: Option<Value>,
) -> Result<Value, Error>
pub async fn call( &self, method: &str, params: Option<Value>, ) -> Result<Value, Error>
Send a JSON-RPC request, check for errors, and return the result value.
This is the primary method for session-level RPC calls. It wraps the internal send/receive cycle with error checking so callers don’t need to inspect the response manually.
§Cancel safety
Cancel-safe. The frame is committed to the wire via the
writer-actor task before the future yields; cancelling the await
(via tokio::time::timeout, select!, or dropped JoinHandle)
drops the response oneshot but does not desync the transport.
The pending-requests entry is cleaned up by an RAII guard.
However, the call’s side effect on the CLI may still occur —
the CLI receives the request and processes it; the caller just
won’t see the response. For idempotent methods this is fine; for
non-idempotent methods (e.g. session.create) the caller should
avoid wrapping the call in a timeout shorter than the expected
CLI processing window.
Sourcepub fn protocol_version(&self) -> Option<u32>
pub fn protocol_version(&self) -> Option<u32>
Returns the protocol version negotiated with the CLI server, if any.
Set during start. Returns None if the server didn’t
report a version, or if the client was created via
from_streams without calling
verify_protocol_version.
Sourcepub async fn verify_protocol_version(&self) -> Result<(), Error>
pub async fn verify_protocol_version(&self) -> Result<(), Error>
Verify the CLI server’s protocol version is within the supported range.
Called automatically by start. Call manually after
from_streams if you need version verification
on a custom transport.
§Handshake sequence
- Sends the
connectJSON-RPC method, forwardingClientOptions::tcp_connection_token(or the auto-generated token for SDK-spawned TCP servers) as thetokenparam. This is the canonical handshake used by all SDK languages and is what the CLI uses to enforce loopback authentication when started withCOPILOT_CONNECTION_TOKEN. - If the server returns
-32601(MethodNotFound), falls back to the legacypingRPC. This preserves compatibility with older CLI versions that predateconnect.
§Result
Returns an error if the negotiated protocolVersion is outside
MIN_PROTOCOL_VERSION..=SDK_PROTOCOL_VERSION. If the server
doesn’t report a version, logs a warning and succeeds.
Sourcepub async fn ping(&self, message: Option<&str>) -> Result<PingResponse, Error>
pub async fn ping(&self, message: Option<&str>) -> Result<PingResponse, Error>
Send a ping RPC and return the typed PingResponse.
Pass Some(message) to have the server echo it back; pass None for
a bare health check. The response includes a protocolVersion when
the CLI reports one.
Sourcepub async fn list_sessions(
&self,
filter: Option<SessionListFilter>,
) -> Result<Vec<SessionMetadata>, Error>
pub async fn list_sessions( &self, filter: Option<SessionListFilter>, ) -> Result<Vec<SessionMetadata>, Error>
List persisted sessions, optionally filtered by working directory, repository, or git context.
Sourcepub async fn get_session_metadata(
&self,
session_id: &SessionId,
) -> Result<Option<SessionMetadata>, Error>
pub async fn get_session_metadata( &self, session_id: &SessionId, ) -> Result<Option<SessionMetadata>, Error>
Fetch metadata for a specific persisted session by ID.
Returns Ok(None) if no session with the given ID exists. More
efficient than calling list_sessions and
filtering when you only need data for a single session.
§Example
use github_copilot_sdk::types::SessionId;
if let Some(metadata) = client.get_session_metadata(&SessionId::new("session-123")).await? {
println!("Session started at: {}", metadata.start_time);
}Sourcepub async fn delete_session(&self, session_id: &SessionId) -> Result<(), Error>
pub async fn delete_session(&self, session_id: &SessionId) -> Result<(), Error>
Delete a persisted session by ID.
Sourcepub async fn get_last_session_id(&self) -> Result<Option<SessionId>, Error>
pub async fn get_last_session_id(&self) -> Result<Option<SessionId>, Error>
Return the ID of the most recently updated session, if any.
Useful for resuming the last conversation when the session ID was
not stored. Returns Ok(None) if no sessions exist.
§Example
if let Some(last_id) = client.get_last_session_id().await? {
println!("Last session: {last_id}");
}Sourcepub async fn get_foreground_session_id(
&self,
) -> Result<Option<SessionId>, Error>
pub async fn get_foreground_session_id( &self, ) -> Result<Option<SessionId>, Error>
Return the ID of the session currently displayed in the TUI, if any.
Only meaningful when connected to a server running in TUI+server mode
(--ui-server). Returns Ok(None) if no foreground session is set.
Sourcepub async fn set_foreground_session_id(
&self,
session_id: &SessionId,
) -> Result<(), Error>
pub async fn set_foreground_session_id( &self, session_id: &SessionId, ) -> Result<(), Error>
Request that the TUI switch to displaying the specified session.
Only meaningful when connected to a server running in TUI+server mode
(--ui-server).
Sourcepub async fn get_status(&self) -> Result<GetStatusResponse, Error>
pub async fn get_status(&self) -> Result<GetStatusResponse, Error>
Get the CLI server status.
Sourcepub async fn get_auth_status(&self) -> Result<GetAuthStatusResponse, Error>
pub async fn get_auth_status(&self) -> Result<GetAuthStatusResponse, Error>
Get authentication status.
Sourcepub async fn list_models(&self) -> Result<Vec<Model>, Error>
pub async fn list_models(&self) -> Result<Vec<Model>, Error>
List available models.
When ClientOptions::on_list_models is set, returns the handler’s
result without making a models.list RPC. Otherwise queries the CLI.
Sourcepub fn pid(&self) -> Option<u32>
pub fn pid(&self) -> Option<u32>
Return the OS process ID of the CLI child process, if one was spawned.
Sourcepub async fn stop(&self) -> Result<(), StopErrors>
pub async fn stop(&self) -> Result<(), StopErrors>
Cooperatively shut down the client and the CLI child process.
Walks every still-registered session and sends session.destroy
for each one, then kills the CLI child. Errors from per-session
destroys and the final child-kill are collected into
StopErrors rather than short-circuiting on the first failure
— so callers see the full picture of teardown.
If you have already called Session::disconnect on every
session this client created, the per-session destroy step is a
no-op (the router map is empty); only the child-kill remains.
§Cancel safety
Cancel-unsafe but recoverable. The body sequentially destroys
every registered session (each via Client::call,
individually cancel-safe) before killing the child. Cancelling
stop() mid-loop leaves some sessions still in the router map
and the child still running. Recovery: call force_stop
(sync, kills the child unconditionally and clears router state)
or call stop() again with a fresh future. The documented
tokio::time::timeout(..., client.stop()) pattern in the example
below uses force_stop as the fallback for exactly this case.
Sourcepub fn force_stop(&self)
pub fn force_stop(&self)
Forcibly stop the CLI process without waiting for it to exit.
Synchronous fallback when stop is unsuitable — for
example when the awaiting tokio runtime is shutting down or the
process is wedged on I/O. Sends a kill signal without awaiting
reaper completion and immediately drops all per-session router
state so dependent tasks observe a closed channel rather than a
hang.
§Cancel safety
Synchronous and infallible by construction. Not async; cannot
be cancelled. Designed as the recovery path when stop
is wrapped in a timeout that elapses.
§Example
// Try graceful shutdown first; fall back to force_stop if hung.
match tokio::time::timeout(
std::time::Duration::from_secs(5),
client.stop(),
).await {
Ok(_) => {}
Err(_) => client.force_stop(),
}Sourcepub fn subscribe_lifecycle(&self) -> LifecycleSubscription
pub fn subscribe_lifecycle(&self) -> LifecycleSubscription
Subscribe to lifecycle events.
Returns a LifecycleSubscription that yields every
SessionLifecycleEvent sent by the CLI. Drop the value to
unsubscribe; there is no separate cancel handle.
The returned handle implements both an inherent
recv method and Stream,
so callers can use a while let loop or any combinator from
tokio_stream::StreamExt / futures::StreamExt.
Each subscriber maintains its own queue. If a consumer cannot keep
up, the oldest events are dropped and recv returns
RecvError::Lagged with the count of skipped events; consumers
should match on it and continue. Slow consumers do not block the
producer.
To filter by event type, match on event.event_type in the
consumer task. There is no built-in typed filter — match is more
flexible and keeps the API surface small.
§Example
let mut events = client.subscribe_lifecycle();
tokio::spawn(async move {
while let Ok(event) = events.recv().await {
println!("session {} -> {:?}", event.session_id, event.event_type);
}
});Sourcepub fn state(&self) -> ConnectionState
pub fn state(&self) -> ConnectionState
Return the current ConnectionState.
The state advances to Connected once
Client::start / Client::from_streams returns successfully and
drops to Disconnected after
stop or force_stop.