Fork me on GitHub

Copilot SDK for Java - Documentation

⚠️ Disclaimer: This is an unofficial, community-driven SDK and is not supported or endorsed by GitHub. Use at your own risk.

This guide covers common use cases for the Copilot SDK for Java. For complete API details, see the Javadoc.

Table of Contents


Basic Usage

Create a client, start a session, and send a message:

import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;

try (var client = new CopilotClient()) {
    client.start().get();

    var session = client.createSession(
        new SessionConfig().setModel("gpt-4.1")
    ).get();

    var response = session.sendAndWait("Explain Java records in one sentence").get();
    System.out.println(response.getData().getContent());

    session.close();
}

The client manages the connection to the Copilot CLI. Sessions are independent conversations that can run concurrently.


Handling Responses

Simple Request-Response

For straightforward interactions, use sendAndWait():

var response = session.sendAndWait("What is the capital of France?").get();
System.out.println(response.getData().getContent());

Event-Based Processing

For more control, subscribe to events and use send():

Exception isolation: If a handler throws an exception, the SDK logs the error. By default, dispatch stops after the first handler error (PROPAGATE_AND_LOG_ERRORS). To continue dispatching to remaining handlers, set EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS. You can customize error handling with session.setEventErrorHandler() — see the Advanced Usage guide.

Troubleshooting Event Handling

Symptoms of policy misconfiguration

  • You registered multiple session.on(...) handlers, but only the first one runs
  • A handler throws once and later handlers stop receiving events
  • You expected “best effort” fan-out, but dispatch halts on errors

Fix

Set the event error policy to suppress-and-continue:

session.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);

Optionally add a custom error handler for observability:

session.setEventErrorHandler((event, exception) -> {
    System.err.println("Handler failed for event " + event.getType() + ": " + exception.getMessage());
});

Use PROPAGATE_AND_LOG_ERRORS when you want fail-fast behavior.

var done = new CompletableFuture<Void>();

// Type-safe event handlers (recommended)
session.on(AssistantMessageEvent.class, msg -> {
    System.out.println("Response: " + msg.getData().getContent());
});

session.on(SessionErrorEvent.class, err -> {
    System.err.println("Error: " + err.getData().message());
});

session.on(SessionIdleEvent.class, idle -> {
    done.complete(null);
});

session.send("Tell me a joke").get();
done.get(); // Wait for completion

You can also use a single handler for all events:

session.on(event -> {
    switch (event) {
        case AssistantMessageEvent msg -> 
            System.out.println("Response: " + msg.getData().getContent());
        case SessionErrorEvent err -> 
            System.err.println("Error: " + err.getData().message());
        case SessionIdleEvent idle -> 
            done.complete(null);
        default -> { }
    }
});

Key Event Types

Event Description
AssistantMessageEvent Complete assistant response
AssistantMessageDeltaEvent Streaming chunk (when streaming enabled)
SessionIdleEvent Session finished processing
SessionErrorEvent An error occurred

For the complete list of all event types, see Event Types Reference below.


Event Types Reference

The SDK supports event types organized by category. All events extend AbstractSessionEvent.

Session Events

Event Type String Description
SessionStartEvent session.start Session has started
SessionResumeEvent session.resume Session was resumed
SessionIdleEvent session.idle Session finished processing, ready for input
SessionErrorEvent session.error An error occurred in the session
SessionInfoEvent session.info Informational message from the session
SessionShutdownEvent session.shutdown Session is shutting down (includes reason and exit code)
SessionModelChangeEvent session.model_change The model was changed mid-session
SessionHandoffEvent session.handoff Session handed off to another agent
SessionTruncationEvent session.truncation Context was truncated due to limits
SessionSnapshotRewindEvent session.snapshot_rewind Session rewound to a previous snapshot
SessionUsageInfoEvent session.usage_info Token usage information
SessionCompactionStartEvent session.compaction_start Context compaction started (infinite sessions)
SessionCompactionCompleteEvent session.compaction_complete Context compaction completed

Assistant Events

