Skip to main content

Events

Every agent, admin, and tool call in LogicGrid publishes a strongly typed event. The bus is the foundation that the logger and the trace both subscribe to — but it is also the way you wire your own handlers: forward to your log/metrics system, react to a ToolCallFailedEvent, surface a MaxLoopsReachedEvent to operators, flag ReflexionExhaustedEvent results for human review, and so on.

IAgentEventBus

public interface IAgentEventBus
{
void Subscribe<T>(Action<T> handler) where T : class;
void SubscribeAsync<T>(Func<T, Task> handler) where T : class;
Task PublishAsync<T>(T evt) where T : class;
}

public sealed class AgentEventBus : IAgentEventBus { }

Two subscription modes — sync and async — and one publish method. Handler exceptions are swallowed silently so a bad subscriber can't crash a run.

Get a bus

You almost never need to construct one yourself. .WithLogging() and .WithTracing() on AgentContext create one for you.

If you do want to subscribe directly:

var ctx = new AgentContext().WithLogging();
var bus = ctx.EventBus!;

bus.Subscribe<AgentCompletedEvent>(e =>
Console.WriteLine($"{e.AgentName}{e.TokensUsed.TotalTokens} tokens"));

Catalog

Events served in LogicGrid.Core.Events. Grouped by what they report on:

Run lifecycle

EventFired when
RunStartedEventAn admin's RunAsync is called.
RunCompletedEventAn admin's RunAsync returns successfully. Includes total counts and duration.
RunFailedEventAn admin's RunAsync throws.

Agents

EventFired when
AgentStartedEventAn agent's RunAsync is invoked (by an admin or directly).
AgentCompletedEventThe agent returns. Carries Output, Duration, RetryCount, TokensUsed, Cost.
AgentFailedEventThe agent throws.

LLM calls

EventFired when
LlmCallStartedEventAn agent issues an LLM request. Carries the messages array and model name.
LlmCallCompletedEventThe LLM responds. Carries response text, TokenUsage, duration.
LlmCallRetryingEventThe agent's RetryPolicy triggered a retry. Carries the validation error.

Tool calls

EventFired when
ToolCallStartedEventAn LLM-initiated tool call begins. Carries tool name and argsJson.
ToolCallCompletedEventThe tool returns. Carries the result and duration.
ToolCallFailedEventThe tool throws.

GroupChatAdmin

EventFired when
NextAgentSelectingEventThe admin asks its LLM to pick the next speaker. Carries the candidate list.
NextAgentSelectedEventThe selection is made. Carries the chosen agent's name and the loop number.
MaxLoopsReachedEventThe loop hit AdminOptions.MaxLoops without DONE or the configured finalAgentName being chosen. Carries MaxLoops and ForcedFinalAgentName. When the latter is non-null, the admin then invokes that agent once after the loop so the run still produces a usable final result — see Group chat → MaxLoops behaviour.

ParallelAdmin / MapReduceAdmin

EventFired when
ParallelRunStartedEventFan-out begins. Carries the agent names being launched.
ParallelRunCompletedEventAll branches have completed. Carries the agent count and total duration.
MapIterationCompletedEventA single map item completed. Carries index, total, and output.

ReflexionAdmin

EventFired when
ReflexionIterationEventEach actor → critic round completes. Carries Iteration, Approved, CriticFeedback.
ReflexionExhaustedEventThe loop reached ReflexionOptions.MaxIterations without the critic ever approving. Carries the last actor output and last critic feedback. The admin still returns the last output — subscribe to flag it for human review or downgrade downstream confidence.

GraphAdmin

EventFired when
GraphEdgeTraversedEventThe admin moves from one node to the next. Carries FromNode, ToNode, LoopNumber.
GraphTerminatedEventThe graph reaches a terminal node or MaxLoops. Carries the reason.

Budget

EventFired when
BudgetWarningEventSpend reaches BudgetWarningThreshold of MaxBudgetUsd. Default 80%.
BudgetExceededEventSpend exceeds MaxBudgetUsd. Followed by BudgetExceededException from the admin.

Subscribing examples

Forward to your own log system

var bus = ctx.EventBus!;
bus.Subscribe<AgentCompletedEvent>(e =>
myLogger.LogInformation(
"Agent {Name} ran in {Ms}ms ({Tokens} tokens)",
e.AgentName, e.Duration.TotalMilliseconds, e.TokensUsed.TotalTokens));

Detect retry storms

bus.Subscribe<LlmCallRetryingEvent>(e =>
{
if (e.AttemptNumber >= 3)
myAlerts.Page($"agent {e.AgentName} hit retry {e.AttemptNumber}");
});

Stream live progress to a UI

bus.SubscribeAsync<AgentStartedEvent>(async e =>
{
await uiHub.SendAsync("step", new { agent = e.AgentName, phase = "start" });
});

bus.SubscribeAsync<AgentCompletedEvent>(async e =>
{
await uiHub.SendAsync("step", new {
agent = e.AgentName, phase = "done", output = e.Output,
});
});

Notes

  • Subscriptions are not ordered. If you have two subscribers for the same event, don't depend on which runs first.
  • Handler exceptions are swallowed. A buggy subscriber can't crash a run — but you also won't see the exception. Add try/catch in your handler if you want to surface it.
  • Subscribe on a fresh bus per run if your handler holds per-run state. Or use RunStartedEvent to reset it.