> For the complete documentation index, see [llms.txt](https://docs.holons.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.holons.io/software/ai-ui.md).

# AI UI

> **Status: early — `@holons/ai-ui` is at `0.1.0`.** The agent loop and tool conversion are stable; the command registry currently lives as a fallback in this package and will move to `@holons/core/commands` upstream. Expect the import path for shared commands to change.

**`@holons/ai-ui`** is an in-process natural-language interpreter for Holons. It runs a Claude tool-use loop against the `@holons/core` commands so a user can say "log two hours on the garden task" and the agent translates it into the right sequence of `@holons/core` calls. It is what makes a Claude model **embedded inside** the holon's own infrastructure, rather than orchestrating it from outside.

This is the sibling of the [MCP server](/software/mcp-server.md): both expose `@holons/core` to AI agents, but they sit at different layers.

|               | `@holons/ai-ui`                                           | `@holons/mcp-ui`                                            |
| ------------- | --------------------------------------------------------- | ----------------------------------------------------------- |
| Process model | **In-process** — embeds Claude SDK inside the running app | **Out-of-process** — MCP server reached by external clients |
| Entry point   | `holons-ai` CLI (or library import)                       | stdio / SSE over HTTP                                       |
| Caller        | A holon's own scripts, cron jobs, bot handlers            | Claude Desktop, IDE integrations, other MCP-aware tools     |
| Best for      | Embedding intelligence into the holon                     | Letting external agents participate in the holon            |

The two are not redundant—they support different deployment patterns. A holon can run both.

## What it is

* **Name:** `@holons/ai-ui`
* **Location:** `harvest/packages/ai-ui/`
* **Binary:** `holons-ai`
* **SDK:** `@anthropic-ai/sdk`
* **Default model:** `claude-sonnet-4-6` (override via `HOLONS_AI_MODEL`)

## Using the CLI

```bash
# Single-prompt mode
holons-ai "Create a task in the garden holon called 'water the seedlings'"

# Pipe from stdin
echo "What tasks are open in the kitchen holon?" | holons-ai

# Help
holons-ai --help
```

Environment:

| Variable            | Purpose                    | Required |
| ------------------- | -------------------------- | -------- |
| `ANTHROPIC_API_KEY` | Claude API auth            | Yes      |
| `HOLONS_AI_MODEL`   | Override the default model | No       |

The CLI prints the agent's final text answer to stdout; everything else goes to stderr.

## How the loop works

```
user prompt
    │
    ▼
┌────────────────────────────────────────────────┐
│ Claude (sonnet-4-6) — sees:                    │
│  - system prompt (cached)                      │
│  - tool definitions (cached)                   │
│  - growing message history                     │
└────────────────────────────────────────────────┘
    │
    ▼  tool_use blocks?
    ├─ no  → return final text
    └─ yes
         │
         ▼
   For each tool_use:
     registry.get(name).execute(input)
     → JSON-stringify result
     → push as tool_result block
         │
         ▼
   Next iteration (back to Claude)
```

The loop terminates when the model emits `stop_reason: 'end_turn'` or produces no further `tool_use` blocks. Defensive cap: `maxIterations` (default 10), to prevent runaway loops.

Per-call defaults:

* `maxTokens` = 4096
* `maxIterations` = 10

## Prompt caching

The system prompt and tool definitions don't change across turns within a session, so they're sent with a single `cache_control: { type: 'ephemeral' }` breakpoint on the system block. Anthropic's render order is **tools → system → messages**, so a breakpoint on the system block caches both tools and system together. This:

* keeps the implementation well under the 4-breakpoint cache cap,
* avoids unnecessary cache writes,
* makes multi-turn sessions cheap.

The same pattern is recommended for any tool-use loop calling Claude — see the `agent.ts` source for the exact wiring.

## Tool definitions

Tools are derived from a `CommandRegistry`. Each registered command has:

```ts
interface CoreCommand {
  name: string;
  description: string;
  params: CoreCommandParam[];
  execute(params): Promise<{ ok: boolean; message?: string; data?: unknown }>;
}
```

At load time, every command is converted to an Anthropic `Tool` with a JSON Schema generated from its params. Required-param enforcement, type coercion, and side effects all live inside the command's own `execute()` — so the same registry powers both the AI loop and the [text UI](/software/harvest-dashboard.md) CLI.

Currently the registry lives at `packages/ai-ui/src/commands.ts` as a **fallback** (identical to the text-ui copy), designed so that when `@holons/core/commands` is extracted upstream, the package can switch to the shared registry with a no-op rename.

## System prompt

The default system prompt is intentionally narrow:

> *You are the Holons assistant. You help users manage tasks, hours, and shopping lists across their holons by calling the available tools. Always call a tool when the user requests an action; never fabricate results. After tools complete, summarize what was done in one sentence.*

Two principles worth noting:

1. **Always call a tool when the user requests an action.** The agent is not asked to be clever about when to act—if the user wants an action, an action happens.
2. **Never fabricate results.** Tool calls are the source of truth; the model's job is to dispatch and summarize, not to invent.

Callers can override the system prompt by passing `system` to `runAgent()`.

## Programmatic use

`runAgent()` is the library entry point — usable wherever a holon wants to embed natural-language intelligence (a bot handler, a webhook, a scheduled job):

```ts
import { runAgent } from '@holons/ai-ui';

const result = await runAgent("Create a task: water the seedlings", {
  model: 'claude-sonnet-4-6',
  maxIterations: 5,
});

console.log(result.text);
console.log(`tools called in ${result.iterations} turns`);
console.log(`stopped because: ${result.stopReason}`);
```

The returned `AgentResult` includes the final text, the iteration count, the last stop reason, and the full message transcript—useful for debugging or for feeding back into another loop.

## Testing

`packages/ai-ui/src/tools.test.ts` covers the command→tool conversion and the registry fallback. The agent loop is testable by injecting a pre-built `Anthropic` client into `runAgent({ client })`—no network needed for unit tests.

## When to use it

Pick `@holons/ai-ui` when:

* A holon's own code (a bot, a cron job, a script) wants to interpret natural-language input.
* You're comfortable shipping the Anthropic SDK as part of your holon's runtime.
* The agent should run **on behalf of** the holon, not as a separate user identity.

Pick the [MCP server](/software/mcp-server.md) when:

* An external client (Claude Desktop, an IDE) should be able to drive the holon.
* Multiple AI agents may connect concurrently with different actor identities.
* The integration should follow the standard MCP protocol rather than a Holons-specific API.

## See also

* [MCP Server](/software/mcp-server.md) — the out-of-process complement
* [Harvest](/software/harvest-dashboard.md) — the monorepo `@holons/ai-ui` is part of
* [HolonsBot](/software/holonsbot.md) — a natural place to embed an in-process agent loop
* [Glossary](/getting-started/glossary.md) — vocabulary for the protocol concepts the agent acts on


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.holons.io/software/ai-ui.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
