Architecture
Harnez uses a local client/server architecture. Running harnez connects to
an existing server or starts one. From the outside, it should still feel like
one application.
┌──────────────────────┐
│ TUI Client │
└──────────┬───────────┘
│
local IPC
│
┌──────────▼───────────┐
│ Harnez Server │
│ │
│ Session Manager │
│ Agent Runtime │
│ Subagent Manager │
│ Context Manager │
│ Tool Registry │
│ Skill Registry │
│ MCP Manager │
│ Artifact Store │
│ Persistence │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ Model Runtime Layer │
└──────────────────────┘
Runtime
The server is a long-lived process implemented in TypeScript on Bun. The TUI stays thin. Expensive session and agent state lives on the server, not the client, so the interface can be killed and reattached without losing work. Each top-level request runs inside a fresh task runtime with its own capability snapshot and execution ledger.
Agent loop
A turn is a straight line: a user message goes through context construction into a model call; tool calls come back, run, and their results feed back into context for the next model call. It ends when the model responds without requesting further action.
User message → context → model call → tool calls
→ tool execution → tool results → context → model call → … → response
Three inputs steer that loop from the TUI:
- Enter modifies the active task in place. The model restarts around the new message.
- Option + Enter queues a follow-up task for after the current one finishes, without altering it.
- Esc aborts the current foreground step, including generation, running tools, and anything pending in that step.
Subagents
Subagents are isolated agents with predefined profiles (implementer,
explorer, reviewer, and similar). A subagent doesn't inherit the parent's
whole transcript. It starts from its profile, relevant skills, applicable
repository instructions, and an explicit task brief. It reports back a
compact result: status, changes, verification performed, and anything
unresolved. The parent never imports a child's raw transcript.
Context management
Harnez separates the lossless session history from the bounded working set
sent to a model. Every context item is tracked as pinned, active,
retained, or archived. System instructions remain protected; user messages
and explicit pins collapse into one bounded rolling summary only as a final
fallback. Tool output is externalized at creation. The model sees a bounded
preview and a reference it can use to recall the exact result later.
When the context budget is crossed, one deterministic cleanup pass runs: completed tool exchanges retire first, completed work archives in a fixed order, then the oldest pinned user content rolls into a summary if necessary. Capability content is charged against the same final input budget. Nothing is deleted; the full event history persists independently of what's currently in the model's context.
Tool discovery
Each task gets a capability catalog for workspace tools and model-invocable
skills. capabilities_list and capabilities_search return compact metadata;
capabilities_inspect returns one validated contract. A discovered tool can be
admitted with tools_load, while skills use skills_activate. Core workspace
tools are loaded when the task starts. See
Tool discovery for the full flow.