Event Type String Description
AssistantTurnStartEvent assistant.turn_start Assistant began processing
AssistantIntentEvent assistant.intent Assistant's detected intent
AssistantReasoningEvent assistant.reasoning Full reasoning content (reasoning models)
AssistantReasoningDeltaEvent assistant.reasoning_delta Streaming reasoning chunk
AssistantMessageEvent assistant.message Complete assistant message
AssistantMessageDeltaEvent assistant.message_delta Streaming message chunk
AssistantTurnEndEvent assistant.turn_end Assistant finished processing
AssistantUsageEvent assistant.usage Token usage for this turn

Tool Events

Event Type String Description
ToolUserRequestedEvent tool.user_requested User requested a tool invocation
ToolExecutionStartEvent tool.execution_start Tool execution started
ToolExecutionProgressEvent tool.execution_progress Tool execution progress update
ToolExecutionPartialResultEvent tool.execution_partial_result Partial result from tool
ToolExecutionCompleteEvent tool.execution_complete Tool execution completed

User Events

Event Type String Description
UserMessageEvent user.message User sent a message
PendingMessagesModifiedEvent pending_messages.modified Pending message queue changed

Subagent Events

Event Type String Description
SubagentStartedEvent subagent.started Subagent was spawned
SubagentSelectedEvent subagent.selected Subagent was selected for task
SubagentCompletedEvent subagent.completed Subagent completed its task
SubagentFailedEvent subagent.failed Subagent failed

Other Events

Event Type String Description
AbortEvent abort Operation was aborted
HookStartEvent hook.start Hook execution started
HookEndEvent hook.end Hook execution completed
SystemMessageEvent system.message System-level message
SkillInvokedEvent skill.invoked A skill was invoked

See the events package Javadoc for detailed event data structures.


Streaming Responses

Enable streaming to receive response chunks as they're generated:

var session = client.createSession(
    new SessionConfig()
        .setModel("gpt-4.1")
        .setStreaming(true)
).get();

var done = new CompletableFuture<Void>();

session.on(AssistantMessageDeltaEvent.class, delta -> {
    // Print each chunk as it arrives
    System.out.print(delta.getData().deltaContent());
});

session.on(SessionIdleEvent.class, idle -> {
    System.out.println(); // Newline at end
    done.complete(null);
});

session.send("Write a haiku about Java").get();
done.get();

Session Operations

Get Conversation History

Retrieve all messages and events from a session using getMessages():

var history = session.getMessages().get();

for (var event : history) {
    switch (event) {
        case UserMessageEvent user -> 
            System.out.println("User: " + user.getData().getContent());
        case AssistantMessageEvent assistant -> 
            System.out.println("Assistant: " + assistant.getData().getContent());
        case ToolExecutionCompleteEvent tool -> 
            System.out.println("Tool: " + tool.getData().getToolName());
        default -> { }
    }
}

This is useful for:

  • Displaying conversation history in a UI
  • Persisting conversations for later review
  • Debugging and logging session state

Abort Current Operation

Cancel a long-running operation using abort():

// Start a potentially long operation
var messageFuture = session.send("Analyze this large codebase...");

// User clicks cancel button
session.abort().get();

// The session will emit an AbortEvent
session.on(AbortEvent.class, evt -> {
    System.out.println("Operation was cancelled");
});

Use cases:

  • User-initiated cancellation in interactive applications
  • Timeout handling in automated pipelines
  • Graceful shutdown when application is closing

Custom Timeout

Use sendAndWait with a custom timeout for CI/CD pipelines:

try {
    // 30-second timeout
    var response = session.sendAndWait(
        new MessageOptions().setPrompt("Quick question"),
        30000  // timeout in milliseconds
    ).get();
} catch (ExecutionException e) {
    if (e.getCause() instanceof TimeoutException) {
        System.err.println("Request timed out");
        session.abort().get();
    }
}

Choosing a Model

List Available Models

Query available models before creating a session:

var models = client.listModels().get();

for (var model : models) {
    System.out.printf("%s (%s)%n", model.getId(), model.getName());
}

Use a Specific Model

