Copilot SDK for Java - Documentation
This document provides detailed API reference and usage examples for the Copilot SDK for Java.
Table of Contents
API Reference
CopilotClient
Constructor
new CopilotClient()
new CopilotClient(CopilotClientOptions options)
Options:
cliPath- Path to CLI executable (default: “copilot” from PATH)cliArgs- Extra arguments prepended before SDK-managed flagscliUrl- URL of existing CLI server to connect to (e.g.,"localhost:8080"). When provided, the client will not spawn a CLI process.port- Server port (default: 0 for random)useStdio- Use stdio transport instead of TCP (default: true)logLevel- Log level (default: “info”)autoStart- Auto-start server (default: true)autoRestart- Auto-restart on crash (default: true)cwd- Working directory for the CLI processenvironment- Environment variables to pass to the CLI process
Methods
start(): CompletableFuture<Void>
Start the CLI server and establish connection.
stop(): CompletableFuture<Void>
Stop the server and close all sessions.
forceStop(): CompletableFuture<Void>
Force stop the CLI server without graceful cleanup.
createSession(SessionConfig config): CompletableFuture<CopilotSession>
Create a new conversation session.
Config:
sessionId- Custom session IDmodel- Model to use (“gpt-5”, “claude-sonnet-4.5”, etc.)tools- Custom tools exposed to the CLIsystemMessage- System message customizationavailableTools- List of tool names to allowexcludedTools- List of tool names to disableprovider- Custom API provider configuration (BYOK)streaming- Enable streaming of response chunks (default: false)mcpServers- MCP server configurationscustomAgents- Custom agent configurationsonPermissionRequest- Handler for permission requests
resumeSession(String sessionId, ResumeSessionConfig config): CompletableFuture<CopilotSession>
Resume an existing session.
ping(String message): CompletableFuture<PingResponse>
Ping the server to check connectivity.
getStatus(): CompletableFuture<GetStatusResponse>
Get CLI status including version and protocol information.
getAuthStatus(): CompletableFuture<GetAuthStatusResponse>
Get current authentication status.
listModels(): CompletableFuture<List<ModelInfo>>
List available models with their metadata (id, name, capabilities, billing info).
getState(): ConnectionState
Get current connection state. Returns one of: DISCONNECTED, CONNECTING, CONNECTED, ERROR.
listSessions(): CompletableFuture<List<SessionMetadata>>
List all available sessions.
deleteSession(String sessionId): CompletableFuture<Void>
Delete a session and its data from disk.
getLastSessionId(): CompletableFuture<String>
Get the ID of the most recently used session.
CopilotSession
Represents a single conversation session.
Properties
getSessionId()- The unique identifier for this session
Methods
send(String prompt): CompletableFuture<String>
Convenience method to send a simple text message. Equivalent to send(new MessageOptions().setPrompt(prompt)).
send(MessageOptions options): CompletableFuture<String>
Send a message to the session.
Options:
prompt- The message/prompt to sendattachments- File attachmentsmode- Delivery mode (“enqueue” or “immediate”)
Returns the message ID.
sendAndWait(String prompt): CompletableFuture<AssistantMessageEvent>
Convenience method to send a simple text message and wait for the session to become idle. Equivalent to sendAndWait(new MessageOptions().setPrompt(prompt)).
sendAndWait(MessageOptions options): CompletableFuture<AssistantMessageEvent>
Send a message and wait for the session to become idle. Default timeout is 60 seconds.
sendAndWait(MessageOptions options, long timeoutMs): CompletableFuture<AssistantMessageEvent>
Send a message and wait for the session to become idle with custom timeout.
on(Consumer<AbstractSessionEvent> handler): Closeable
Subscribe to session events. Returns a Closeable to unsubscribe.
var subscription = session.on(evt -> {
System.out.println("Event: " + evt.getType());
});
// Later...
subscription.close();
abort(): CompletableFuture<Void>
Abort the currently processing message in this session.
getMessages(): CompletableFuture<List<AbstractSessionEvent>>
Get all events/messages from this session.
close()
Dispose the session and free resources.
Event Types
Sessions emit various events during processing. Each event type extends AbstractSessionEvent:
UserMessageEvent- User message addedAssistantMessageEvent- Assistant responseAssistantMessageDeltaEvent- Streaming response chunkToolExecutionStartEvent- Tool execution startedToolExecutionCompleteEvent- Tool execution completedSessionStartEvent- Session startedSessionIdleEvent- Session is idleSessionErrorEvent- Session error occurredSessionResumeEvent- Session was resumed- And more…
Use pattern matching (Java 21+) to handle specific event types:
session.on(evt -> {
if (evt instanceof AssistantMessageEvent msg) {
System.out.println(msg.getData().getContent());
} else if (evt instanceof SessionErrorEvent err) {
System.out.println("Error: " + err.getData().getMessage());
}
});
Streaming
Enable streaming to receive assistant response chunks as they're generated:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setStreaming(true)
).get();
var done = new CompletableFuture<Void>();
session.on(evt -> {
if (evt instanceof AssistantMessageDeltaEvent delta) {
// Streaming message chunk - print incrementally
System.out.print(delta.getData().getDeltaContent());
} else if (evt instanceof AssistantMessageEvent msg) {
// Final message - complete content
System.out.println("\n--- Final message ---");
System.out.println(msg.getData().getContent());
} else if (evt instanceof SessionIdleEvent) {
done.complete(null);
}
});
session.send(new MessageOptions().setPrompt("Tell me a short story")).get();
done.get();
Listing Models
Query available models and their capabilities before creating a session:
try (var client = new CopilotClient()) {
client.start().get();
// List all available models
List<ModelInfo> models = client.listModels().get();
for (ModelInfo model : models) {
System.out.println("Model: " + model.getId());
System.out.println(" Name: " + model.getName());
if (model.getCapabilities() != null) {
System.out.println(" Max Output Tokens: " + model.getCapabilities().getMaxOutputTokens());
}
if (model.getPolicy() != null) {
System.out.println(" State: " + model.getPolicy().getState());
}
}
// Use a specific model from the list
var session = client.createSession(
new SessionConfig().setModel(models.get(0).getId())
).get();
}
Each ModelInfo contains:
id- Model identifier (e.g., “claude-sonnet-4.5”, “gpt-4o”)name- Human-readable display namecapabilities- Model limits including max output tokenspolicy- Policy state informationbilling- Billing/usage information
Advanced Usage
Manual Server Control
var client = new CopilotClient(
new CopilotClientOptions().setAutoStart(false)
);
// Start manually
client.start().get();
// Use client...
// Stop manually
client.stop().get();
Tools
You can let the CLI call back into your process when the model needs capabilities you own:
var lookupTool = ToolDefinition.create(
"lookup_issue",
"Fetch issue details from our tracker",
Map.of(
"type", "object",
"properties", Map.of(
"id", Map.of("type", "string", "description", "Issue identifier")
),
"required", List.of("id")
),
invocation -> {
String id = ((Map<String, Object>) invocation.getArguments()).get("id").toString();
return CompletableFuture.completedFuture(fetchIssue(id));
}
);
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setTools(List.of(lookupTool))
).get();
System Message Customization
Control the system prompt using SystemMessageConfig in session config:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setSystemMessage(new SystemMessageConfig()
.setMode(SystemMessageMode.APPEND)
.setContent("""
<workflow_rules>
- Always check for security vulnerabilities
- Suggest performance improvements when applicable
</workflow_rules>
"""))
).get();
For full control (removes all guardrails), use REPLACE mode:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setSystemMessage(new SystemMessageConfig()
.setMode(SystemMessageMode.REPLACE)
.setContent("You are a helpful assistant."))
).get();
Multiple Sessions
var session1 = client.createSession(
new SessionConfig().setModel("gpt-5")
).get();
var session2 = client.createSession(
new SessionConfig().setModel("claude-sonnet-4.5")
).get();
// Both sessions are independent
session1.send(new MessageOptions().setPrompt("Hello from session 1")).get();
session2.send(new MessageOptions().setPrompt("Hello from session 2")).get();
File Attachments
session.send(new MessageOptions()
.setPrompt("Analyze this file")
.setAttachments(List.of(
new Attachment()
.setType("file")
.setPath("/path/to/file.java")
.setDisplayName("My File")
))
).get();
Bring Your Own Key (BYOK)
Use a custom API provider:
var session = client.createSession(
new SessionConfig()
.setProvider(new ProviderConfig()
.setType("openai")
.setBaseUrl("https://api.openai.com/v1")
.setApiKey("your-api-key"))
).get();
Permission Handling
Handle permission requests from the CLI:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setOnPermissionRequest((request, invocation) -> {
// Approve or deny the permission request
var result = new PermissionRequestResult();
result.setKind("user-approved");
return CompletableFuture.completedFuture(result);
})
).get();
Infinite Sessions
Infinite sessions enable automatic context management for long-running conversations. When enabled (default), the session automatically manages context window limits through background compaction and persists state to a workspace directory.
How It Works
As conversations grow, they eventually approach the model's context window limit. Infinite sessions solve this by:
-
Background Compaction: When context utilization reaches the background threshold (default 80%), the session starts compacting older messages asynchronously while continuing to process new messages.
-
Buffer Exhaustion Protection: If context reaches the exhaustion threshold (default 95%) before compaction completes, the session blocks until compaction finishes to prevent overflow.
-
Workspace Persistence: Session state is persisted to a workspace directory containing:
checkpoints/- Session checkpoints for resumptionplan.md- Current conversation planfiles/- Associated files
Configuration
var infiniteConfig = new InfiniteSessionConfig()
.setEnabled(true)
.setBackgroundCompactionThreshold(0.80) // Start compacting at 80% utilization
.setBufferExhaustionThreshold(0.95); // Block at 95% until compaction completes
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setInfiniteSessions(infiniteConfig)
).get();
Configuration Options
| Option | Default | Description |
|---|---|---|
enabled |
true |
Whether infinite sessions are enabled |
backgroundCompactionThreshold |
0.80 |
Context utilization (0.0-1.0) at which background compaction starts |
bufferExhaustionThreshold |
0.95 |
Context utilization (0.0-1.0) at which the session blocks until compaction completes |
Accessing the Workspace
When infinite sessions are enabled, you can access the workspace path:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setInfiniteSessions(new InfiniteSessionConfig().setEnabled(true))
).get();
String workspacePath = session.getWorkspacePath();
if (workspacePath != null) {
System.out.println("Session workspace: " + workspacePath);
// Access checkpoints/, plan.md, files/ subdirectories
}
Disabling Infinite Sessions
For short conversations where context management isn't needed:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setInfiniteSessions(new InfiniteSessionConfig().setEnabled(false))
).get();
// session.getWorkspacePath() will return null
Error Handling
try {
var session = client.createSession().get();
session.send(new MessageOptions().setPrompt("Hello")).get();
} catch (ExecutionException ex) {
Throwable cause = ex.getCause();
System.err.println("Error: " + cause.getMessage());
}
