- name
- claude-code-harness-architecture
- description
- Expert guidance on AI Agent Harness architecture based on the comprehensive Claude Code analysis book
- triggers
- ["how do I build an agent harness","explain agent conversation loop architecture","show me tool permission pipeline design","how does context compression work in agents","implement agent memory system","design multi-agent coordinator pattern","set up MCP integration for agents","build fork-based sub-agent system"]
# Claude Code Harness Architecture
> Skill by [ara.so](https://ara.so) — Claude Code Skills collection.
This skill provides deep architectural knowledge from the 420,000-word book "Decoding Agent Harness" (御舆:解码 Agent Harness), a comprehensive analysis of Claude Code's internal architecture. Use this to design, build, and debug production-grade AI Agent systems.
## What This Covers
The book analyzes the complete Agent Harness architecture through 15 chapters:
- **Conversation Loop**: Async generator-based dialog control
- **Tool System**: 45+ tools, concurrent execution, safety protocols
- **Permission Pipeline**: 4-stage access control with speculative classification
- **Context Management**: 4-level progressive compression with circuit breakers
- **Memory System**: 4 types of closed-form memory, fork inheritance
- **Hook System**: 26 lifecycle events, 5 hook types
- **Sub-Agents**: Fork mode with byte-level context inheritance
- **MCP Integration**: 8 transport protocols, bridge architecture
- **Skills & Plugins**: 11 core skills, frontmatter-based loading
- **Streaming Architecture**: Performance optimization patterns
## Core Architecture Patterns
### 1. Conversation Loop (The Heartbeat)
The main loop is an async generator with 5 yield event types:
```typescript
async function* conversationLoop(
deps: QueryDeps
): AsyncGenerator<ConversationEvent> {
while (true) {
// Yield status updates
yield { type: 'thinking', phase: 'analyzing' };
// Stream LLM response
const response = await streamLLMCompletion(deps);
yield { type: 'text', content: response.delta };
// Execute tool calls
if (response.toolUse) {
yield { type: 'tool_call', tool: response.toolUse.name };
const result = await executeTool(response.toolUse, deps);
yield { type: 'tool_result', data: result };
}
// Check termination (10 types)
if (shouldTerminate(response, deps)) {
yield { type: 'done', reason: response.stopReason };
break;
}
}
}
```
**10 Termination Reasons**:
- `end_turn`: Natural completion
- `max_tokens`: Context limit reached
- `stop_sequence`: Explicit stop marker
- `tool_use`: Waiting for tool approval
- `user_interrupt`: Manual cancellation
- `error`: Execution failure
- `timeout`: Time limit exceeded
- `budget_exceeded`: Token budget exhausted
- `recursion_limit`: Max fork depth reached
- `safety_violation`: Permission denied
### 2. Tool System Architecture
**Tool Protocol** (5 elements):
```typescript
interface Tool<I = unknown, O = unknown, P = unknown> {
// 1. Schema: Zod v4 for validation
input: z.ZodType<I>;
// 2. Metadata
name: string;
description: string;
// 3. Parameters (optional context)
parameters?: P;
// 4. Execute: Core logic
execute: (input: I, params: P, deps: QueryDeps) => Promise<O>;
// 5. Attributes
readOnly: boolean;
destructive: boolean;
concurrencySafe: boolean;
}
```
**Fault-Safe Tool Factory**:
```typescript
function buildTool<I, O, P>(spec: ToolSpec<I, O, P>): Tool<I, O, P> {
return {
...spec,
execute: async (input, params, deps) => {
try {
// Validate input
const validated = spec.input.parse(input);
// Execute with timeout
const result = await Promise.race([
spec.execute(validated, params, deps),
new Promise((_, reject) =>
setTimeout(() => reject('timeout'), deps.timeout)
)
]);
return result as O;
} catch (error) {
// Return structured error, never throw
return {
success: false,
error: error.message,
recovery: spec.errorRecovery?.(error)
} as O;
}
}
};
}
```
**12 Tool Categories** (from Appendix B):
- File Operations: `read_file`, `write_file`, `search_files`
- Shell: `execute_command`, `bash_session`
- Browser: `navigate`, `screenshot`, `extract`
- Git: `commit`, `diff`, `log`
- Search: `web_search`, `codebase_search`
- Memory: `remember`, `recall`, `forget`
- Agent: `fork_agent`, `call_coordinator`
- MCP: `mcp_call_tool`, `mcp_list_resources`
- Plan: `create_plan`, `update_plan_step`
- Config: `get_setting`, `update_setting`
- Debug: `inspect_context`, `trace_tool_call`
- System: `sleep`, `notify`, `request_permission`
### 3. Permission Pipeline (4 Stages)
```typescript
type PermissionMode =
| 'auto' // No approval needed
| 'notify' // Show notification, auto-proceed
| 'confirm' // Require user approval
| 'reject' // Always deny
| 'interactive'; // Progressive disclosure
async function permissionPipeline(
toolCall: ToolCall,
deps: QueryDeps
): Promise<PermissionResult> {
// Stage 1: Rule Matching (bash-style patterns)
const mode = matchPermissionRule(toolCall.name, deps.config.permissions);
if (mode === 'auto') return { approved: true };
if (mode === 'reject') return { approved: false, reason: 'policy' };
// Stage 2: Speculative Classification (2s timeout)
const classification = await Promise.race([
classifyToolIntent(toolCall, deps),
Promise.resolve({ risk: 'unknown', confidence: 0 })
]);
if (classification.risk === 'low' && classification.confidence > 0.9) {
return { approved: true, source: 'classifier' };
}
// Stage 3: User Prompt
if (mode === 'confirm' || mode === 'interactive') {
const response = await deps.ui.promptUser({
tool: toolCall.name,
args: toolCall.arguments,
risk: classification.risk,
preview: generatePreview(toolCall)
});
return { approved: response.approved, memorize: response.remember };
}
// Stage 4: Fallback (default deny)
return { approved: false, reason: 'no_approval' };
}
```
**Rule Matching Examples**:
```yaml
permissions:
- pattern: "read_*"
mode: auto
- pattern: "write_file:/tmp/**"
mode: notify
- pattern: "execute_command:rm *"
mode: reject
- pattern: "fork_agent:**"
mode: confirm
max_depth: 3
```
### 4. Context Management (Compression Strategies)
**Effective Window Formula**:
```
EffectiveWindow = MaxContext - (SystemPrompt + Tools + Config + Memory + OutputReserve)
```
**4-Level Progressive Compression**:
```typescript
async function manageContext(deps: QueryDeps): Promise<Message[]> {
const budget = calculateBudget(deps);
let messages = deps.conversation.messages;
// Level 1: Snip (truncate old content)
if (getTokenCount(messages) > budget.warning) {
messages = snipOldMessages(messages, budget.target);
}
// Level 2: MicroCompact (compress code blocks)
if (getTokenCount(messages) > budget.warning) {
messages = await microCompact(messages, {
maxCodeLength: 500,
preserveErrors: true
});
}
// Level 3: Collapse (summarize message pairs)
if (getTokenCount(messages) > budget.critical) {
messages = await collapseMessages(messages, {
minPairAge: 10,
maxCollapse: 0.5
});
}
// Level 4: AutoCompact (LLM-based summary)
if (getTokenCount(messages) > budget.critical) {
messages = await autoCompact(messages, deps);
}
// Circuit Breaker: Hard truncate if all fails
if (getTokenCount(messages) > budget.max) {
messages = messages.slice(-budget.max);
deps.metrics.recordCircuitBreak('context_overflow');
}
return messages;
}
```
**Circuit Breaker Pattern**:
```typescript
class ContextCircuitBreaker {
private failures = 0;
private state: 'closed' | 'open' | 'half_open' = 'closed';
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
throw new Error('Circuit breaker open');
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onFailure() {
this.failures++;
if (this.failures >= 3) {
this.state = 'open';
setTimeout(() => this.state = 'half_open', 30000);
}
}
private onSuccess() {
this.failures = 0;
this.state = 'closed';
}
}
```
### 5. Memory System (4 Types)
**Closed-Form Memory** (only store non-derivable information):
```typescript
interface Memory {
type: 'fact' | 'preference' | 'context' | 'goal';
content: string;
source: 'user' | 'agent' | 'tool';
timestamp: number;
expiresAt?: number;
confidence: number;
}
class MemorySystem {
async remember(memory: Memory, deps: QueryDeps) {
// Deduplicate: Don't store if derivable
if (await this.isDerivable(memory, deps)) {
return { stored: false, reason: 'derivable' };
}
// Store in MEMORY.md
await deps.fs.appendFile('.claude/MEMORY.md',
`\n## ${memory.type} (${new Date(memory.timestamp).toISOString()})\n` +
`${memory.content}\n` +
`Source: ${memory.source} | Confidence: ${memory.confidence}\n`
);
// Update index
await this.updateIndex(memory, deps);
}
async recall(query: string, deps: QueryDeps): Promise<Memory[]> {
// Vector search in index
const results = await deps.vectorDB.search(query, {
type: ['fact', 'preference', 'context', 'goal'],
minConfidence: 0.7,
limit: 10
});
// Filter expired
return results.filter(m =>
!m.expiresAt || m.expiresAt > Date.now()
);
}
}
```
**Fork Memory Inheritance**:
```typescript
async function forkAgent(config: ForkConfig, deps: QueryDeps): Promise<Agent> {
const childMemory = {
// Inherit parent facts
facts: deps.memory.getFacts(),
// Inherit preferences (shallow copy)
preferences: { ...deps.memory.getPreferences() },
// New context scope
context: [],
// Inherit goals if specified
goals: config.inheritGoals ? deps.memory.getGoals() : []
};
return createAgent({
...config,
memory: childMemory,
parent: deps.agentId,
depth: deps.forkDepth + 1
});
}
```
### 6. Sub-Agent & Coordinator Pattern
**Fork Mode** (byte-level context inheritance):
```typescript
interface ForkConfig {
type: 'fork' | 'spawn' | 'clone';
inheritContext: boolean;
inheritMemory: boolean;
inheritTools: boolean;
inheritGoals: boolean;
maxDepth: number;
}
async function forkAgent(
config: ForkConfig,
deps: QueryDeps
): Promise<{ agentId: string; channel: MessageChannel }> {
// Recursion guard
if (deps.forkDepth >= config.maxDepth) {
throw new Error(`Max fork depth ${config.maxDepth} exceeded`);
}
// Byte-level context copy
const childContext = config.inheritContext
? structuredClone(deps.conversation)
: { messages: [] };
// Create child agent
const child = await createAgent({
...config,
context: childContext,
memory: config.inheritMemory ? cloneMemory(deps.memory) : {},
tools: config.inheritTools ? deps.tools : getDefaultTools(),
parent: deps.agentId,
depth: deps.forkDepth + 1
});
return {
agentId: child.id,
channel: child.messageChannel
};
}
```
**Coordinator Pattern** (orchestration-only):
```typescript
class CoordinatorAgent {
// Constraint: Coordinators never execute tools directly
private readonly allowedTools = [
'fork_agent',
'send_message_to_agent',
Ver no GitHub