var session = client.createSession(
    new SessionConfig().setModel("claude-sonnet-4")
).get();

Reasoning Effort

For models that support it, control how much effort the model spends reasoning before responding:

var session = client.createSession(
    new SessionConfig()
        .setModel("o3-mini")
        .setReasoningEffort("high")
).get();
Level Description
"low" Fastest responses, less detailed reasoning
"medium" Balanced speed and reasoning depth
"high" Thorough reasoning, slower responses
"xhigh" Maximum reasoning effort

Note: Only applies to reasoning models (e.g., o3-mini). Non-reasoning models ignore this setting.


Tool Filtering

Control which built-in tools the assistant can use with allowlists and blocklists.

Allowlist (Available Tools)

Restrict the session to only specific tools:

var session = client.createSession(
    new SessionConfig()
        .setAvailableTools(List.of("read_file", "search_code", "list_dir"))
).get();

When availableTools is set, the assistant can only use tools in this list.

Blocklist (Excluded Tools)

Allow all tools except specific ones:

var session = client.createSession(
    new SessionConfig()
        .setExcludedTools(List.of("execute_command", "write_file"))
).get();

Tools in the excludedTools list will not be available to the assistant.

Combining with Custom Tools

Tool filtering applies to built-in tools. Your custom tools (registered via setTools()) are always available:

var lookupTool = ToolDefinition.create("lookup_issue", "Fetch issue", schema, handler);

var session = client.createSession(
    new SessionConfig()
        .setTools(List.of(lookupTool))           // Custom tool always available
        .setAvailableTools(List.of("read_file")) // Only allow read_file from built-ins
).get();

Working Directory

Set the working directory for file operations in the session:

var session = client.createSession(
    new SessionConfig()
        .setWorkingDirectory("/path/to/project")
).get();

This affects how the assistant resolves relative file paths when using tools like read_file, write_file, and search_code.


Connection State & Diagnostics

Checking Connection State

Query the client's connection state at any time without making a server call:

ConnectionState state = client.getState();
switch (state) {
    case CONNECTED -> System.out.println("Ready");
    case CONNECTING -> System.out.println("Starting up...");
    case DISCONNECTED -> System.out.println("Not connected");
    case ERROR -> System.out.println("Connection failed");
}

The four states are:

State Description
DISCONNECTED Client has not been started yet
CONNECTING start() was called but hasn't completed
CONNECTED Client is connected and ready
ERROR Connection failed (check logs for details)

State Transitions

                 start()
  DISCONNECTED ──────────► CONNECTING
                               │
                    ┌──────────┼──────────┐
                    │ success  │          │ failure
                    ▼          │          ▼
              CONNECTED        │        ERROR
                    │          │
        stop() /    │          │
        forceStop() │          │
                    ▼          │
              DISCONNECTED ◄───┘
                    │
                    │ start()  (retry)
                    ▼
               CONNECTING
  • DISCONNECTED → CONNECTING: start() was called
  • CONNECTING → CONNECTED: Server connection and protocol handshake succeeded
  • CONNECTING → ERROR: Connection or protocol verification failed
  • CONNECTED → DISCONNECTED: stop() or forceStop() was called, or close() via try-with-resources
  • DISCONNECTED → CONNECTING: start() can be called again to retry

Server Status

Get CLI version and protocol information:

var status = client.getStatus().get();
System.out.println("CLI version: " + status.getVersion());
System.out.println("Protocol version: " + status.getProtocolVersion());

Authentication Status

Check whether the current connection is authenticated and how:

var auth = client.getAuthStatus().get();
if (auth.isAuthenticated()) {
    System.out.println("Logged in as: " + auth.getLogin());
    System.out.println("Auth type: " + auth.getAuthType());
    System.out.println("Host: " + auth.getHost());
} else {
    System.out.println("Not authenticated: " + auth.getStatusMessage());
}

Ping

Verify server connectivity:

var pong = client.ping("hello").get();
System.out.println("Server responded, protocol version: " + pong.protocolVersion());

See ConnectionState, GetStatusResponse, and GetAuthStatusResponse Javadoc for details.


