Copilot Supercharged SDK for Elixir

Elixir SDK for the GitHub Copilot CLI. Communicates with the Copilot CLI server via JSON-RPC 2.0 over stdio.

Requirements

Installation

Add to your mix.exs dependencies:

def deps do
[
{:copilot_sdk_supercharged, path: "../elixir"} # or from Hex when published
]
end

Then fetch dependencies:

mix deps.get

Quick Start

alias Copilot.Client
alias Copilot.Session
alias Copilot.DefineTool
alias Copilot.Types.{CopilotClientOptions, SessionConfig, MessageOptions}
# Start the client (spawns the CLI process automatically)
{:ok, client} = Client.start_link(%CopilotClientOptions{log_level: "info"})
# Create a session
{:ok, session} = Client.create_session(client, %SessionConfig{})
# Subscribe to events
Session.on(session, fn event ->
if event["type"] == "assistant.message" do
IO.puts("Assistant: " <> event["data"]["content"])
end
end)
# Send a message and wait for the response
{:ok, response} = Session.send_and_wait(session, %MessageOptions{prompt: "What is 2+2?"})
IO.puts("Response: #{response["data"]["content"]}")
# Clean up
Session.destroy(session)
Client.stop(client)

Architecture

The SDK consists of the following modules:

ModuleDescription
Copilot.ClientMain client GenServer. Spawns the CLI process, manages the JSON-RPC connection, and provides session lifecycle operations (create, resume, delete, list).
Copilot.SessionSession GenServer. Sends messages, subscribes to events, handles tool calls, permissions, user input, and hooks.
Copilot.JsonRpcClientLow-level JSON-RPC 2.0 client using Erlang Port for stdio communication with Content-Length header framing.
Copilot.TypesAll type definitions as Elixir structs with typespecs.
Copilot.DefineToolHelper for defining tools to expose to the CLI.
Copilot.SdkProtocolVersionProtocol version constant (must match the server).

Defining Tools

Tools allow the assistant to call custom functions you define. Use Copilot.DefineTool.define/2:

tool = Copilot.DefineTool.define("get_weather",
description: "Get the current weather for a city.",
parameters: %{
"type" => "object",
"properties" => %{
"city" => %{"type" => "string", "description" => "City name"}
},
"required" => ["city"]
},
handler: fn %{"city" => city}, _invocation ->
"The weather in #{city} is sunny, 72F."
end
)
{:ok, session} = Client.create_session(client, %SessionConfig{tools: [tool]})

The handler function receives two arguments:

  1. The parsed arguments (a map matching your JSON schema)
  2. A Copilot.Types.ToolInvocation struct with session context

It can return:

Image Generation

Request image responses using response_format and image_options:

response = Copilot.Session.send_and_wait(session, %Copilot.Types.MessageOptions{
prompt: "Generate a sunset over mountains",
response_format: :image,
image_options: %{size: "1024x1024", quality: "hd", style: "natural"}
})

Event Subscriptions

Subscribe to all events or specific event types:

# All events
ref = Session.on(session, fn event -> IO.inspect(event) end)
# Specific event type
ref = Session.on(session, "assistant.message", fn event ->
IO.puts(event["data"]["content"])
end)
# Unsubscribe
Session.off(session, ref)

Common Event Types

Event TypeDescription
session.startSession started
session.idleSession finished processing
session.errorAn error occurred
user.messageUser message recorded
assistant.messageFinal assistant response
assistant.message_deltaStreaming response chunk (when streaming enabled)
tool.execution_startTool execution started
tool.execution_completeTool execution finished

Permissions

Handle permission requests from the server:

alias Copilot.Types.{PermissionRequest, PermissionRequestResult}
config = %SessionConfig{
on_permission_request: fn %PermissionRequest{kind: kind}, _ctx ->
IO.puts("Permission requested: #{kind}")
%PermissionRequestResult{kind: :approved}
end
}

User Input (ask_user)

Handle user input requests from the agent:

alias Copilot.Types.{UserInputRequest, UserInputResponse}
config = %SessionConfig{
on_user_input_request: fn %UserInputRequest{question: q}, _ctx ->
answer = IO.gets("#{q} > ") |> String.trim()
%UserInputResponse{answer: answer, was_freeform: true}
end
}

Hooks

Intercept session lifecycle events with hooks:

alias Copilot.Types.SessionHooks
config = %SessionConfig{
hooks: %SessionHooks{
on_pre_tool_use: fn input, _ctx ->
IO.puts("About to use tool: #{input["toolName"]}")
%{"permissionDecision" => "allow"}
end,
on_post_tool_use: fn input, _ctx ->
IO.puts("Tool completed: #{input["toolName"]}")
nil
end
}
}

Session Management

# List all sessions
{:ok, sessions} = Client.list_sessions(client)
# Resume a previous session
{:ok, session} = Client.resume_session(client, session_id)
# Delete a session
:ok = Client.delete_session(client, session_id)
# Get the last session ID
{:ok, last_id} = Client.get_last_session_id(client)
# List available models
{:ok, models} = Client.list_models(client)

Session Idle Timeout

Configure automatic session cleanup after a period of inactivity:

{:ok, client} = CopilotClient.start_link(session_idle_timeout_seconds: 300)

SessionFs (Persistent Session Filesystem)

SessionFs provides a virtual filesystem scoped to each session, enabling persistent state across compaction boundaries and session resumes.

{:ok, client} = CopilotClient.start_link(
session_fs: %{
initial_cwd: "/repo",
session_state_path: "/tmp/state",
conventions: "posix"
}
)

Session Metadata

Retrieve metadata about a session (model, creation time, status):

{:ok, meta} = CopilotClient.get_session_metadata(client, "session-123")

Skills and Sub-Agent Orchestration

Register skill directories and control sub-agent behavior:

{:ok, session} = CopilotClient.create_session(client,
skill_directories: ["./skills"],
disabled_skills: ["test-skill"],
include_sub_agent_streaming_events: true
)

Custom Providers (BYOK)

Use your own API endpoint:

alias Copilot.Types.ProviderConfig
config = %SessionConfig{
provider: %ProviderConfig{
type: "openai",
base_url: "http://localhost:11434/v1",
api_key: "ollama"
},
model: "llama3"
}

Protocol Version

The SDK protocol version must match the CLI server's version. The current version is 2. Version mismatches will produce a clear error message on connection.

Running the Example

cd elixir
mix deps.get
mix run examples/basic_example.exs

License

MIT - See LICENSE for details.