IronHive CLI - command-line agent frontend for the IronHive.Host SDK
Install
No framework information available.
README
ironhive-host
Universal Agent Host β CLI Β· server Β· embedded
μν Β·λ²μΒ·κΈ°λ₯ λͺ μΈ(λ―Έλ€μ¨μ΄ μ 체μ±)λ
CHARTER.mdμ°Έμ‘° β product-middleware Β§β€ μμ΄μ νΈ νΈμ€νΈ.
A foundation tool for AI-powered automationβnot just coding, but any task that benefits from intelligent command execution. One agent core (plan β execute β tool-call), exposed as an installable CLI, an embeddable .NET SDK, or a long-running server (stdio/JSON-Lines or HTTP/SSE) β pick the surface that fits, without re-implementing the agent loop.
The agent loop, context/compaction, mode system, MCP plugins, and permission engine itself live in
IronHive.Agent (from ironhive-agent),
which this package consumes as a PackageReference. IronHive.Host adds the hosting surface on top:
CLI/server/embed entry points, the IronHive.Host.Protocol turn-stream contract, layered config,
provider adapters, and session/execution-log/memory integration. See CHARTER.md for
the full role boundary.
Features
- Three surfaces, one core β CLI (
ironhive), embeddable SDK (IronHive.Host), and server runners (stdio or HTTP/SSE) all drive the sameIronHive.Agentloop. - MCP-native tooling β plugs into MCP servers (memory, code execution, custom tools) instead of hardcoding a tool set.
- Multi-provider out of the box β OpenAI, Anthropic, GoogleAI, Azure OpenAI, xAI, Ollama, LM Studio, GPUStack, and local inference via
LMSUPPLY_ENABLED. - Context-window safe by default β automatic history compaction (
ContextManager) and a hard-backstopTokenBudgetChatClientprevent silent context overflows, including on small quantized models. - Resilient tool-calling β
ResilientFunctionInvokerturns malformed tool-call arguments into model-actionable recovery hints instead of aborting the stream. - Layered configuration β global β project β environment β
.env, with automatic migration from legacysettings.json.
β οΈ 0.16.0 breaking repackage (upgrading from an older version? read this)
SDK λΌμ΄λΈλ¬λ¦¬ IronHive.Host.Core β IronHive.Host(SDKκ° top-level μ΄λ¦μ μμ ,
λ€μμ€νμ΄μ€ IronHive.Host.Core.* β IronHive.Host.*). CLI tool ν¨ν€μ§ IronHive.Host β IronHive.Cli
(μ€ν λͺ
λ Ήμ ironhive κ·Έλλ‘). turn-stream νλ‘ν μ½ κ³μ½μ λ³λ thin ν¨ν€μ§ IronHive.Host.Protocol(무μμ‘΄)λ‘ λΆλ¦¬ μ μ§.
μ IronHive.Host tool ν¨ν€μ§λ λ°°ν¬ μ€λ¨(unlist μλ β κΈ°μ‘΄ 볡μ κ³μ λμ); μ κ· μ€μΉλ dotnet tool install -g IronHive.Cli.
릴리μ€λ μ΄μ iyulab/ironhive-hostμμ self-host β μ ironhive-cli-releasesλ archive(read-only, κΈ°μ‘΄ v0.11-0.15 λ€μ΄λ‘λ URL μ μ§).
Contents
- Philosophy
- As an SDK (IronHive.Host)
- As a CLI (IronHive.Cli)
- Quick Start
- Configuration
- Core Library Integration
- Samples
- Development
- Contributing
- Related Projects
- License
Philosophy
Do one thing well.
Receive a command. Plan. Execute. Return.
βββββββββββββββββββββββββββββββββββββββββββ
β External Systems β
β CI/CD Β· Schedulers Β· Orchestrators β
ββββββββββββββββββββββ¬βββββββββββββββββββββ
β invoke
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β ironhive-host β
β Command β Plan β Execute β Done β
ββββββββββββββββββββββ¬βββββββββββββββββββββ
β MCP
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β Plugins (MCP Servers) β
β code-beaker Β· memory-indexer Β· ... β
βββββββββββββββββββββββββββββββββββββββββββ
As an SDK (IronHive.Host)
dotnet add package IronHive.Host
Build reusable agent hosts (agent loop, tools, session, providers). The CLI below is one consumption surface of this SDK.
As a CLI (IronHive.Cli)
dotnet tool install -g IronHive.Cli
ironhive
Or build from source:
git clone https://github.com/iyulab/ironhive-host
cd ironhive-host
dotnet build
Quick Start
# Interactive mode
ironhive
# Single command
ironhive -p "Write a README for this project"
# JSON output (for programmatic use)
ironhive -p "Hello" --output json
# Streaming JSON Lines
ironhive -p "Hello" --output jsonl
# Plain text (no ANSI)
ironhive -p "Hello" --plain
Without an API key
ironhive does not need a cloud provider to be useful. Local inference is on by default
(lmsupply.enabled: true; LMSUPPLY_ENABLED=false turns it off): with no other provider
configured, the first run downloads a GGUF model (cached under the LMSupply model cache) and runs
it in-process through LMSupply β no key, no server:
ironhive -p "Summarize this directory" # works with an empty config
ironhive doctor # shows which providers are configured and reachable
doctor reports Using local inference only (lmsupply) on such a setup and suggests a remote
provider only as a performance option β that is the expected state, not a warning to fix. The first
run is slower (model download + load); later runs start from the cache.
JSON Output Schema
--output json/jsonl is meant for programmatic consumption β piping into jq, another process,
or a script. Fields that would otherwise serialize as null (sessionId, usage, thinking,
toolCalls) are omitted from the object entirely rather than written as null.
--output json (single response, -p/--prompt):
{
"content": "string", // always present β the assistant's text reply
"sessionId": "string", // omitted if no session is active
"usage": { // omitted if usage wasn't reported
"inputTokens": 0,
"outputTokens": 0,
"totalTokens": 0
},
"thinking": { // present only with --show-thinking AND the model returned thinking content
"content": "string",
"tokenCount": 0
},
"toolCalls": [ // omitted if no tools were called
{
"name": "string",
"arguments": "string", // JSON-encoded arguments, as a string
"result": "string",
"success": true // true | false | null β null means the outcome is unknown
// (unset unless the underlying IChatClient has
// Microsoft.Extensions.AI function-invocation middleware)
}
]
}
On cancellation (Ctrl+C) or an unhandled error, the object is replaced with an error shape instead:
{ "error": "cancelled", "code": 130 } or { "error": "<message>", "code": 1 }.
--output jsonl (streaming JSON Lines β one object per line, discriminated by type):
type |
Fields | When |
|---|---|---|
start |
sessionId |
First line, always |
thinking |
content |
Only with --show-thinking, once per thinking delta |
text |
content |
Once per text delta |
tool_call |
id, name, arguments |
Once per tool-call delta (arguments is JSON-encoded) |
done |
sessionId |
Last line on success |
error |
error |
Instead of done, on cancellation or an unhandled exception |
sessions list --output json β an array ([] if empty), one object per session:
[
{
"id": "string",
"status": "string", // lowercased, e.g. "active", "completed"
"model": "string",
"created": "2026-08-31T00:00:00.0000000Z", // ISO 8601 (round-trip "o" format)
"messageCount": 0,
"firstMessage": "string"
}
]
sessions delete <id> --output json:
- Success:
{ "deleted": "<id>", "success": true } - Missing
<id>or session not found:{ "error": "string", "code": 1 }
Session Management
# Continue most recent session
ironhive -c
# Resume specific session
ironhive -r <session-id>
# List sessions
ironhive sessions list
ironhive sessions list --output json
Model Configuration
# Environment variables
export GPUSTACK_ENDPOINT=http://localhost:8080/v1
export GPUSTACK_API_KEY=your-key
export GPUSTACK_MODEL=gpt-4o-mini
# Or OpenAI
export OPENAI_API_KEY=sk-xxx
Configuration
Configuration is merged in order (later overrides earlier):
- Global:
~/.ironhive/config.yaml - Project:
.ironhive/config.yaml - Environment:
IRONHIVE_*,GPUSTACK_*,OPENAI_*,ANTHROPIC_*,GOOGLEAI_*/GOOGLE_API_KEY,XAI_*,AZURE_OPENAI_*,OLLAMA_*,LMSTUDIO_*,LMSUPPLY_ENABLED, and other provider-specific vars - .env file: Project root
.env
On first run, a legacy
~/.ironhive/settings.json(from earlier versions) is automatically migrated toconfig.yaml.
Config keys
The loader accepts these top-level keys in config.yaml. Acronym provider sections use lowercase keys; unknown top-level keys are ignored with a logged warning.
| Key | Notes |
|---|---|
gpuStack |
camelCase |
openai |
lowercase (acronym) |
anthropic |
|
googleai |
lowercase (acronym) |
azureopenai |
lowercase (acronym) |
xai |
|
ollama |
|
lmstudio |
lowercase (acronym) |
lmsupply |
lowercase (acronym) |
permissions |
|
compaction |
|
webSearch |
camelCase |
deepResearch |
camelCase |
chatBehavior |
camelCase |
# ~/.ironhive/config.yaml
openai:
apiKey: sk-...
model: gpt-4o-mini
gpuStack:
endpoint: http://localhost:8080
apiKey: ...
model: gpt-4o-mini
# .ironhive/config.yaml
# Context compaction is active by default β long sessions compact history
# instead of overflowing. Tune via the compaction section:
compaction:
useTokenBasedCompaction: true
protectRecentTokens: 40000 # most-recent tokens always kept
minimumPruneTokens: 20000 # only compact when at least this much is prunable
targetRatio: 0.70 # compact down to ~70% of the context window
Core Library Integration
Use IronHive.Host for direct .NET integration:
// Add to your project
<PackageReference Include="IronHive.Host" />
// Configure with DI
services.AddIronHiveWithOpenAI(apiKey, "gpt-4o-mini");
// Or
services.AddIronHiveWithOllama("llama3.2");
// Use
var agentLoop = serviceProvider.GetRequiredService<IAgentLoop>();
var response = await agentLoop.RunAsync("Hello");
// Streaming
await foreach (var chunk in agentLoop.RunStreamingAsync("Hello"))
{
Console.Write(chunk.TextDelta);
}
See samples/console-chat for a complete example.
ChatBehaviorConfig
Controls how FunctionInvokingChatClient orchestrates the tool-call iteration loop. Exposed in IronHiveConfig.ChatBehavior so you can tune per-model without forking the source.
| Property | Default | Notes |
|---|---|---|
MaximumIterationsPerRequest |
10 | Lower (5β7) for small 4K-window models; raise (15β20) for large-context models |
MaximumConsecutiveErrorsPerRequest |
3 | Backstop on back-to-back marshaller errors; rarely hit when ResilientFunctionInvoker is installed |
# .ironhive/config.yaml
chatBehavior:
maximumIterationsPerRequest: 7 # tune down for small/quantized models
maximumConsecutiveErrorsPerRequest: 3
Context Compaction
Long sessions are kept within the model's context window automatically. When the agent loop is built
(via the CLI, the server runners, or IronHive.Host DI), a ContextManager is wired from the
compaction config so older history is compacted (token-based, protecting the most recent turns and
important tool outputs) instead of silently overflowing.
- Enabled by default; tune via the
compaction:config section (see Configuration above) - Model-aware β the context window is sized from the active model
- Embedded consumers can set
options.CompactiononAddIronHive(...); manual loop builders can wire it viaHostContextManagerFactory.Create(compactionConfig, modelName)and pass the result toAgentLoop/ThinkingAgentLoop - Complements (does not replace)
TokenBudgetChatClient, which remains the hard backstop against per-request overflow
TokenBudgetChatClient
IChatClient decorator that short-circuits streaming calls when the accumulated message-history size would exceed a configurable fraction of the model's context window. Prevents context-overflow silent failures on small quantized models (e.g. 4K-window Gemma E4B).
- Sits between
FunctionInvokingChatClientand the underlying provider - Estimates tokens as
total-chars Γ· 4(conservative upper bound) - When the estimate exceeds
maxContextTokens Γ threshold, emits a gracefulChatFinishReason.Lengthresponse instead of letting the model error silently - Context window auto-detected via
IContextSizeProviderif the inner client exposes it; otherwise falls back todefaultMaxContextTokens
var client = new TokenBudgetChatClient(
inner: innerClient,
defaultMaxContextTokens: 4096,
threshold: 0.8); // trigger at 80 % of context window
ResilientFunctionInvoker
Factory for the M.E.AI FunctionInvoker delegate that converts marshaller-level ArgumentException (missing/malformed tool arguments) into model-actionable procedural error strings, enabling small quantized models to self-correct without aborting the stream.
Install via UseFunctionInvocation:
chatClient.UseFunctionInvocation(configure: c =>
{
c.FunctionInvoker = ResilientFunctionInvoker.Create();
});
When the model sends a tool call with a missing required parameter, instead of throwing and aborting, the invoker returns a numbered recovery directive telling the model exactly what is missing, where to find the value, and explicitly forbidding the empty-args retry pattern.
AgentServerRunner / AgentHttpRunner
Two runner implementations share the same processor delegate signature, enabling a single agent pipeline to serve either transport:
async IAsyncEnumerable<ServerEvent> ProcessMessage(
UserMessageRequest msg,
CancellationToken token)
{
// msg.Model carries the per-message model override (nullable)
await foreach (var evt in agentLoop.RunStreamingAsync(msg.Content, token)
.ToServerEvents(executionLog, token))
{
yield return evt;
}
}
// stdin/stdout JSON Lines (used by `ironhive run --server`)
var runner = new AgentServerRunner(ProcessMessage, logger);
await runner.RunAsync(ct);
// HTTP/SSE (host spawns the agent and communicates via REST)
var httpRunner = new AgentHttpRunner("http://localhost:5100", sessionId, ProcessMessage, logger);
await httpRunner.RunAsync(ct);
AgentHttpRunner expects these host endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
/api/agent/{id}/ready |
POST | Signal agent is up |
/api/agent/{id}/inbox |
GET (SSE) | Receive ServerRequest commands |
/api/agent/{id}/events |
POST | Deliver ServerEvent batches |
Both runners support typeInfoModifiers to extend polymorphic type registrations without mutating the base JsonSerializerOptions:
var runner = new AgentServerRunner(ProcessMessage, logger,
typeInfoModifiers: [ApplyCustomPolymorphismOverrides]);
AgentHttpRunner additionally exposes WaitForHitlResponseAsync / ResolveHitl for human-in-the-loop flows, and PublishEvent for out-of-band event delivery (e.g. provider fallback notices).
Protocol types (ServerRequest β agent, ServerEvent β host):
| Type | Discriminator | Key fields |
|---|---|---|
UserMessageRequest |
user_message |
Content, Model? |
ContextUpdateRequest |
context_update |
WorkingPath?, SelectedItems? |
HitlResponseRequest |
hitl_response |
Approved, Reason? |
CancelRequest |
cancel |
β |
ShutdownRequest |
shutdown |
β |
ToolStartEvent |
tool_start |
Tool, Input?, CallId? |
ToolEndEvent |
tool_end |
Tool, Success, Output? (β€ 8 KB), CallId? |
FallbackServerEvent |
fallback |
Kind (retry|fallback|exhausted), Category, Message, ProviderIndex, TotalProviders, Attempt, MaxAttempts |
TextDeltaEvent |
text_delta |
Content |
TurnEndEvent |
turn_end |
InputTokens?, OutputTokens?, TotalTokens? (summed across every model round-trip in the turn; null when the provider reported no usage) |
ErrorEvent |
error |
Message |
Samples
| Sample | Path | Description |
|---|---|---|
| Console Chat | samples/console-chat/ |
Core library direct integration |
| Web Chat | samples/web-ai-chat/ |
Next.js + CLI subprocess |
# Console Chat
cd samples/console-chat
dotnet run
# Web Chat
cd samples/web-ai-chat
npm install && npm run dev
Development
Requirements
- .NET 10 SDK
- Git
Build & Test
dotnet build
dotnet test
Project Structure
ironhive-host/
βββ src/
β βββ IronHive.Host.Protocol/ # Thin turn-stream contracts (zero-dep NuGet)
β βββ IronHive.Host/ # SDK / core library (NuGet)
β β βββ Config/ # Configuration classes
β β βββ Extensions/ # DI helpers
β β βββ Providers/ # LMSupply, IronHive chat client providers
β β βββ Server/ # AgentServerRunner, AgentHttpRunner (references Host.Protocol)
β β βββ Session/ # Session management
β β βββ Tools/ # Built-in tools, ResilientFunctionInvoker, TokenBudgetChatClient
β βββ IronHive.Cli/ # CLI application (tool command: ironhive)
βββ samples/
β βββ console-chat/ # .NET Core integration
β βββ web-ai-chat/ # Next.js + subprocess
βββ tests/
Contributing
Issues and pull requests are welcome. This project is pre-1.0 (0.x) β breaking changes land
freely for structural correctness; see CHARTER.md for scope and design intent
before proposing a feature. dotnet build && dotnet test (see Development) should
pass before opening a PR.
Related Projects
- ironhive β LLM abstraction
- ironbees β Multi-agent management
- memory-indexer β Semantic memory MCP
- code-beaker β Code execution platform
License
MIT
Changes from Previous Version
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.