Help us shape the future of Go! Take the 2026 Go Developer Survey

uagent-harness

module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2026 License: Apache-2.0

README

uah

CI Docs checked by Memoria Release AUR License

A terminal coding agent that works like Codex, running on unreal-agent through uagent.

brew install viktordanov/tap/uah

The ledger lists what's next.

[!NOTE] The docs are kept in sync with the code by Memoria: CI fails when code changes and its README hasn't been reviewed.


Get started

  1. Install it:

    brew install viktordanov/tap/uah                                 # macOS and Linux
    yay -S uah-bin                                                   # Arch Linux (AUR)
    go install github.com/viktordanov/uagent-harness/cmd/uah@latest  # from source, Go 1.27.1 or later
    

    Or grab an archive from the releases.

  2. Sign in with codex login; uah uses your ChatGPT account. For another provider, pass --provider (openai, openrouter, fireworks, or ollama) and set its API key variable.

  3. Run uah doctor. It checks the login, sandbox, config, and MCP servers, and says how to fix anything that fails.

  4. Start in a repository:

    cd ~/code/proj
    uah                                       # the TUI
    uah "Fix the failing test in pkg/foo"     # the TUI, starting with a prompt
    
  5. If you installed with go install, add shell completion (bash, zsh, fish, or pwsh):

    uah completion zsh > "${fpath[1]}/_uah"
    

Keys worth knowing:

Key Does
enter Send. While the agent works, the message queues and goes out when it finishes
ctrl+enter Send now: the working agent reads it before its next model request. On an empty prompt, it sends the queued messages now, in order
esc esc Interrupt; queued messages stay. While the agent is idle, on an empty prompt: go back to an earlier message and edit it
/ Commands, such as /model, /effort, /compact, /context, /mcp, /agents, /status, /resume, and /new
@ Mention a workspace file (fuzzy search)
ctrl+t The detailed view: turns, tokens, and each tool's result

The TUI README lists every key and command.


Common tasks

Resume a session
uah resume              # pick one of this directory's sessions (--all: any directory)
uah resume --last       # this directory's most recent session
uah --session 3f2a      # a session by ID or unique prefix

When you quit the TUI, it prints the session's token usage and the command that continues it (uah resume <id>), as Codex does. In the TUI, ctrl+s opens the picker and ctrl+n starts a new session. The picker hides sessions from uah run and subagents, as Codex hides codex exec sessions.

Go back to an earlier message

Press esc twice on an empty prompt while the agent is idle, or type /rewind: your latest message is selected. Esc or ↑ selects an earlier one and ↓ a later one; enter puts the message back in the prompt, with its images, to edit and send again. The message and everything after it leave the agent's context and the screen, as Codex's backtrack does, and a resumed session keeps the cut. The session file keeps the old branch, and uah sessions show prints it. Files the agent changed stay changed. It needs the embedded engine; see the rewind design.

Headless mode

uah run prints progress on stderr and each answer on stdout, and exits when the agent is idle.

uah run "Fix the failing test in pkg/foo"
uah run --last "Now update the changelog"       # continue this directory's latest session
printf 'first\nsecond\n' | uah run --stdin       # each line is a message; lines queue while the agent works
uah run --stream "..."                           # JSONL events for scripts, with the answer as it arrives

On the embedded engine, the TUI shows the answer as the model writes it, and --stream adds text_delta, reasoning_delta, and stream_reset events before the final assistant_message. Plain uah run prints each answer once, when it is complete. See the streaming design.

It exits 0 when the run succeeds, 1 when it fails, 3 at the disk limit, 124 on a timeout, and 130 on an interrupt. Nobody can answer an approval headless, so commands that need one are declined with a reason. uah run --help lists the flags.

