Current section
Files
Jump to
Current section
Files
lib/codex/protocol/collab_agent_state.ex
defmodule Codex.Protocol.CollabAgentState do
@moduledoc """
Typed representation of a collaboration agent lifecycle state.
"""
use TypedStruct
@type status ::
:pending_init
| :running
| :completed
| :errored
| :shutdown
| :not_found
| String.t()
typedstruct do
field(:status, status(), enforce: true)
field(:message, String.t() | nil)
end
@spec from_map(map() | atom() | String.t() | t() | nil) :: t()
def from_map(%__MODULE__{} = state), do: state
def from_map(nil), do: %__MODULE__{status: :pending_init}
def from_map(value) when is_atom(value), do: value |> Atom.to_string() |> from_map()
def from_map(value) when is_binary(value) do
%__MODULE__{status: decode_status(value)}
end
def from_map(%{} = value) do
normalized = normalize_keys(value)
cond do
Map.has_key?(normalized, "status") ->
%__MODULE__{
status: decode_status(Map.get(normalized, "status")),
message: normalize_message(Map.get(normalized, "message"))
}
Map.has_key?(normalized, "completed") ->
%__MODULE__{
status: :completed,
message: normalize_message(Map.get(normalized, "completed"))
}
Map.has_key?(normalized, "errored") ->
%__MODULE__{
status: :errored,
message: normalize_message(Map.get(normalized, "errored"))
}
true ->
%__MODULE__{status: :pending_init}
end
end
@spec to_map(t()) :: map()
def to_map(%__MODULE__{} = state) do
%{"status" => encode_state_status(state.status)}
|> maybe_put("message", state.message)
end
@spec to_event_value(t()) :: map() | String.t()
def to_event_value(%__MODULE__{status: :completed, message: nil}), do: "completed"
def to_event_value(%__MODULE__{status: :completed, message: message}),
do: %{"completed" => message}
def to_event_value(%__MODULE__{status: :errored, message: message}),
do: %{"errored" => message || ""}
def to_event_value(%__MODULE__{status: status}), do: encode_event_status(status)
defp decode_status("pendingInit"), do: :pending_init
defp decode_status("pending_init"), do: :pending_init
defp decode_status("running"), do: :running
defp decode_status("completed"), do: :completed
defp decode_status("errored"), do: :errored
defp decode_status("shutdown"), do: :shutdown
defp decode_status("notFound"), do: :not_found
defp decode_status("not_found"), do: :not_found
defp decode_status(other) when is_binary(other), do: other
defp decode_status(other), do: to_string(other)
defp encode_state_status(:pending_init), do: "pendingInit"
defp encode_state_status(:running), do: "running"
defp encode_state_status(:completed), do: "completed"
defp encode_state_status(:errored), do: "errored"
defp encode_state_status(:shutdown), do: "shutdown"
defp encode_state_status(:not_found), do: "notFound"
defp encode_state_status(status) when is_binary(status), do: status
defp encode_state_status(status), do: to_string(status)
defp encode_event_status(:pending_init), do: "pending_init"
defp encode_event_status(:running), do: "running"
defp encode_event_status(:completed), do: "completed"
defp encode_event_status(:errored), do: "errored"
defp encode_event_status(:shutdown), do: "shutdown"
defp encode_event_status(:not_found), do: "not_found"
defp encode_event_status(status) when is_binary(status), do: status
defp encode_event_status(status), do: to_string(status)
defp normalize_keys(%{} = map) do
map
|> Enum.map(fn {key, value} ->
{normalize_key(key), value}
end)
|> Map.new()
end
defp normalize_key(key) when is_atom(key), do: key |> Atom.to_string() |> normalize_key()
defp normalize_key(key) when is_binary(key), do: key
defp normalize_message(nil), do: nil
defp normalize_message(value) when is_binary(value), do: value
defp normalize_message(value), do: to_string(value)
defp maybe_put(map, _key, nil), do: map
defp maybe_put(map, key, value), do: Map.put(map, key, value)
end