HarnessAgent is a thin wrapper around ReActAgent that packages the engineering capabilities long-running agents need — workspace-driven persona, long-term memory, subagent orchestration, sandbox isolation, skill composition, plan mode, channel routing — into a single builder.
A bare ReActAgent only handles “one request → reason → tool → reply”. Harness answers a different set of questions: how does the next turn pick up where the last left off, how does context stay bounded, how do users stay isolated, how do dangerous actions get reviewed, how do reusable capabilities accumulate.
Installation, dependency, and an end-to-end “first HarnessAgent” walkthrough live in Quickstart. This page is architecture only.
Core working principle
Three things to keep in mind: 1. Capabilities layer onto the reasoning loop, not into it. Workspace injection, compaction, subagents, sandbox, Plan Mode — each hooks into key moments of the ReAct loop. The core algorithm is untouched; Harness only adds. 2. Capabilities don’t depend on each other; they share three objects. Each capability does one job and is unaware of the others. They cooperate through:RuntimeContext— who is speaking in this call:sessionId,userId, plus arbitrary extras. Not persisted.- The workspace — who reads and writes which files. Where they physically land (local disk, sandbox, KV store) is a configuration choice.
AgentStateStore— how runtime state is restored across calls.
.middleware(...) runs before Harness’s built-ins.
Core components
Each capability answers one problem; opt in on the builder.How state flows
Three layers exist; the framework moves data between them automatically.- In-call state —
AgentState(conversation context, permission rules, Plan Mode state, tool state) plusRuntimeContext(sessionId,userId, sandbox handle, extras). - Cross-call state — auto-saved at the end of every
call()and auto-loaded on the next: theAgentStateruntime snapshot in the configuredAgentStateStore(default~/.agentscope/state/<agentId>/, addressed by(userId, sessionId)), the never-compacted full conversation log undersessions/<sessionId>.log.jsonl, subtask records, and sandbox metadata. - Long-term memory — accumulated across sessions:
memory/YYYY-MM-DD.mdis append-only, periodically merged intoMEMORY.mdby a throttled background job;MEMORY.mdis injected into the system prompt every reasoning step.
- The system prompt is rebuilt every reasoning step, so edits to
AGENTS.mdorMEMORY.mdtake effect immediately — no restart. - Compaction, memory distillation, and background maintenance are throttled; they don’t run every turn.
AgentStateis persisted by core’sReActAgent+AgentStateStore. Harness no longer adds its own persistence hook.
Adding your own middleware
To insert custom behaviour without bypassing Harness’s plumbing:- Use
.middleware(...)— your middleware runs before all Harness built-ins. - Read
RuntimeContextfrom the agent for the current call’s identity (userId/sessionId). - For workspace I/O, go through
harnessAgent.getWorkspaceManager()— it routes correctly under sandbox or remote-store modes.java.nio.Fileswrites to the host disk and will land in the wrong place outside local mode.
Related pages
- Workspace — directory layout, what gets injected into the system prompt,
tools.json - Context & AgentState —
AgentState,RuntimeContext,AgentStateStorepersistence, multi-user isolation - Memory — two-layer memory
- Compaction — summary compaction, large-result offloading, overflow recovery
- Filesystem — local + shell / shared store / sandbox
- Sandbox — isolated execution, cross-call recovery, distributed
- Subagent — declarations, sync/background, streaming forwarding
- Skill — four-layer composition, self-learning loop
- Plan Mode — read-only phase + HITL exit
- Channel — session management, multi-agent routing, streaming SSE