Skip to content

The console

The console ships inside AgentPrism.UI as a pre-built, Brotli-compressed single-page application. There is no node_modules in your project and no JavaScript build in your pipeline.

builder.AddAgentPrism()
.UseOpenAI(apiKey)
.UseUI();
app.MapAgentPrism("/agentprism");

There is no separate mapping call. MapAgentPrism finds the registration and binds the console under the same prefix, so the prefix is written once.

The dashboard: run counts, error rate, tokens, cost, and a runs-over-time chart

Runs, errors, tokens, and cost across every agent, over a window you choose. Cost appears only when pricing is configured; models with no pricing are counted separately rather than silently written as zero.

A token breakdown bar shows where the tokens actually went: prompt-cache hits, fresh input, reasoning, and plain output. The four slices are disjoint — cache hits and reasoning are re-cut out of the input and output totals rather than added beside them, so the bar is never longer than the tokens that were spent. Each slice is named with its own count underneath, so colour is never the only signal.

The footer always states which storage is active. In the screenshot above it says in-memory — a reminder that this data ends with the process.

The agent list, showing code-defined and database-defined agents

Code-defined and database-defined agents in one list. The origin is on each entry, because it decides what you can do: a code agent can be run and read but not edited, and the console hides the edit form rather than offering one that would fail.

An agent's detail screen with its model binding, tools, and version history

The detail screen carries the model binding, tools, skills, callable agents, and — for a database agent — the version history with a diff between any two versions and a one-click rollback.

The editor validates as you save, using the same rules the compiler applies. An unknown tool or skill name is refused at write time, not on the first run.

The instructions panel also holds a culture-keyed section: add a culture tag (tr, de, …) with its own instructions text, and a run requesting that culture picks it up (see Culture-keyed instructions). The version diff screen shows each culture’s text as its own section.

The playground, with a streaming reply and tool calls rendered as cards

Talk to an agent. The reply streams token by token and tool calls appear as cards with their arguments and their results, so you can see why an answer came out the way it did rather than just reading the answer.

Attachments can be added to a message, and with the voice layer enabled there is a live conversation mode.

The run list with status, duration, token counts, and event counts

Every execution, filterable by agent, status, and kind — and, once your application binds IRunAttributionContext, by user and by label (key:value, or a bare key to match any value of it). Only root runs are shown by default — otherwise a single question that fanned out to four agents would fill the list with rows nobody started.

A run’s detail header names the user it belongs to and shows its labels as badges.

Opening a run gives the summary and the full event stream in order: message deltas, tool calls with arguments and results, errors with their class. When a reasoning model’s thinking is recorded, it renders as its own collapsible block, separate from the answer. A run that called other agents shows the whole tree, and each agent’s tokens and duration are attributed separately.

Two runs can be compared side by side, and any run can be scored — those scores sit next to the ones automatic judges write.

The session list with message counts and last activity

A session is a durable conversation. The list shows every session the active store knows about, with its agent, message count, and last activity; opening one reads the conversation back turn by turn, exactly as the model saw it.

Two things are only visible here. A session can be branched from any addressable item, which forks the conversation without touching the original — the fork opens as a new session with its own id. And attachments referenced by a message are listed with their type and size, so you can see what actually reached the provider.

In-memory storage keeps sessions only for the life of the process. Reading a conversation back, and branching it, need a SQL store.

The job queue with kind, status, attempt count, and next run time

Everything AgentPrism runs in the background, in one queue: queued agent runs, scheduled runs, workflow executions, evaluation runs, online-evaluation scoring, and webhook deliveries. Each row carries its kind, status, attempt count, and — for a failure — the classified error.

Schedules live on the same screen. A schedule is a cron expression plus the payload to run; leaving the expression empty makes it manual-only, which is the honest way to park one. Triggering a schedule by hand queues exactly the job the timer would have.

The queue only drains in a process that opted in with UseScheduling(). A queue that never moves is almost always an API process with no worker behind it — see Jobs, schedules, and queues.

The workflow list and a compiled workflow graph

Workflows defined in code and in the database. Opening one draws the compiled graph, and the node ids are the same executor ids that appear in run events — which is how nodes light up live while a workflow runs.

A workflow waiting on human input shows the pending request as a card; answering it resumes execution from the checkpoint as a new run.

The eval suite list

Suites, their cases, and their past runs. A case can be promoted straight from a real run, which is the fastest path from “this conversation went wrong” to “this is a regression test”.

The experiment list with per-arm results

An experiment splits live traffic between two versions of the same agent. The results show per-arm counts, error rates, tokens, and durations — and make no claim about a winner. The numbers are yours to judge.

