Packages
In-process autonomous dev agent for Elixir. Captures errors, creates tickets, picks up board work, and runs Codex to investigate and fix.
Current section
Files
Jump to
Current section
Files
lib/sentinel.ex
defmodule Sentinel do
@moduledoc """
In-process autonomous dev agent for Elixir.
Sentinel captures runtime errors, deduplicates them, creates tracker issues,
and optionally invokes Codex to investigate and fix. It also polls the tracker
board, self-assigns eligible work, and starts executing it.
## Quick start (error collection)
# mix.exs
{:sentinel, "~> 0.1.0"}
# config/dev.exs
config :sentinel, enabled: true
## With ticket creation
config :sentinel,
tracker: {Sentinel.Tracker.Linear, api_key: System.get_env("LINEAR_API_KEY")},
tracker_project: "MY-PROJECT"
## With autonomous agent
config :sentinel,
agent: [enabled: true, command: "codex app-server", approval_policy: "never"],
board_watcher: [enabled: true, pickup_states: ["Todo"], labels: ["sentinel"]]
"""
alias Sentinel.{Collector, Deduplicator, ErrorBucket}
@doc "Manually report an error to Sentinel."
@spec report(Exception.t() | term(), keyword()) :: :ok
def report(error, opts \\ []) do
stacktrace = Keyword.get(opts, :stacktrace, [])
metadata = Keyword.get(opts, :metadata, %{})
Collector.handle_error(error, stacktrace, metadata)
end
@doc "Get all current error buckets."
@spec error_buckets() :: [ErrorBucket.t()]
def error_buckets do
Deduplicator.list_buckets()
end
@doc "Get Sentinel's current status."
@spec status() :: map()
def status do
%{
enabled: enabled?(),
environment: environment(),
error_bucket_count: length(error_buckets()),
tracker_configured: tracker_configured?(),
agent_enabled: agent_enabled?(),
board_watcher_enabled: board_watcher_enabled?()
}
end
@doc "Check if Sentinel is enabled."
@spec enabled?() :: boolean()
def enabled? do
Application.get_env(:sentinel, :enabled, false)
end
@doc false
@spec environment() :: atom()
def environment do
Application.get_env(:sentinel, :environment, Mix.env())
rescue
_ -> :prod
end
@doc false
@spec tracker_configured?() :: boolean()
def tracker_configured? do
Application.get_env(:sentinel, :tracker) != nil
end
@doc false
@spec agent_enabled?() :: boolean()
def agent_enabled? do
get_in(Application.get_env(:sentinel, :agent, []), [:enabled]) == true
end
@doc false
@spec board_watcher_enabled?() :: boolean()
def board_watcher_enabled? do
get_in(Application.get_env(:sentinel, :board_watcher, []), [:enabled]) == true
end
end