Packages

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
agent_client_protocol lib acp client.ex
Raw

lib/acp/client.ex

defmodule ACP.Client do
@moduledoc """
Behaviour defining the interface that ACP-compliant clients must implement.
Clients are typically code editors that provide the interface between users and AI agents.
Required callbacks: `request_permission/1`, `session_notification/1`.
Optional callbacks have default implementations that return `{:error, method_not_found}`.
"""
@type result(t) :: {:ok, t} | {:error, ACP.Error.t()}
@doc "Request permission from the user for a tool call operation."
@callback request_permission(ACP.RequestPermissionRequest.t()) ::
result(ACP.RequestPermissionResponse.t())
@doc "Handle session update notifications from the agent."
@callback session_notification(ACP.SessionNotification.t()) :: :ok | {:error, ACP.Error.t()}
@doc "Write content to a text file. Optional."
@callback write_text_file(ACP.WriteTextFileRequest.t()) ::
result(ACP.WriteTextFileResponse.t())
@doc "Read content from a text file. Optional."
@callback read_text_file(ACP.ReadTextFileRequest.t()) ::
result(ACP.ReadTextFileResponse.t())
@doc "Create a new terminal and execute a command. Optional."
@callback create_terminal(ACP.CreateTerminalRequest.t()) ::
result(ACP.CreateTerminalResponse.t())
@doc "Get terminal output and exit status. Optional."
@callback terminal_output(ACP.TerminalOutputRequest.t()) ::
result(ACP.TerminalOutputResponse.t())
@doc "Release a terminal. Optional."
@callback release_terminal(ACP.ReleaseTerminalRequest.t()) ::
result(ACP.ReleaseTerminalResponse.t())
@doc "Wait for terminal command to exit. Optional."
@callback wait_for_terminal_exit(ACP.WaitForTerminalExitRequest.t()) ::
result(ACP.WaitForTerminalExitResponse.t())
@doc "Kill terminal command without releasing. Optional."
@callback kill_terminal_command(ACP.KillTerminalCommandRequest.t()) ::
result(ACP.KillTerminalCommandResponse.t())
@doc "Handle extension method requests. Optional."
@callback ext_method(ACP.ExtRequest.t()) :: result(ACP.ExtResponse.t())
@doc "Handle extension notifications. Optional."
@callback ext_notification(ACP.ExtNotification.t()) :: :ok | {:error, ACP.Error.t()}
@optional_callbacks [
write_text_file: 1,
read_text_file: 1,
create_terminal: 1,
terminal_output: 1,
release_terminal: 1,
wait_for_terminal_exit: 1,
kill_terminal_command: 1,
ext_method: 1,
ext_notification: 1
]
defmacro __using__(_opts) do
quote do
@behaviour ACP.Client
@impl ACP.Client
def write_text_file(_args), do: {:error, ACP.Error.method_not_found()}
@impl ACP.Client
def read_text_file(_args), do: {:error, ACP.Error.method_not_found()}
@impl ACP.Client
def create_terminal(_args), do: {:error, ACP.Error.method_not_found()}
@impl ACP.Client
def terminal_output(_args), do: {:error, ACP.Error.method_not_found()}
@impl ACP.Client
def release_terminal(_args), do: {:error, ACP.Error.method_not_found()}
@impl ACP.Client
def wait_for_terminal_exit(_args), do: {:error, ACP.Error.method_not_found()}
@impl ACP.Client
def kill_terminal_command(_args), do: {:error, ACP.Error.method_not_found()}
@impl ACP.Client
def ext_method(_args), do: {:ok, %ACP.ExtResponse{value: nil}}
@impl ACP.Client
def ext_notification(_args), do: :ok
defoverridable write_text_file: 1,
read_text_file: 1,
create_terminal: 1,
terminal_output: 1,
release_terminal: 1,
wait_for_terminal_exit: 1,
kill_terminal_command: 1,
ext_method: 1,
ext_notification: 1
end
end
end