Pending tool approvals awaiting a decision

Tool calls waiting on a human, with the arguments as they were recorded and an expiry. Approving or rejecting both resume the run — the model has to see a result or a refusal and carry on.

Standing “don’t ask again” rules live under Governance, where they can be reviewed, revoked, or written by hand with an argument condition (for example amount <= 100) instead of an exact-argument fingerprint. The condition editor is a closed set of comparisons, not a free-text expression box.

The tool list with each tool's JSON schema

Tools with their generated JSON schemas — read-only, and permanently so. This screen is where the code-only rule is most visible: you can see every tool an agent may use, and there is no way to add one from here. A tool registered with AddClientTool(...) carries a “client-side” badge: its declaration is still code-only, but its body runs on the caller instead of the server. See Client-side tools and the embeddable widget. A destructive tool carries a red badge, one that sends data outside the process an orange one, and a tool with a declared permission or a non-default timeout shows both next to it — see Tools: authorization and timeout.

The skill list with frontmatter, resources, and allowed tools

Skills sit beside the tools. A skill is markdown instructions plus read-only resources that an agent loads at run time, with approval. The editor shows the frontmatter, the compatibility and license fields, the allowed-tool list, and the resources; the markdown is stored as source text and the console does not render it.

This is the second place the code-only boundary is visible: a skill may carry a script, but the console can only reference a script the application already registered and granted. It cannot write one.

Registered model providers and their catalogues

Providers and their configured models, with a health status served from cache. A provider that implements no health check reports Unknown, which is not an error.

Registered MCP servers and the tools discovered from them

Remote MCP servers and the tools discovered from each. A server is a definition — an endpoint, a transport, and the name of the configuration key its authorization value is read from — so the console never holds a credential. Discovered tools carry the same approval badges as code-defined ones, and refreshing a server re-reads its catalog without a restart.

The audit trail, filterable by actor, action, and entity

Who changed what, when, and from what to what — filterable by actor, action, entity, and date range. Secret-looking fields are masked before anything is stored.

Quotas, retention policies, API keys, tenants, tenant provider bindings and egress policy (BYOK), and skill script grants have their own screens in the same area.

Inbound triggers with their target, payload mode, and signed-request URL

Inbound triggers let an external system start a queued run with one signed request and no API key. A trigger’s editor shows the exact URL to configure in that system, the target agent or workflow, and the name of the configuration key holding its signing secret — see Inbound triggers.

The diagnostics screen showing storage, migrations, and configuration

Diagnostics answers “is this deployment actually wired up the way I think”: which stores are active, whether migrations are up to date, and what is pending.

It is opt-in because setup details are sensitive. Enable the Admin endpoint before the screen can load it:

app.MapAgentPrism("/agentprism", options =>
{
options.EnableDiagnosticsEndpoint = true;
});

Settings: version, prefix, authentication method, active stores, theme, language

Version, prefix, authentication method, active stores, theme, and language.

AgentPrism.UI also builds a second, much smaller bundle: a floating chat widget meant for a different page — your own product’s site, not the console. It ships from a separate Vite entry, with its own budget gate (30 KB gzip; current size: 2.7 KB), so console code cannot leak into it. UseUI() serves both; no separate registration is needed.

<script src="https://your-agentprism-host/agentprism/embed/embed.js"
data-server="https://your-agentprism-host"
data-agent="support"
data-api-key="sk_..."></script>

The widget calls the run endpoint directly from the embedding page’s origin, so AllowedOrigins must list that origin — empty by default, so a page you have not explicitly allowed is blocked by the browser. See Client-side tools and the embeddable widget for the full walkthrough, including how the widget runs a client-side tool.

  • The console works under any prefix and learns it at run time
  • Light and dark themes; the default follows the operating system
  • English and Turkish; the default follows the browser. Server messages are shown as they are and never translated — the same failure has to read the same way in a log, a test, and a support ticket
  • A command palette on ⌘K / Ctrl+K
  • Buttons the caller has no role for are hidden, based on what /api/meta reports
  • The token is kept in sessionStorage and is gone when the tab closes; theme and language live in localStorage
  • The JavaScript budget is 250 KB gzip and is enforced by the build. Current size: 169.4 KB

The editor supports structured output, harness settings, and compaction. Three newer definition areas are code/HTTP-only today: provider-specific ProviderSettings, static McpResourceUris, and Knowledge vector bindings. Do not round-trip a definition that uses those fields through the current editor; edit it through code or the management HTTP API so the fields remain explicit.