Packages
agent_client_protocol
0.1.0
Elixir implementation of the Agent Client Protocol (ACP) for communication between code editors and AI coding agents. Includes schema types, JSON-RPC primitives, behaviours, and connection management.
Current section
Files
Jump to
Current section
Files
lib/acp/side.ex
defmodule ACP.Side do
@moduledoc """
Side behaviour for decoding JSON-RPC messages into typed structs.
Each side (client or agent) knows how to decode incoming requests and notifications.
"""
@callback decode_request(method :: String.t(), params :: map() | nil) ::
{:ok, any()} | {:error, ACP.Error.t()}
@callback decode_notification(method :: String.t(), params :: map() | nil) ::
{:ok, any()} | {:error, ACP.Error.t()}
end
defmodule ACP.ClientSide do
@moduledoc """
Client side of an ACP connection.
Decodes incoming requests (from agent) and notifications (from agent).
"""
@behaviour ACP.Side
@impl true
def decode_request(_method, nil), do: {:error, ACP.Error.invalid_params()}
def decode_request(method, params) do
case method do
"session/request_permission" ->
ACP.RequestPermissionRequest.from_json(params) |> wrap(:request_permission)
"fs/write_text_file" ->
ACP.WriteTextFileRequest.from_json(params) |> wrap(:write_text_file)
"fs/read_text_file" ->
ACP.ReadTextFileRequest.from_json(params) |> wrap(:read_text_file)
"terminal/create" ->
ACP.CreateTerminalRequest.from_json(params) |> wrap(:create_terminal)
"terminal/output" ->
ACP.TerminalOutputRequest.from_json(params) |> wrap(:terminal_output)
"terminal/kill" ->
ACP.KillTerminalCommandRequest.from_json(params) |> wrap(:kill_terminal_command)
"terminal/release" ->
ACP.ReleaseTerminalRequest.from_json(params) |> wrap(:release_terminal)
"terminal/wait_for_exit" ->
ACP.WaitForTerminalExitRequest.from_json(params) |> wrap(:wait_for_terminal_exit)
"_" <> custom_method ->
{:ok, {:ext_method, %ACP.ExtRequest{method: custom_method, params: params}}}
_ ->
{:error, ACP.Error.method_not_found()}
end
end
@impl true
def decode_notification(_method, nil), do: {:error, ACP.Error.invalid_params()}
def decode_notification(method, params) do
case method do
"session/update" ->
{:ok, notif} = ACP.SessionNotification.from_json(params)
{:ok, {:session_notification, notif}}
"_" <> custom_method ->
{:ok, {:ext_notification, %ACP.ExtNotification{method: custom_method, params: params}}}
_ ->
{:error, ACP.Error.method_not_found()}
end
end
defp wrap({:ok, val}, tag), do: {:ok, {tag, val}}
end
defmodule ACP.AgentSide do
@moduledoc """
Agent side of an ACP connection.
Decodes incoming requests (from client) and notifications (from client).
"""
@behaviour ACP.Side
@impl true
def decode_request(_method, nil), do: {:error, ACP.Error.invalid_params()}
def decode_request(method, params) do
case method do
"initialize" ->
ACP.InitializeRequest.from_json(params) |> wrap(:initialize)
"authenticate" ->
ACP.AuthenticateRequest.from_json(params) |> wrap(:authenticate)
"session/new" ->
ACP.NewSessionRequest.from_json(params) |> wrap(:new_session)
"session/load" ->
ACP.LoadSessionRequest.from_json(params) |> wrap(:load_session)
"session/set_mode" ->
ACP.SetSessionModeRequest.from_json(params) |> wrap(:set_session_mode)
"session/prompt" ->
ACP.PromptRequest.from_json(params) |> wrap(:prompt)
"session/list" ->
ACP.ListSessionsRequest.from_json(params) |> wrap(:list_sessions)
"session/fork" ->
ACP.ForkSessionRequest.from_json(params) |> wrap(:fork_session)
"session/resume" ->
ACP.ResumeSessionRequest.from_json(params) |> wrap(:resume_session)
"session/set_config_option" ->
ACP.SetSessionConfigOptionRequest.from_json(params) |> wrap(:set_session_config_option)
"session/set_model" ->
ACP.SetSessionModelRequest.from_json(params) |> wrap(:set_session_model)
"_" <> custom_method ->
{:ok, {:ext_method, %ACP.ExtRequest{method: custom_method, params: params}}}
_ ->
{:error, ACP.Error.method_not_found()}
end
end
@impl true
def decode_notification(_method, nil), do: {:error, ACP.Error.invalid_params()}
def decode_notification(method, params) do
case method do
"session/cancel" ->
ACP.CancelNotification.from_json(params) |> wrap(:cancel)
"_" <> custom_method ->
{:ok, {:ext_notification, %ACP.ExtNotification{method: custom_method, params: params}}}
_ ->
{:error, ACP.Error.method_not_found()}
end
end
defp wrap({:ok, val}, tag), do: {:ok, {tag, val}}
end