Search old sessions
uah sessions                           # this directory's sessions, newest first (--all: every directory)
uah sessions --search "flaky parser"   # sessions whose prompts or answers contain the words
uah sessions show 3f2a                 # the transcript (--json)
Images
  1. Copy an image, or a screenshot, and press ctrl+v (or alt+v) in the TUI on macOS or Linux. [Image #1] appears at the cursor.
  2. Or paste or drop an image file on the terminal, or choose one after @. The path becomes [Image #N].
  3. Write your message around the placeholders and press enter. The images go to the model with the message.

To remove an image, delete its placeholder: one backspace at its end removes it all. On Linux, uah reads the clipboard with wl-paste (Wayland) or xclip (X11); install one of them. Images larger than 2000 pixels on a side are scaled down. Images need the embedded engine; see the images design.

Model and effort
  • For this session: /model gpt-6-luna or /effort low in the TUI, or alt+, and alt+. to lower or raise the effort. On the embedded engine it applies from the next model request, even mid-run.
  • At start: uah -m gpt-6-luna -e medium, and --fast for priority processing.
  • For every session: model and effort in the configuration.
  • See what the provider offers: uah models (--json, --refresh), /model then tab in the TUI, or tab after -m. A model the provider does not list is refused with the nearest names ("gpt-luna-6 is not available on openai-codex; did you mean gpt-6-luna?").
Usage limits

With the default provider, openai-codex, uah shows how much of your ChatGPT plan's usage is left, as Codex does:

uah usage          # pro plan (openai-codex)
                   # weekly  [███████████████░░░░░]  22% used · 78% left · resets 15:44 on 26 Sep
uah usage --json   # the same for scripts
  • In the TUI, /usage shows the same, /status shows a row per window, and the footer shows the tightest one beside the context meter (weekly 78% left · 64% context left).
  • A notice warns once when a window passes 75, 90, and 95% used. When a run stops at the limit, a notice says when to try again.
  • uah doctor warns from 90% used.

Windows are named by their length (5h, daily, weekly), because a plan can have only a weekly window. uah reads the usage when you ask and after each run, never on a timer. Other providers have no usage to show.

A lost connection

When a model request fails because the connection dropped, it timed out, or the provider answered 429 or a server error, the runner's client sends it again: after 2 s, then 4, 8, and 16 s, then every 30 s. uah allows 10 attempts, about 3 minutes, so a Wi-Fi switch or a short outage does not end the run. While a request waits, the TUI's working line says so:

λ Reconnecting, attempt 3 of 10 (retrying in 8s • esc to interrupt)

When every attempt loses the connection, the run fails with "gave up after 10 attempts because the connection to the model was lost". Change the limit with request_max_attempts in the configuration, --max-attempts, or UNREAL_HARNESS_LLM_MAX_ATTEMPTS. The process engine retries the same way, but it cannot show the attempts.

Command rules
  1. When uah asks, answer s ("Yes, and don't ask again"). It saves a rule for the command's prefix.

  2. Or press shift+tab until the footer says auto mode: the auto-reviewer then approves or declines each escalation, and you are not asked. See Switch the permission mode.

  3. Or list prefixes in the configuration:

    [approvals]
    allow = ["go test", "git status"]
    forbid = ["git push --force"]
    

The approvals README gives the order in which rules, the sandbox, the auto-reviewer, hooks, and you decide.

Permission modes

Press shift+tab in the TUI. It cycles three modes, and the footer shows the current one:

Mode Commands can What needs approval
read only Read files, write nothing You
workspace (default) Write the workspace You
auto Write the workspace The auto-reviewer decides; you are not asked
  • On the embedded engine, a change applies from the next command, even mid-run. On the process engine, it applies from the next run.
  • A resumed session keeps its mode, with its model, effort, and fast mode.
  • To start in a mode, set permission_mode in the configuration. --sandbox read-only or --sandbox workspace-write also picks a mode for one session.
  • Full access (no sandbox) is not in the cycle. Set it with --sandbox danger-full-access or permission_mode = "full-access"; shift+tab then moves to read only.
Shell mode
  1. Type ! in the empty composer. The λ becomes !, and the footer says ! shell mode.
  2. Type the command, such as go test ./..., and press enter. It runs in the workspace at once, also while the agent works, and its output streams into the transcript with the exit status.
  3. Send your next message. The agent gets the command, its exit code, and its output with it, in Codex's <user_shell_command> format. The command alone never starts a turn.
  • Backspace on the empty composer, or esc, leaves shell mode. Esc esc stops a running command.
  • The output the agent sees is cut to 40,000 characters, keeping the start and the end. A command stops after an hour.
  • The command runs as your own, outside the sandbox and the command rules, as in Codex and Claude Code. user_shell_sandbox = true in the configuration runs it like the agent's commands instead: in the sandbox of the current permission mode, and refused by a forbid rule.

The shell mode design compares Codex and Claude Code.

MCP setup
uah mcp add docs -- npx -y @example/docs-mcp            # a stdio server (--env KEY=VALUE)
uah mcp add linear --url https://mcp.linear.app/mcp     # an HTTP server
uah mcp login linear                                     # OAuth in the browser, if the server asks for it
uah mcp list                                             # every server, its status, and its auth

uah mcp add writes Codex's [mcp_servers.<name>] format into your user file, so a Codex configuration copies over. /mcp in the TUI shows each server and its tools; /new picks up a server added or logged in while the TUI runs.

To stop a server's tools from asking for approval:

uah mcp add docs --approve -- npx -y @example/docs-mcp   # its tools never ask
uah mcp approve docs search --mode approve              # one tool never asks
uah mcp approve docs --mode prompt                      # every other tool always asks
uah mcp approve docs                                    # print the current modes

When the TUI asks about an MCP call, answer a ("Yes, and don't ask again for this tool"). It saves approval_mode = "approve" for the tool, and the session stops asking at once.

AGENTS.md and skills

Put them in AGENTS.md at the repository root or in any directory below it, as for Codex. To read CLAUDE.md too, set project_doc_fallback_filenames = ["CLAUDE.md"]. Skills go in .agents/skills/<name>/SKILL.md. /context shows how much of the context window they take.

Subagents and agent files

Ask for it, for example "use two subagents to review the TUI and the store in parallel". The agent starts them with Codex's tools; each shows in the transcript as AGENT <name> with what it is doing, and /agents lists them. Subagents never start subagents of their own. To define a kind of subagent, add a Markdown file with front matter, as for Claude Code (.claude/agents/*.md files work as they are), to ~/.uah/agents/, or to .uah/agents/ in a trusted project:

---
name: reviewer
description: Reviews a diff for bugs and missing tests.
tools: Bash, mcp__github
effort: high
approve: [git diff, git log, mcp__github__get_pull_request]
---

Review only; do not edit files. List each finding with its file and line.

The body is the subagent's instructions. tools limits the tools it is offered (Claude Code's Edit and Write are apply_patch; omit tools for all of them). approve lists commands and MCP tools it runs without asking, within the session's permission mode: read only stays read only, and a forbid rule still wins. Codex's TOML role files (reviewer.toml) work too. See agent files.

A subagent runs on the parent's provider. Ask for another model or effort ("use a subagent on gpt-6-luna with low effort"), set model, effort, and fast: true in its agent file, or set defaults in [agents]; see agent files. Ask for a forked subagent ("fork a subagent to write the tests for what we just discussed") to hand it the conversation so far through fork_context.

To watch a subagent, type /agents <name> or press alt+← and alt+→: the TUI shows its transcript, and what you type goes to it. When a subagent finishes or is interrupted, the main agent is told with your next message, as in Codex. uah sessions show subagent-1a2b3c4d prints a finished one's transcript.

Compaction and /clear

uah compacts automatically at 90% of the context window. /compact compacts now, and /compact keep the failing test names tells the summary what to focus on. /context shows what fills the window. The summary model, its prompt, and when compaction starts are configurable. /clear starts the agent fresh in the same session: its next request carries nothing from before, while the session keeps its history. /new starts a new session.

Custom prompts
uah prompts init           # writes compact.md, system.md, system-codex.md, and review.md to ~/.uah/prompts
uah prompts show system    # prints a built-in prompt: compact, system, system-codex, or review

uah prompts init starts from the built-in compaction prompt, the runner's host prompt (system.md), and the auto-review policy. It prints the lines to add to your user file: experimental_compact_prompt_file, model_instructions_file, and [review] policy_file. It also writes Codex's own system prompt as system-codex.md and prints its model_instructions_file line commented out, so Codex's prompt is used only when you choose it. Codex's prompt names Codex's tools, which differ from uah's (the differences). AGENTS.md files still follow the system prompt. Edit the files; each new session reads them. It overwrites existing files only with --force.

Hook setup

Add a hook, for example a notification when the agent is idle:

[[hooks.Stop]]
command = "osascript -e 'display notification \"uah is idle\"'"

Hooks in a project's .uah/config.toml run only after uah hooks trust; uah hooks lists them and whether each runs.

The /config panel

Type /config in the TUI. It lists the basic settings (compaction, the model and effort, fast mode, the permission mode, the details view, and the mouse) with each value and its source. ↑↓ choose, enter or space changes, esc closes. Each change is saved to your user file, keeping its comments, and applies to the running session where it can; compaction settings apply from the next session. A flag or a trusted project file that sets the same key still wins, and /config says so.

Inspect the configuration

uah config shows each setting's value and where it came from. uah doctor checks that everything works.


Configuration

Everything uah reads and writes lives in ~/.uah, as Codex keeps ~/.codex: the configuration, AGENTS.md, agents, prompts, skills, hook trust, MCP credentials, sessions, run records, the session index, pasted images, the model cache, and logs. UAH_HOME names another home; --config (UAH_CONFIG) and --state-dir (UAH_STATE_DIR) move just the user file or the state.

Two TOML files:

File Applies to When
~/.uah/config.toml (or --config) Every workspace Always
<workspace>/.uah/config.toml One workspace The user file marks the workspace trusted under [projects]; its hooks also need uah hooks trust

A flag wins over the environment, which wins over a resumed session's settings (its provider, model, effort, fast mode, and permission mode), then the project file, the user file, and the defaults. Unknown keys are errors, so a typo fails loudly.

Earlier versions used ~/.config/uagent, ~/.local/state/unreal-agent, and a project's .uagent. On its first start without ~/.uah, uah copies the two folders into ~/.uah and says so; the old folders are only read. It never moves a project's .uagent: uah and uah doctor show the git mv .uagent .uah that does. UAGENT_CONFIG and UAGENT_STATE_DIR are no longer read, and uah warns when either is set.

/config in the TUI changes the basic settings in the user file (see Change settings). Every key, by group. The configuration reference gives each one's type, default, flag, and merge rule, and the environment variables.

Group Keys
Model and engine provider, model, effort, fast, engine, timeout, max_disk, request_max_attempts
Sandbox permission_mode, sandbox_mode; [sandbox_workspace_write] network_access, writable_roots; [shell_environment_policy] inherit, ignore_default_excludes, exclude, include_only, set
Approvals approval_policy, approvals_reviewer; [approvals] allow, forbid; [review] model, effort, timeout, policy_file
Compaction auto_compact_percent, model_auto_compact_token_limit, model_context_window, compact_model, compact_effort, compact_prompt, experimental_compact_prompt_file, compact_user_message_max_tokens
Instructions and skills model_instructions_file, project_doc_fallback_filenames, project_root_markers, project_doc_max_bytes; [instructions] enabled, max_bytes
Hooks [[hooks.<Event>]] matcher, command, timeout
MCP servers [mcp_servers.<name>] command, args, env, env_vars, cwd, url, bearer_token_env_var, http_headers, env_http_headers, enabled, required, startup_timeout_sec, tool_timeout_sec, enabled_tools, disabled_tools, supports_parallel_tool_calls, default_tools_approval_mode, tools.<tool>.approval_mode, auth, scopes, oauth_resource, [oauth]; mcp_oauth_credentials_store, mcp_oauth_callback_port, mcp_oauth_callback_url
Subagents [agents] enabled, max_concurrent_threads_per_session, max_depth, default_subagent_model, default_subagent_reasoning_effort
TUI [tui] details, mouse
Projects [projects."<path>"] trusted

A short user file:

model = "gpt-6-sol"
effort = "high"
sandbox_mode = "workspace-write"                 # read-only, workspace-write, danger-full-access
project_doc_fallback_filenames = ["CLAUDE.md"]   # also read CLAUDE.md

[approvals]
allow = ["go test", "git status"]

[mcp_servers.docs]
command = "npx"
args = ["-y", "@example/docs-mcp"]

[projects."/Users/me/code/proj"]
trusted = true   # apply this workspace's .uah/config.toml

This repository's own .uah/config.toml is a working project file: its [approvals] forbid rules keep the agent from rm -rf /, force pushes, and git reset --hard.


How it works

Each part is documented next to its code. These are the summaries, with links to the full READMEs.

Sessions and the TUI

A session owns its settings, a message queue, at most one live run, pending approvals, and its hooks on one goroutine, and merges run events and its own events into one ordered stream. Messages queue while the agent works, a steer reaches the running agent when the engine allows it, and an interrupt keeps the queue.

The TUI is a pure reducer from session events and user intents to state and effects, a pure renderer from state to screen lines, and a thin Bubble Tea v2 shell that turns keys into intents and runs the effects against the session. Keys never change meaning: enter queues while the agent works, ctrl+enter sends now, esc esc interrupts, and ctrl+v pastes an image.

Read more: sessions, the session index, and the TUI with its look. Diagnostics go to ~/.uah/logs/uah-tui.log.

Engines

An engine starts runs of unreal-agent-runner for a session: the embedded engine (the default) runs the runner's packages inside uah, so messages, model, effort, fast mode, and the permission mode reach a live run, and the process engine spawns the runner binary through uagent. Both keep uagent's guards, session lock, and run records, apply the command rules, and write the same session files, so a session can move between them; one capability table says what the process engine does not run, and the session, uah doctor, and /status report it from there.

embedded is the default; choose with --engine or engine. The process engine sandboxes every command and applies the allow and forbidden command rules in the shell it gives the runner, but it has no live input, approvals, compaction, PreToolUse hooks, MCP servers, subagents, apply_patch, pasted images, a status line for a retried model request, or streaming: the answer appears when the model finishes it. A session on it shows one notice for each such feature the configuration uses, uah doctor warns about them in its engine check, and /status lists what the engine runs without. The engine README has the capability table and where each behavior lives.

File edits and diffs

Models edit files with Codex's apply_patch tool: a patch of *** Add File, *** Update File (with *** Move to), and *** Delete File sections with @@ hunks, parsed and applied as Codex does, with its lenient context matching and its messages. The embedded engine applies patches inside the writable roots at once, and asks for any other write as for a Bash escalation; the diff it records shows under the call in the TUI and in uah sessions show.

Read more: patches, and how patches are approved in approvals.

Instructions and skills

uah finds instruction files the way Codex does: the user's AGENTS.md, then one file per directory from the project root down to the workspace (AGENTS.override.md, else AGENTS.md, else a configured fallback such as CLAUDE.md). They are joined, capped at 32 KiB, and placed after the base instructions (the runner's default host prompt, or the file that Codex's model_instructions_file key names), and skills come from Codex's skill folders.

--no-instructions turns this off. Read more: instructions.

Sandbox

Commands run in the operating system's sandbox, as in Codex: Seatbelt on macOS and bubblewrap on Linux. The default mode, workspace-write, lets commands read the whole disk and write only the workspace and temporary directories, without network, and keeps .git, .uah, .agents, and .codex read-only.

Read more: sandbox.

Approvals, rules, and auto-review

On the embedded engine, each command runs in the sandbox unless a rule or an approval says otherwise: a command rule can allow, forbid, or ask; the model can ask to run a command outside the sandbox; and an escalation goes to PermissionRequest hooks, then you. The permission mode, which shift+tab cycles, picks the sandbox and who answers: you in read-only and workspace, the auto-reviewer alone in auto. The defaults are Codex's: workspace-write, on-request, and the user as reviewer (Codex's "Ask for approval").