Message Delivery Mode

Control how messages are delivered to the session:

// Default: message is enqueued for processing
session.send(new MessageOptions()
    .setPrompt("Analyze this codebase")
).get();

// Immediate: process the message right away
session.send(new MessageOptions()
    .setPrompt("Quick question")
    .setMode("immediate")
).get();
Mode Description
"enqueue" Queue the message for processing (default)
"immediate" Process the message immediately

Session Management

Multiple Concurrent Sessions

var session1 = client.createSession(
    new SessionConfig().setModel("gpt-4.1")
).get();

var session2 = client.createSession(
    new SessionConfig().setModel("claude-sonnet-4")
).get();

// Send messages concurrently
var future1 = session1.sendAndWait("Summarize REST APIs");
var future2 = session2.sendAndWait("Summarize GraphQL");

System.out.println("GPT: " + future1.get().getData().getContent());
System.out.println("Claude: " + future2.get().getData().getContent());

Resume a Previous Session

// Get the last session ID
var lastSessionId = client.getLastSessionId().get();

// Or list all sessions
var sessions = client.listSessions().get();

// Resume a session
var session = client.resumeSession(lastSessionId).get();

Resume Options

When resuming a session, you can optionally reconfigure many settings. This is useful when you need to change the model, update tool configurations, or modify behavior.

Option Description
model Change the model for the resumed session
systemMessage Override or extend the system prompt
availableTools Restrict which tools are available
excludedTools Disable specific tools
provider Re-provide BYOK credentials (required for BYOK sessions)
reasoningEffort Adjust reasoning effort level
streaming Enable/disable streaming responses
workingDirectory Change the working directory
configDir Override configuration directory
mcpServers Configure MCP servers
customAgents Configure custom agents
skillDirectories Directories to load skills from
disabledSkills Skills to disable
infiniteSessions Configure infinite session behavior

Example: Changing Model on Resume

// Resume with a different model
var config = new ResumeSessionConfig()
    .setModel("claude-sonnet-4")    // Switch to a different model
    .setReasoningEffort("high");    // Increase reasoning effort

var session = client.resumeSession("user-123-task-456", config).get();

See ResumeSessionConfig Javadoc for complete options.

Clean Up Sessions

// Delete a specific session
client.deleteSession(sessionId).get();

SessionConfig Reference

Complete list of all SessionConfig options for createSession():

Option Type Description Guide
sessionId String Custom session ID (auto-generated if omitted)
model String AI model to use Choosing a Model
reasoningEffort String Reasoning depth: "low", "medium", "high", "xhigh" Reasoning Effort
tools List<ToolDefinition> Custom tools the assistant can invoke Custom Tools
systemMessage SystemMessageConfig Customize assistant behavior System Messages
availableTools List<String> Allowlist of built-in tool names Tool Filtering
excludedTools List<String> Blocklist of built-in tool names Tool Filtering
provider ProviderConfig BYOK API provider configuration BYOK
onPermissionRequest PermissionHandler Handler for permission requests Permission Handling
onUserInputRequest UserInputHandler Handler for user input requests User Input Handling
hooks SessionHooks Lifecycle and tool execution hooks Session Hooks
workingDirectory String Working directory for file operations Working Directory
streaming boolean Enable streaming response chunks Streaming Responses
mcpServers Map<String, Object> MCP server configurations MCP Servers
customAgents List<CustomAgentConfig> Custom agent definitions Custom Agents
infiniteSessions InfiniteSessionConfig Auto-compaction for long conversations Infinite Sessions
skillDirectories List<String> Directories to load skills from Skills
disabledSkills List<String> Skills to disable by name Skills
configDir String Custom configuration directory Config Dir

See SessionConfig Javadoc for full details.


Next Steps

  • 📖 Advanced Usage - Tools, BYOK, MCP Servers, System Messages, Infinite Sessions, Skills
  • 📖 Session Hooks - Intercept tool execution and session lifecycle events
  • 📖 MCP Servers - Integrate external tools via Model Context Protocol
  • 📖 API Javadoc - Complete API reference