Agents
FabrCore 2.0 · Release and package availability
These guides track the current 2.0 source. Stable 2.0.0 publication is pending; package commands show the release target. Until it is published, follow the source quick start or use a matching available prerelease set. Release migration · Runtime modes
Agents
Agents are the core building block of FabrCore. Each agent is a distributed actor backed by an Orleans grain, with its own LLM context, tools, and persistent state. You build agents by extending FabrCoreAgentProxy.
Long-running work
Compose an existing proxy with CreateFabrCoreHarnessAgent or AsFabrCoreHarnessAgent for model-managed todos, iteration loops, durable snapshots, and delegation. Harness guide
Bounded context
The 2.0.0 compaction ladder bounds tool results, history, projections, and whole-run budgets. Compaction guide
FabrCoreAgentProxy
FabrCoreAgentProxy is a wrapper around the Microsoft Agent Framework's AIAgent that enables building distributed, actor-based AI agents.
Protected Members
| Member | Type | Description |
|---|---|---|
config | AgentConfiguration | Configuration passed during creation |
fabrcoreAgentHost | IFabrCoreAgentHost | Host interface for messaging, timers, persistence |
serviceProvider | IServiceProvider | DI service provider |
logger | ILogger | Pre-configured logger instance |
configuration | IConfiguration | Application configuration |
Basic Agent
using System.ComponentModel;
using FabrCore.Core;
using FabrCore.Sdk;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
[AgentAlias("my-agent")]
[Description("A helpful assistant")]
[FabrCoreCapabilities("Answers questions using configured tools")]
[FabrCoreNote("Configure tools and model access before use")]
public class MyAgent : FabrCoreAgentProxy
{
private AIAgent? _agent;
private AgentSession? _session;
public MyAgent(
AgentConfiguration config,
IServiceProvider serviceProvider,
IFabrCoreAgentHost fabrcoreAgentHost)
: base(config, serviceProvider, fabrcoreAgentHost) { }
public override async Task OnInitialize()
{
// Resolve plugins, standalone tools, and MCP tools from config
var tools = await ResolveConfiguredToolsAsync();
var result = await CreateChatClientAgent(
"default",
threadId: config.Handle ?? fabrcoreAgentHost.GetHandle(),
tools: tools);
_agent = result.Agent;
_session = result.Session;
}
public override async Task<AgentMessage> OnMessage(AgentMessage message)
{
var response = message.Response();
var chatMessage = new ChatMessage(ChatRole.User, message.Message);
await foreach (var update in _agent!.RunStreamingAsync(
chatMessage, _session!))
{
response.Message += update.Text;
}
return response;
}
}
Helper Methods
GetChatClient
Returns a raw IChatClient for direct LLM access. Use with custom agent frameworks or direct completions.
CreateChatClientAgent
Returns a fully configured ChatClientAgent with automatic message persistence via FabrCoreChatHistoryProvider.
var created = await CreateChatClientAgent(
"default",
threadId: config.Handle,
tools: [AIFunctionFactory.Create(plugin.MyTool)]
);
agent = created.Agent;
thread = created.Session;
Lifecycle
| Method | When Called | Purpose |
|---|---|---|
OnInitialize() | Agent creation / grain activation | Set up LLM clients, tools, and state |
OnMessage(AgentMessage) | Request/response message received | Process user messages and return responses |
OnEvent(EventMessage) | Fire-and-forget event received | Handle event notifications from other agents |
GetHealth(HealthDetailLevel) | Health check requested | Return custom health metrics |
AgentSession Patterns
| Pattern | Description | Use Case |
|---|---|---|
| One thread per agent | Create in OnInitialize, reuse | Single continuous conversation |
| Thread per user | Store in dictionary by user ID | Multi-user agents |
| Independent calls | Use a client without a persistent message-store provider | A new session alone does not disable a configured durable history store |
AgentAlias
Use the [AgentAlias] attribute to register your agent with one or more aliases for routing:
[AgentAlias("CustomerSupport")]
[AgentAlias("support")]
public class CustomerSupportAgent : FabrCoreAgentProxy
{
// ...
}
Multiple aliases can be applied to the same class. Whitespace is trimmed. The alias is used when creating agents via the REST API, the SDK API client, or host services.
Registry Attributes
FabrCore provides metadata attributes that enrich the discovery registry with information about what your agents can do. This metadata is returned by the /fabrcoreapi/discovery endpoint and helps other agents and users decide whether to interact with a given agent.
| Attribute | Multiplicity | Purpose |
|---|---|---|
[FabrCoreCapabilities("...")] | One per class | Describes what the agent can do — its core responsibilities and features |
[FabrCoreHidden] | One per class | Hides the agent from the discovery endpoint (still usable, just not listed) |
[FabrCoreNote("...")] | Multiple allowed | Usage instructions, prerequisites, or guidance on when not to use this agent |
FabrCoreCapabilities
Use [FabrCoreCapabilities] to describe the agent's core functionality. This is surfaced in the discovery response as the capabilities field:
[AgentAlias("job-agent")]
[Description("Manufacturing job management agent")]
[FabrCoreCapabilities("Manages manufacturing jobs — lookup, status tracking, priority changes, and ship date queries.")]
public class JobAgent : FabrCoreAgentProxy
{
// ...
}
FabrCoreNote
Use [FabrCoreNote] to add usage guidance, prerequisites, or warnings. Multiple notes are allowed and appear as an array in the discovery response:
[AgentAlias("job-agent")]
[FabrCoreNote("Requires a job number in context before most tools will work.")]
[FabrCoreNote("Do not use for quoting — use the quotes-agent instead.")]
public class JobAgent : FabrCoreAgentProxy
{
// ...
}
FabrCoreHidden
Use [FabrCoreHidden] to exclude an agent from the discovery endpoint. The agent remains fully functional — it just won't appear in the registry listing:
[AgentAlias("internal-worker")]
[FabrCoreHidden]
public class InternalWorkerAgent : FabrCoreAgentProxy
{
// Hidden from /fabrcoreapi/discovery but still fully operational
}
These attributes are optional but strongly recommended for any agent that will be discoverable by other agents or surfaced in a registry UI. Combine [Description] (from System.ComponentModel) for a short summary with [FabrCoreCapabilities] for detailed functionality.
AIContextProvider
Microsoft's AIContextProvider enables dynamic context injection during agent execution. Wire providers directly to each ChatClientAgent for per-agent context control.
var userInfoMemory = new UserInfoMemory(chatClient);
var created = await CreateChatClientAgent(
"default",
threadId: config.Handle,
tools: [AIFunctionFactory.Create(plugin.Echo)],
configureOptions: options =>
{
options.AIContextProviders = [.. options.AIContextProviders ?? [], userInfoMemory];
}
);
agent = created.Agent;
thread = created.Session;
AIContext Properties
| Property | Type | Description |
|---|---|---|
Instructions | string? | Additional instructions injected into the system prompt |
Messages | IList<ChatMessage>? | Messages added to the conversation context |
Tools | IList<AITool>? | Dynamic tools available for this invocation |
Use AIContextProvider for RAG (retrieval augmented generation), dynamic tool injection based on user permissions, and user memory extraction. See the Persistence guide for combining with custom state.
Health Monitoring
Override GetHealth(HealthDetailLevel) for custom health metrics:
public override async Task<ProxyHealthStatus> GetHealth(HealthDetailLevel detailLevel)
{
var health = await base.GetHealth(detailLevel);
return health with { CustomMetrics = new Dictionary<string, string>
{ ["application_status"] = "ready" } };
}
Health Detail Levels
| Level | Fields Returned |
|---|---|
Basic | Handle, State, Timestamp, IsConfigured |
Detailed | + AgentType, Uptime, MessagesProcessed, TimerCount, ReminderCount |
Full | + ProxyHealth, ActiveStreams, Diagnostics |
Agent Discovery
FabrCore provides two scopes for querying agents, each with a different purpose:
| API | Scope | Use Case |
|---|---|---|
IPrincipalGrain.GetTrackedAgents() | Per-principal | Agents tracked for a specific principal handle |
GET /fabrcoreapi/diagnostics/agents | Global | All agents in the cluster (administrative view) |
GET /fabrcoreapi/diagnostics/agents/statistics | Global | Aggregate counts by type and status |
Agents created through the Host API are routed through PrincipalGrain so they can be tracked for the owning principal. Use GetTrackedAgents() for principal-scoped queries and the diagnostics API for administrative views.
Delegation Patterns
Agents that orchestrate other agents can use the health checking and messaging primitives to discover and delegate work:
Harness agents can also create private internal specialists for bounded subtasks. These specialists are owned by the host session rather than exposed as durable public team members, which is useful for focused research, review, and transformation work. For reusable visible teams, define a Surface squad.
private async Task<List<AgentHealthStatus>> GetHealthyAgentsAsync(
IEnumerable<string> handles)
{
var results = new List<AgentHealthStatus>();
foreach (var handle in handles)
{
var health = await fabrcoreAgentHost.GetAgentHealth(
handle, HealthDetailLevel.Detailed);
if (health.State == HealthState.Healthy && health.IsConfigured)
results.Add(health);
}
return results;
}
// Use SendAndReceiveMessage for synchronous delegation
var result = await fabrcoreAgentHost.SendAndReceiveMessage(new AgentMessage
{
ToHandle = targetHandle,
FromHandle = fabrcoreAgentHost.GetHandle(),
Kind = MessageKind.Request,
Message = userMessage
});
Actor Model Constraints
FabrCore is built on Orleans, which follows the actor model. This provides strong guarantees but imposes design constraints:
- Grain isolation: Each agent's state is isolated. No shared mutable state between agents.
- Single-threaded execution: Grains process one message at a time (with
[AlwaysInterleave]exceptions). - Location transparency: Grains may run on different silos in a cluster. All communication is via messaging.
Calling tools on one agent from another agent bypasses the message queue, violates single-threaded guarantees, and introduces concurrency hazards. Use SendAndReceiveMessage() for inter-agent communication — its latency depends on routing, load and the work performed. If two agents need the same tool, configure the same plugin on both.
Operator management and isolated diagnostics
FabrCore 2.0 exposes configuration, health, custom state and thread management through the administration API. Restart preserves configuration and stored state; reset clears threads/custom state; eviction permanently deletes runtime and persisted state. Revision checks protect edits, and active normal/admin turns block disruptive actions.
Ask about this agent runs a read-only internal diagnostic agent over copied history and captured observations. It can overlap normal processing while keeping its own busy flag, model usage, timeout and reply path. It never calls normal OnMessage or business/MCP tools. Applications expose read-only data through GetAdminDiagnosticSnapshotAsync(CancellationToken). Diagnostic transcripts live in operator-owned sessions outside normal threads; default in-memory storage cannot resume after process restart.
Access connected services
FabrCoreAgentProxy exposes Connections.GetHttpClientAsync and GetAccessTokenAsync to trusted agent code. Ownership and exact agent grants are checked by the host. Keep raw tokens out of model tools. Read the integration guide.