Read more: approvals, rules, and auto-review.

Hooks

Hooks run a command at a session event with Claude Code's contract: the event arrives as JSON on stdin, exit 0 continues, exit 2 blocks with stderr as the reason, and any other exit is reported and ignored. Project hooks run only after uah hooks trust records their exact commands and the content of any local script they run.

Read more: hooks, with every event and its payload.

MCP servers

uah runs the MCP servers in [mcp_servers] (Codex's format) on the embedded engine through the official Go SDK: stdio and streamable HTTP servers, their tools offered as mcp__<server>__<tool> and called without blocking the agent, Codex's approval modes, OAuth logins with uah mcp login kept in the OS keyring, and uah mcp to list, add, remove, and approve servers.

  • Servers start on the first run (or /mcp) and stop with the session. One that fails to start is left out, unless it has required = true.
  • A call runs in the background, so the agent keeps working. A tool asks for approval by its approval_mode, as in Codex.
  • OAuth tokens are kept in the OS keyring (or a 0600 file without one) and refreshed as they expire. A server that needs a login shows "needs login" in /mcp and uah doctor.

Read more: MCP, and the design and validation.

Subagents

Subagents are child sessions that a session's agent starts, messages, waits for, and closes through Codex's v1 multi-agent tools. internal/agents implements them behind the engine.Subagents seam: the embedded engine offers the tools and runs their calls in the background, and the package owns the tools, the children's lifecycle, approvals through the parent, limits, hooks, and resume.

A subagent is the same as the main agent in every way except its session, which is nested under the parent's: the same instructions, skills, sandbox, approvals, hooks, MCP servers, and compaction. It asks for approval through the parent's session.

  • Its session ID is subagent-<uuid>. A role or the spawn call can give it another model, effort, or fast mode on the parent's provider.
  • A role is a Markdown file with front matter, as Claude Code's, or a Codex TOML role file; it can limit the subagent's tools and approve commands and MCP tools in advance, within the permission mode.
  • A subagent never starts subagents: the depth limit is 1.
  • fork_context starts it from a copy of the parent's history, so its first model request starts with the parent's, for the provider's prompt cache.
  • A failed subagent reports why, such as the provider's message, to the parent's wait_agent, the TUI, and uah run.
  • /agents <name> shows its live transcript in the TUI.

Read more: subagents, and the design and validation.

Compaction and /context

uah compacts a long conversation as Codex does: the earlier user messages stay verbatim and in order, up to the newest 20,000 tokens of them, and the rest is replaced by a model-written handoff summary. The summary model, effort, and prompt, when compaction starts, and the kept-message cap are configurable, with Codex's key names where Codex has them. The session file keeps the full history; only what goes to the model changes, and a compaction is saved next to the session so a resumed session keeps it.

/context shows what fills the window, as Claude Code's does: a 10×10 grid, one cell per percent, with each category's tokens (system prompt, instruction files, skills, tools, MCP tools, your messages, agent messages, and tool calls with their results), the free space, and the auto-compact buffer, then a line per file, skill, and tool. It breaks down the last request sent, estimated at 4 bytes a token and scaled to the input tokens the provider reported (internal/contextusage).

Read more: compaction, and the design and validation.

Model catalog

uah asks the provider which models the login can use, as Codex does: the list comes from the provider at runtime, is cached for five minutes with its ETag, and falls back to Codex's bundled catalog only when the provider cannot be asked. A new model therefore needs no uah release, and a mistyped model is refused before a run starts, with the nearest model names.

/model, -m completion, uah models, and uah doctor read the catalog, and the context window comes from it when the provider gives one. Read more: the model catalog.

Plan usage

The usage package reads the ChatGPT subscription's rate limits for the openai-codex provider, as Codex does: one read-only GET to the ChatGPT backend's usage endpoint with the login's credentials and uah's own identity. A reader caches the answer for 60 seconds and sends one request at a time; it reads on demand and after each run, never on a timer. Other providers have no usage.

app.Setup builds one reader per session, next to the model catalog, and the TUI gets it through bubble.Deps. uah usage, /status, the footer, the warnings, and uah doctor read through it. Read more: plan usage, and the design.


Development

Tests need no model or tokens: the process engine runs against uagent's fake runner, and the embedded engine against testing/fakellm, a scripted Responses API. One test drives the real unreal-agent-runner and the embedded engine with the same script and requires the same events; go test -short skips it.

go run ./cmd/uah --version   # build and run
go test -race ./...          # unit and end-to-end tests
golangci-lint run ./...      # lint (golangci-lint v2.13.2)

The title image is docs/assets/title.html, drawn in the TUI's colors and captured with headless Chrome: chrome --headless=new --force-device-scale-factor=2 --default-background-color=00000000 --window-size=1130,1400 --screenshot=uah.png title.html, then magick uah.png -trim +repage uah.png.

Pushing a v1.2.3 tag builds the release archives for macOS and Linux (arm64 and x86_64) with scripts/package-release.sh and attaches them to the GitHub release, each with a .sha256 file. Two builds of the same commit with the same Go version give the same bytes. To rebuild an existing tag, run the Release assets workflow with the tag.

CI runs the build, the race tests, the Markdown renderer's benchmarks once (so they keep running; its tests hold the bounds), and the linter on each push; the linter also fails on a function above 20 cyclomatic complexity, a backstop for the rule of about 15. Design records, the architecture rules, and the documentation procedure are in docs:

The configuration reference, design records for the harness, the TUI, state storage, sandboxing, compaction, MCP, subagents, pasted images, streaming, Markdown rendering, and going back to an earlier message, plus the architecture rules and documentation procedure for uagent-harness.

bench/tui is a separate Go module with the benchmark behind choosing Bubble Tea v2. go test -run '^$' -bench Markdown -benchmem ./internal/tui/render measures the Markdown renderer.


License and acknowledgements

uah is licensed under the Apache License, Version 2.0.

uah owes its shape to OpenAI Codex. Its configuration format, sandbox profiles, approval rules, auto-review, compaction, apply_patch, MCP handling, and subagent tools follow Codex closely, and some of its code and prompts are adapted from Codex's (Apache License 2.0, Copyright 2025 OpenAI). It runs on unreal-agent through uagent, with a few files adapted from unreal-agent's (MIT License, Copyright 2026 Unreal Labs), and borrows ideas from Claude Code (hooks, /context, Markdown agents, permission modes). THIRD_PARTY_NOTICES.md lists every adapted file and its license.

Directories

Path Synopsis
cmd
uah command
Command uah is a general-purpose harness for unreal-agent-runner: long-lived sessions, a terminal UI, and headless runs built on uagent.
Command uah is a general-purpose harness for unreal-agent-runner: long-lived sessions, a terminal UI, and headless runs built on uagent.
internal
agents
Package agents runs subagents: child sessions a session's agent spawns with the spawn_agent tool, following Codex's v1 multi-agent tools (see docs/design/subagents.md).
Package agents runs subagents: child sessions a session's agent spawns with the spawn_agent tool, following Codex's v1 multi-agent tools (see docs/design/subagents.md).
app
Package app turns what the CLI collected into a session ready to open: Resolve decides the settings without I/O, and Setup loads the files and builds the engine around them.
Package app turns what the CLI collected into a session ready to open: Resolve decides the settings without I/O, and Setup loads the files and builds the engine around them.
approval
Package approval decides how a command runs: in the sandbox, outside it, or not at all.
Package approval decides how a command runs: in the sandbox, outside it, or not at all.
compaction
Package compaction rewrites a model request the way Codex compacts a thread: the user messages stay verbatim and in order (the newest 20,000 tokens of them), and everything else before a point is replaced by one summary message.
Package compaction rewrites a model request the way Codex compacts a thread: the user messages stay verbatim and in order (the newest 20,000 tokens of them), and everything else before a point is replaced by one summary message.
config
Package config reads uah's configuration: a user file and, for workspaces the user trusts, a project file.
Package config reads uah's configuration: a user file and, for workspaces the user trusts, a project file.
config/tomledit
Package tomledit edits TOML configuration files in place, keeping their comments and formatting: it sets or removes one key, or cuts whole tables, and writes the file atomically.
Package tomledit edits TOML configuration files in place, keeping their comments and formatting: it sets or removes one key, or cuts whole tables, and writes the file atomically.
contextusage
Package contextusage breaks a model request into what fills the context window, as Claude Code's /context does: the system prompt, instruction files, skills, tools, MCP tools, and the conversation, with the free space and the auto-compaction buffer.
Package contextusage breaks a model request into what fills the context window, as Claude Code's /context does: the system prompt, instruction files, skills, tools, MCP tools, and the conversation, with the free space and the auto-compaction buffer.
engine
Package engine abstracts how the harness drives unreal-agent-runner.
Package engine abstracts how the harness drives unreal-agent-runner.
engine/codexauth
Package codexauth loads the ChatGPT subscription credentials the openai-codex provider sends: the access token and the account ID.
Package codexauth loads the ChatGPT subscription credentials the openai-codex provider sends: the access token and the account ID.
engine/embedded
Package embedded is the engine that runs unreal-agent-runner's packages in process.
Package embedded is the engine that runs unreal-agent-runner's packages in process.
engine/process
Package process is the engine that spawns unreal-agent-runner through uagent.
Package process is the engine that spawns unreal-agent-runner through uagent.
engine/process/shellgate
Package shellgate applies the command rules to the process engine's commands.
Package shellgate applies the command rules to the process engine's commands.
home
Package home is uah's one home, as Codex has ~/.codex and Claude Code has ~/.claude: the configuration, the instructions, hook trust, MCP credentials, sessions, run records, the index, images, the model cache, and logs all live in ~/.uah, or in $UAH_HOME when it is set.
Package home is uah's one home, as Codex has ~/.codex and Claude Code has ~/.claude: the configuration, the instructions, hook trust, MCP credentials, sessions, run records, the index, images, the model cache, and logs all live in ~/.uah, or in $UAH_HOME when it is set.
home/migrate
Package migrate moves uah's files, once, from the folders it used before ~/.uah: the configuration directory (~/.config/uagent) and uah's part of the state directory it shared with uagent (~/.local/state/unreal-agent).
Package migrate moves uah's files, once, from the folders it used before ~/.uah: the configuration directory (~/.config/uagent) and uah's part of the state directory it shared with uagent (~/.local/state/unreal-agent).
hooks
Package hooks runs user commands at session events, with Claude Code's contract: the event as JSON on stdin; exit 0 continues (optionally with JSON on stdout), exit 2 blocks with stderr as the reason, and any other exit is reported and ignored.
Package hooks runs user commands at session events, with Claude Code's contract: the event as JSON on stdin; exit 0 continues (optionally with JSON on stdout), exit 2 blocks with stderr as the reason, and any other exit is reported and ignored.
images
Package images carries images pasted into the prompt.
Package images carries images pasted into the prompt.
images/clipboard
Package clipboard reads an image from the system clipboard through the system's own tools, without cgo: osascript on macOS, and wl-paste or xclip on Linux.
Package clipboard reads an image from the system clipboard through the system's own tools, without cgo: osascript on macOS, and wl-paste or xclip on Linux.
instructions
Package instructions finds instruction files (AGENTS.md, as Codex does) and builds the host prompt for the runner.
Package instructions finds instruction files (AGENTS.md, as Codex does) and builds the host prompt for the runner.
llmcall
Package llmcall makes one model call outside the agent loop: items in, text out.
Package llmcall makes one model call outside the agent loop: items in, text out.
mcp
Package mcp runs Model Context Protocol servers for the embedded engine: it starts the configured servers, lists their tools under Codex's names, and calls them.
Package mcp runs Model Context Protocol servers for the embedded engine: it starts the configured servers, lists their tools under Codex's names, and calls them.
models
Package models is uah's model catalog: which models the provider offers to this login, with their context windows, reasoning levels, and service tiers.
Package models is uah's model catalog: which models the provider offers to this login, with their context windows, reasoning levels, and service tiers.
models/modelstest
Package modelstest serves model catalogs to tests without the network.
Package modelstest serves model catalogs to tests without the network.
patch
Package patch parses and applies Codex's apply_patch format, and computes the diff of what a patch changed.
Package patch parses and applies Codex's apply_patch format, and computes the diff of what a patch changed.
review
Package review is the automatic approval reviewer, Codex's "auto-review" (guardian): one model call judges an action that needs approval, from the user's messages (trusted), the recent tool calls without their output (untrusted), and the action itself.
Package review is the automatic approval reviewer, Codex's "auto-review" (guardian): one model call judges an action that needs approval, from the user's messages (trusted), the recent tool calls without their output (untrusted), and the action itself.
rules
Package rules is Codex's command rules: `prefix_rule` entries in Starlark `.rules` files, matched against a command's words, with the strictest matching decision winning.
Package rules is Codex's command rules: `prefix_rule` entries in Starlark `.rules` files, matched against a command's words, with the strictest matching decision winning.
sandbox
Package sandbox runs shell commands inside the operating system's sandbox, as Codex does: Seatbelt (sandbox-exec) on macOS and bubblewrap on Linux.
Package sandbox runs shell commands inside the operating system's sandbox, as Codex does: Seatbelt (sandbox-exec) on macOS and bubblewrap on Linux.
session
Package session is the long-lived object the TUI and `uah run` talk to.
Package session is the long-lived object the TUI and `uah run` talk to.
store
Package store keeps a rebuildable SQLite index of the run records in the state directory, so listing, searching, and activity do not reread every summary (docs/design/state.md).
Package store keeps a rebuildable SQLite index of the run records in the state directory, so listing, searching, and activity do not reread every summary (docs/design/state.md).
tui/bubble
Package bubble is the Bubble Tea shell around the TUI state and renderer: it turns keys into intents, runs effects against the session, batches session events, and draws frames.
Package bubble is the Bubble Tea shell around the TUI state and renderer: it turns keys into intents, runs effects against the session, batches session events, and draws frames.
tui/render
Package render draws TUI state as screen lines with lipgloss.
Package render draws TUI state as screen lines with lipgloss.
tui/render/markdown
Package markdown draws the Markdown the model writes as terminal lines, in uah's look after Codex's: goldmark parses it with the GFM extensions, and a renderer of its own walks the tree.
Package markdown draws the Markdown the model writes as terminal lines, in uah's look after Codex's: goldmark parses it with the GFM extensions, and a renderer of its own walks the tree.
tui/state
Package state is the TUI's model and reducer.
Package state is the TUI's model and reducer.
usage
Package usage reads the ChatGPT subscription's rate limits for the openai-codex provider, as Codex does: the windows (for example 5 hours and a week), the percent used, and when each resets.
Package usage reads the ChatGPT subscription's rate limits for the openai-codex provider, as Codex does: the windows (for example 5 hours and a week), the percent used, and when each resets.
usershell
Package usershell runs a command the user types in the TUI's shell mode (`!` in the composer), as Codex's user shell commands and Claude Code's bash mode do, and writes the record the agent sees of it.
Package usershell runs a command the user types in the TUI's shell mode (`!` in the composer), as Codex's user shell commands and Claude Code's bash mode do, and writes the record the agent sees of it.
testing
fakellm
Package fakellm is a scripted OpenAI Responses API for tests.
Package fakellm is a scripted OpenAI Responses API for tests.
harnesstest
Package harnesstest sets up isolated uagent environments for tests: the fake runner from uagent, a workspace, a state directory, and Codex credentials that pass preflight.
Package harnesstest sets up isolated uagent environments for tests: the fake runner from uagent, a workspace, a state directory, and Codex credentials that pass preflight.
mcpserver command
Command mcpserver is a stdio MCP server for tests, built with the official Go SDK.
Command mcpserver is a stdio MCP server for tests, built with the official Go SDK.
oauthserver
Package oauthserver is an MCP server behind OAuth 2.1 for tests, in one httptest server: the protected resource (/mcp, a streamable HTTP MCP server built with the official SDK, with a whoami tool), its protected resource metadata, and a small authorization server with dynamic client registration, PKCE (S256), and refresh tokens.
Package oauthserver is an MCP server behind OAuth 2.1 for tests, in one httptest server: the protected resource (/mcp, a streamable HTTP MCP server built with the official SDK, with a whoami tool), its protected resource metadata, and a small authorization server with dynamic client registration, PKCE (S256), and refresh tokens.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL