Embeddable agent studio for Phoenix applications — a standalone LiveView dashboard for managing and debugging Jido AI agents.
Inspired by Mastra Studio and modeled after Oban Web, Jido Studio is a self-contained Hex package that mounts directly into your Phoenix router with zero asset pipeline integration.
Use the first-principles product doc as the reference for roadmap and UX decisions:
docs/first_principles.md
Add jido_studio to your dependencies:
def deps do
[
{:jido_studio, "~> 0.1.0"}
]
endMount the studio in your Phoenix router:
# lib/my_app_web/router.ex
import JidoStudio.Router
scope "/" do
pipe_through [:browser, :require_authenticated_user]
jido_studio "/studio"
endStart your server and visit /studio.
For a full Phoenix host-app setup (router/session/endpoint parity), use:
cd dev/studio_playground
mix deps.get
mix phx.serverThen open http://localhost:4702/studio.
Notes:
- Host app depends on this package via local path (
{:jido_studio, path: "../.."}) - A local Jido instance (
StudioPlayground.Jido) is started in supervision - Demo agents are auto-seeded at boot for immediate Studio interaction, including non-chat signal-first examples
Studio supports optional package-specific admin pages through extensions. Built-in extension routes are only compiled when their backing package is available.
Current built-in extension:
jido_messaging->Messaging / Roomspage
Optional extension modules can also be registered from the host app:
config :jido_studio,
extension_modules: [MyAppWeb.Studio.Extensions.Custom]For jido_messaging room listing, you can provide an explicit provider if your API differs:
config :jido_studio,
messaging_room_provider: {MyApp.MessagingAdmin, :list_rooms}Use this sequence for a clean first-time setup.
def deps do
[
{:jido_studio, "~> 0.1.0"}
]
endThen fetch dependencies:
mix deps.getSet your Jido supervisor module so Studio can show running instances (without this, Studio still shows discovered modules only):
# config/config.exs
config :jido_studio,
jido_instance: MyApp.JidoOptional override for multi-runtime installs:
config :jido_studio, :jido_instances, [
%{key: "primary", module: MyApp.Jido, label: "Primary"},
%{key: "batch", module: MyApp.BatchJido, label: "Batch Runtime"}
]When :jido_instances is present, Studio shows a runtime selector in the sidebar.
When it is absent, Studio derives a single runtime from :jido_instance and keeps the UI simplified.
Default observability persistence is ETS and needs no extra setup:
config :jido_studio, :persistence,
adapter: JidoStudio.Persistence.ETS,
opts: []Optional next-wave debug/observability defaults:
config :jido_studio,
live_ops: [
enabled: true,
auto_follow_default: true,
scope_keys: [:project_id, :user_id],
event_stream_limit: 100,
agent_list_poll_ms: 2_000,
viewer_tracking: true
],
delegation: [
enabled: true
],
tracing: [
hide_internal_default: true,
chunk_span_sampling: 1.0,
max_span_rows: 5_000
],
evals: [
enabled: true,
rule_sets: [:default]
],
agent_interactions: [
enabled: true,
default_tab: :auto,
runner_timeout_ms: 5_000,
runner_history_limit: 20,
internal_agent_tags: ["internal"]
]Studio now ships a built-in Presence default (JidoStudio.Presence) so viewer tracking works out-of-the-box.
If your host app already has its own Presence module, you can override it:
config :jido_studio,
pubsub: MyApp.PubSub,
live_ops: [
enabled: true,
viewer_tracking: true,
presence_module: MyApp.Presence
]After mount, Studio surfaces a Setup Assistant on Home with a re-entry card in Settings.
Checks cover:
- runtime connectivity/reachability
- persistence durability mode
- realtime event-driven vs polling fallback
- optional chat provider key presence
- smoke-path readiness
Profiles are guidance-only presets shown in-app:
Local Dev Fast StartChat Demo ShowcaseTeam Durable Ops
Apply profile snippet means show/copy snippet text with "What changes?" and rollback notes.
Studio does not mutate host config files.
Studio includes a bundled deterministic starter agent by default:
config :jido_studio, :beginner_agent,
enabled: trueStarter details:
- module:
JidoStudio.BeginnerAgent - purpose: first-run onboarding without provider keys or LLM dependencies
- startup behavior: explicit only; opening starter links can pre-open Start modal (
start=1), but users still confirmStart Instancemanually
Instance view model:
Basic Viewis the default on/agents/:slug/:instance_idfor first-run interactionAdvanced Viewpreserves the full Play/Observe/Configure workbench- mode can be deep-linked with
view=basic|advanced - legacy advanced deep links (
panel,tab,/observe,/configure) remain compatible
Set enabled: false to hide the beginner agent from discovery lists when it is not running.
If a beginner instance is already running, Studio keeps it visible so operators can inspect or stop it safely.
Discovery glossary in product terms:
Discovered modules: compiled agent modules visible in the selected runtimeRunning instances: currently started agent processesActive instances: running instances after current scope/filter application
Studio emits additive telemetry events under [:jido_studio, ...] for Phase 5 hardening:
[:interaction, :started][:interaction, :completed][:onboarding, :first_interaction_succeeded][:onboarding, :starter_opened][:onboarding, :starter_payload_prefilled][:onboarding, :starter_start_modal_opened][:triage, :warning_opened][:triage, :root_cause_opened][:interaction, :state_delta_viewed][:interaction, :next_action_opened][:incidents, :next_step_links_evaluated][:tour, :started][:tour, :step_viewed][:tour, :step_completed][:tour, :dismissed][:tour, :completed]
Common metadata keys include runtime, node, path, source, and session_id.
Event-specific metadata can include mode, status, warning_kind, trace_id, span_id, linked_count, and total_count.
Run the time-to-triage baseline benchmark with:
mix jido_studio.benchmark.triage# lib/my_app_web/router.ex
import JidoStudio.Router
scope "/" do
pipe_through [:browser, :require_authenticated_user]
jido_studio "/studio"
endSwitch the persistence adapter:
config :jido_studio, :persistence,
adapter: JidoStudio.Persistence.Ecto,
opts: [repo: MyApp.Repo, prefix: "public"]Copy the provided migration template into your host app and run:
mix ecto.migrateTemplate:
priv/ecto/migrations/20260215000000_create_jido_studio_persistence_tables.exs
Start Phoenix and open /studio. For MVP pages, verify:
Home(fleet health overview)Guide(opt-in guided tours with resume/replay)Agents(live runtime and debug toggle)Catalog(discovery catalog)Activity(operational timeline)Diagnostics(deep tooling + cluster health)About(links and product context)
No extra setup required. JidoStudio.Application starts the Studio runtime automatically.
Disable auto-start and supervise Studio runtime explicitly in your host app:
# config/config.exs
config :jido_studio, auto_start_runtime: false# lib/my_app/application.ex
children = [
MyAppWeb.Telemetry,
{Phoenix.PubSub, name: MyApp.PubSub},
MyAppWeb.Endpoint,
{JidoStudio.Runtime, []}
]Use this mode when you want explicit startup ordering and restart behavior in the host supervision tree.
Studio chat/thread workspace persistence is enabled by default and uses Jido.Storage adapters.
config :jido_studio,
thread_persistence: true,
thread_storage: {Jido.Storage.File, path: "priv/jido_studio/storage"},
thread_storage_mode: :studio,
thread_retention_days: 30,
persist_strategy_context: :summaryconfig :jido_studio,
thread_storage: {Jido.Storage.ETS, table: :jido_studio_threads}config :jido_studio,
thread_storage_mode: :inherit_jido_instanceNotes:
- ETS is fast but all workspace data is lost on BEAM restart.
- File/custom adapters are restart-safe.
- Studio persistence is for developer workflow context; application business persistence should remain in your app domain.
- Home — Fleet health cards, attention cues, and quick links
- Agents — Active instance index with follow/unfollow, auto-follow targets, filter/sort, viewer counts, uptime, and last activity
- Catalog — Discovery-powered catalog of agents/actions/sensors/plugins (canonical route
/catalog;/registryremains compatible) - Activity — Cross-surface operational timeline and plain-language summaries
- Diagnostics — Deep technical routing into traces/actions/workflows/signals/threads with node health summaries
- Dual Interaction Surface — Chat-first UX for chat-capable agents plus an
Interactworkbench for non-chat/runtime-driven interaction - Signal/Action Introspection — Hybrid runtime+static consumed-signal routes, route origins, action targets, and schema extraction with safe fallbacks
- Guarded Runner — Explicit arm-before-run execution, sync/async dispatch, payload JSON validation, and per-instance run history persistence
- Internal Agents by Tags — Discovered agents are split into
Product AgentsandInternal Agentsusingagent_interactions.internal_agent_tags - Smart Scope Selector — Runtime summary by default, optional runtime selector for multi-runtime installs, and advanced node scope (
node=allornode=<name>) on demand - Live Ops — Event-driven updates with polling fallback, scoped subscriptions, and viewer presence topics
- Messages/Events/TODOs — Runtime thread message snapshots, merged event stream with expandable raw payloads, and strategy TODO visibility
- Delegation/Tasks — Sub-agent detail tabs (config/messages/middleware/tools/events) and task lifecycle visibility
- Tool/Middleware Insights — Runtime summaries, config snapshots, and trace deep links
- Threads — Inspect persisted thread/memory entries
- Traces — Trace list, span timeline, internal-span filtering, and eval history
- Settings — Configure runtime behavior
- About — Official links, version info, and support/docs pointers
Studio Playground seeds non-chat examples under dev/studio_playground/lib/studio_playground/demo_agents/non_chat_agents.ex:
StudioPlayground.DemoAgents.SignalRunnerAgent— Signal-first route execution example for introspection and guarded dispatchStudioPlayground.DemoAgents.DeviceControlAgent— Schema-driven control flows with richer action payloads
These examples are registered in dev/studio_playground/lib/studio_playground/demo_agents.ex so they appear in the active/discovered lists by default.
- No new required config keys are needed for existing installs.
- Scope defaults to
All Nodesand a single derived runtime. - Runtime query support:
runtime=<runtime_key>(propagated across Studio navigation). - Node query support remains:
node=all|<node_name>. - Advanced node controls are collapsed by default and remembered locally in the browser.
Trace and span observability data is stored via the Studio persistence adapter.
Default (zero setup):
config :jido_studio, :persistence,
adapter: JidoStudio.Persistence.ETS,
opts: []Optional Ecto/Postgres adapter:
config :jido_studio, :persistence,
adapter: JidoStudio.Persistence.Ecto,
opts: [repo: MyApp.Repo, prefix: "public"]Migration template:
priv/ecto/migrations/20260215000000_create_jido_studio_persistence_tables.exs
Use a resolver module to control access:
defmodule MyApp.StudioResolver do
@behaviour JidoStudio.Resolver
@impl true
def resolve_user(conn), do: conn.assigns[:current_user]
@impl true
def resolve_access(%{role: :admin}), do: :all
def resolve_access(%{role: :dev}), do: :read_only
def resolve_access(_), do: {:forbidden, "/login"}
end
jido_studio "/studio", resolver: MyApp.StudioResolverApache-2.0 — see LICENSE.