Working With Sprite
Mix.install([{:cia, github: "seanmor5/cia"}])
Sprites
Sprites is a remote sandbox option for CIA.
Sprite sandbox declarations support three lifecycle modes:
-
:ephemeralcreates dedicated compute for the agent and destroys it on stop -
:durablecreates the named sprite if needed and keeps it on stop -
:attachedexpects the named sprite to already exist and keeps it on stop
Environment
The examples below assume these environment variables are available:
sprite_name = System.fetch_env!("LB_CIA_SPRITE_NAME")
sprite_token = System.fetch_env!("LB_CIA_SPRITE_TOKEN")
openai_api_key = System.fetch_env!("LB_OPENAI_API_KEY")
:ok
Minimal Remote Agent
sandbox_opts = [name: sprite_name, token: sprite_token, lifecycle: :durable]
config =
CIA.new()
|> CIA.sandbox(:sprite, sandbox_opts)
|> CIA.workspace(:directory, root: "/workspace")
|> CIA.before_start(fn %{sandbox: sandbox} ->
with {_, 0} <- CIA.Sandbox.cmd(sandbox, "mkdir", ["-p", "/workspace/lib"]),
{_, 0} <-
CIA.Sandbox.cmd(
sandbox,
"sh",
["-lc", "printf '%s\n' 'defmodule Demo do' 'end' > /workspace/lib/demo.ex"]
) do
:ok
end
end)
|> CIA.harness(:codex, auth: {:api_key, openai_api_key})
{:ok, agent} = CIA.start(config)
CIA.subscribe(agent)
{:ok, thread} = CIA.thread(agent,
model: "gpt-5.4"
)
{:ok, turn} = CIA.turn(agent, thread, "Add a function to demo which returns :ok")
pretty = fn term ->
inspect(term, pretty: true, limit: :infinity, printable_limit: :infinity)
end
terminal_turn_event? = fn
%{"turnId" => turn_id, "status" => status} ->
turn_id == turn.id and status in ["completed", "failed", "cancelled"]
%{"id" => turn_id, "status" => status} ->
turn_id == turn.id and status in ["completed", "failed", "cancelled"]
%{"turn" => %{"id" => turn_id, "status" => status}} ->
turn_id == turn.id and status in ["completed", "failed", "cancelled"]
_ ->
false
end
print_codex_event = fn
{:server_message, %{method: method, params: params}} ->
IO.puts("\n[codex] #{method}")
IO.puts(pretty.(params || %{}))
{:channel_message, %{"type" => type} = msg} ->
IO.puts("\n[channel] #{type}")
IO.puts(pretty.(msg))
{:channel_message, msg} ->
IO.puts("\n[channel]")
IO.puts(pretty.(msg))
{:stderr, data} ->
IO.puts("\n[stderr]")
IO.puts(data)
{:transport_exit, reason} ->
IO.puts("\n[transport_exit]")
IO.puts(pretty.(reason))
{:unparsed_channel_message, raw} ->
IO.puts("\n[unparsed_channel_message]")
IO.puts(inspect(raw, limit: :infinity, printable_limit: :infinity))
end
listen = fn listen ->
receive do
{:cia, ^agent, {:harness, :codex, payload}} ->
print_codex_event.(payload)
stop? =
case payload do
{:transport_exit, _reason} ->
true
{:server_message, %{params: params}} ->
terminal_turn_event?.(params || %{})
_ ->
false
end
if stop? do
:ok
else
listen.(listen)
end
after
15_000 ->
IO.puts("\n[idle] no more Codex events")
:ok
end
end
listen.(listen)