CxAgent.Core
The agent loop without a terminal.
Everything under cxagent's UI ships as a package: a conversation, the agent running it, tool execution, sub-agent delegation, permissions, MCP, plugins and resumable stores. You supply where the text goes; there is no UI dependency. cxagent is one consumer of it, and a Spectre.Console front end is another, in about a hundred lines.
dotnet add package CxAgent.Core
Targets net8.0, net9.0 and net10.0. MIT licensed.
The mental model
A session is a conversation. An agent is the loop that runs one turn of it. You supply where words go. Everything else follows from those three sentences.
You construct far less than people expect. SessionManager.Create builds what
sessions share — the resume database, the usage archive, the log directory, the permission rules
store, the command registry — and Open builds everything per-session. You never
construct an Agent or a tool.
An app is four calls
using CxAgent.Core.Sessions;
using CxAgent.Core.Llm;
using CxAgent.Core.Storage;
using var manager = SessionManager.Create(new AppPaths(configDir));
var session = manager.Open(
workingDirectory, // the permission boundary
resolution, // which model — see below
new SessionPorts { Observer = sink, ToolObserver = jobs }); // where words and tool activity go
if (session.Submit("summarise this folder") is Session.SubmitOutcome.Started started)
await started.Turn;
Submit is synchronous. It returns a receipt rather than a task, because there are
three outcomes and they are three different things for a caller to do:
Started (a turn began — await it), Queued (a turn was already running, and
this joined the queue), and NoAgent (no model is wired).
What is actually yours to write
| You supply | What it is |
|---|---|
ISessionObserver | where words go — the one thing you must implement |
IToolObserver | tool activity; a no-op is fine |
a prompt hook + a PermissionPolicy | how a human is asked, and which folder to judge against |
| a model | from code, from cxagent's own config.json, or a test double |
The text an observer receives is markdown, and Core escapes what it interpolates — so a path like
my_test_file.cs arrives with its underscores intact. Core writes no colour tags, so
there is nothing to strip.
Permissions
The engine is in here. The only thing left to you is asking a human. Trust per
folder, the working-directory boundary, edit modes, stored rules, the read-only command list, the
Auto classifier and the wrapper that gates every tool are all Core. What Core cannot do
is put a question on a screen, so you supply one delegate: given this request, what does the human
say?
Two halves, and one without the other is silent. buildGate gives the decider a way to
ask; SessionPorts.Policy gives it the folder and edit mode to judge against. Omit the
policy and every request is refused rather than guessed at; pass no buildGate at all and
nothing is gated — a legitimate headless arrangement, but a choice rather than something to inherit
by forgetting.
Tools
The agent gets read, write, edit, glob, grep, shell, http and web fetch without you registering
anything, plus todowrite, ask_user, agent and
skill. Each is wrapped in a permission gate at construction, so there is no path that
runs a tool ungated.
You can add your own — a deploy(env), a queryOurWarehouse(sql) — with
one line of wiring. They are offered beside the built-ins, dispatched behind them, and gated on the
way through. Or offer fewer: a ToolSelection narrows what an agent is handed, and a
withheld tool is refused if called by name rather than merely hidden.
ToolSelection = new ToolSelection([Tool.Inherited, Tool.Not.RunShell]),
Reference
- api.md — every public type, configuration, and tool selection
- tools.md
— injecting tools of your own: the
IAgentToolinterface and the two gates - The plugin contract — tools loaded from a DLL at run time
- The examples directory — five runnable apps, with what each one is for
The examples: ToolAgent (a console app with an injected tool), SpectreAgent (the same, with a rendered UI), ReadOnlyAgent (taking tools away — a selection you can watch hold), CalculatorPlugin and CalculatorAbiPlugin (a plugin end to end, managed and in one file of C).