한 줄 요약: 하네스 엔지니어링 = "모델 호출"이라는 단일 함수를 둘러싼 6개 레이어(Transport / Loop / Context / Tool / Permission / Extension)를 명시적으로 분리하고, 각 레이어를 검증된 OSS 패턴 중 하나로 구현한 뒤, 레이어 간 결합을 최소화하는 것.
| 층위 | 의미 | 본 레포에서의 위치 |
|---|---|---|
| 빌드 하네스 | 소스 → 실행 가능한 바이너리/번들로 만드는 스캐폴딩 | scripts/build.mjs, stubs/, vendor/ |
| 런타임 하네스 | 모델 호출을 둘러싸고 루프·툴·권한·컨텍스트를 조립하는 스캐폴딩 | src/query.ts, src/Tool.ts, src/services/tools/*, src/utils/permissions/*, src/utils/hooks.ts, src/services/compact/* 등 |
이 문서는 런타임 하네스가 중심입니다(자체 에이전트를 만들 때 가장 큰 가치가 거기 있기 때문).
async function*로 구현. 모든 진행 상태(stream_request_start, 메시지, 툴 결과, 에러)를 yield로 흘려보냄.src/query.ts:219 query(), src/query.ts:241 queryLoop(), while(true) 메인 루프 :307Promise<Result>가 아니라 AsyncIterable<AgentEvent>로 정의. CLI/HTTP/SSE/IDE 어댑터가 이 한 인터페이스만 소비.src/query.ts:1050 collapse_drain, :1119 reactive_compact, :1188 max_output_tokens_recovery (최대 3회), :1267 stop hook blockinghasAttemptedReactiveCompact)로 무한 루프 차단.| 에러 | 1단계 복구 | 2단계 복구 | 3단계 |
|---|---|---|---|
| Prompt too long (413) | reactive compact (요약) | collapse drain (탐욕적 재평가) | yield error + exit |
| Max output tokens | escalate 8k→64k | recovery message 주입 + 재시도 (최대 3회) | yield error + exit |
| Stream fallback | tombstone orphaned + executor reset | — | — |
| Stop hook block | user message 주입 + continue | — | — |
if (err === 'PROMPT_TOO_LONG' && !state.hasAttemptedReactiveCompact) {
state = {
...state,
messages: compact(state.messages),
hasAttemptedReactiveCompact: true,
transition: { reason: 'reactive_compact_retry' }
}
continue
}try/catch가 아니라 유한 상태 머신의 전이로 다루기. 각 복구 단계마다 상태 가드를 두어 한 번만 시도되게.isConcurrencySafe, isReadOnly, isDestructive)를 직접 선언. 오케스트레이터는 이 선언만 보고 배치를 분할.src/Tool.ts:362+, src/services/tools/toolOrchestration.ts:91 partitionToolCalls(), src/services/tools/StreamingToolExecutor.ts:40CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY (기본 10)concurrency: 'safe' | 'serial' | 'destructive' 필드를 강제. 오케스트레이터는 이 필드만 보고 자동 분할.src/utils/permissions/permissions.ts:200 hasPermissionsToUseTool(), src/hooks/useCanUseTool.tsx:27, src/utils/permissions/yoloClassifier.tsallow | deny | next를 반환. ML 분류기는 옵션(피처 플래그).src/utils/systemPrompt.ts:41-123 (override → coordinator → agent → custom → default → appendSystemPrompt)--system-prompt), 서브에이전트 프롬프트, 모드별 프롬프트가 충돌 없이 합성됨.Section[] 배열로 두고, 각 섹션이 priority, mode, compute()를 갖게 함. 조립 함수는 그저 정렬 + concat.src/constants/systemPromptSections.ts — systemPromptSection() (정적, /clear까지 메모이즈) vs DANGEROUS_uncachedSystemPromptSection() (호출 때마다)cacheable: boolean 플래그를 강제. 동적 섹션은 함수명에 Dangerous 같은 접두사로 경고.src/services/compact/autoCompact.ts, src/query/tokenBudget.ts (BudgetTracker로 추세 감지: 3턴 연속 <500 토큰 델타 → diminishing returns)Strategy[] 체인으로. 각 전략의 비용/복원율을 메타데이터로.src/utils/forkedAgent.ts:57-81 CacheSafeParams, src/tools/AgentTool/forkSubagent.ts, src/tools/AgentTool/runAgent.tsCacheSafeParams가 부모/자식의 호모그래프를 검증.(SharedCacheParams, IsolatedState) 페어로 구조화. 자식이 cache prefix를 깨지 않도록 강제./execute_action)로 분리되어 controller와 실제 실행 환경이 네트워크로 격리.state_command로 매 턴 현재 상태(열린 파일, cwd, 커서) JSON으로 동적 주입LocalPythonExecutor가 AST를 한 연산씩 인터프리트, import allowlist, 연산 수 상한. 더 강한 격리로 E2B/Modal/Docker/Wasm을 옵션 제공.interrupt()로 human-in-the-loop가 1급 기능.sandbox-exec), Linux Landlock, Windows restricted-token. 세 정책 축(filesystem, network, exec)을 분리.@codebase, @file, @docs, @diff, @terminal, @problems, @url 등 사용자가 명시적으로 컨텍스트를 선택하는 @-mention. 각 provider는 IContextProvider 인터페이스를 구현. Hub로 패키지 공유.security-reviewer 모드는 *.md에만 쓰기).```shell, ```ipython, ```save path/to/file.py 같은 언어 태그가 곧 툴 식별자. 가장 낮은 프로토콜 오버헤드. 약한 모델에도 이식 가능.주된 사용 시나리오는?
├─ 사용자가 매 턴 게이트키퍼 → REPL + reflection (Aider 스타일)
├─ 자율적으로 여러 턴 진행 → Recursive chat loop (Cline) 또는
│ AsyncGenerator loop (Claude Code)
├─ 멀티 클라이언트/원격 실행 → Event stream (OpenHands)
├─ 분기/체크포인트/감사 필요 → State machine (LangGraph)
└─ 단순 함수 호출만 → Submission/Event 큐 (Codex)
지원할 모델은?
├─ 최신 폐쇄형(Claude/GPT-4)만 → Native function calling
├─ 약한 모델/로컬 모델 포함
│ ├─ 편집 위주 → SEARCH/REPLACE (Aider)
│ ├─ 범용 → XML 태그 (Cline)
│ └─ 단순함 추구 → 마크다운 코드블록 (gptme)
└─ 다단계 데이터 변환 위주 → Code-as-action (smolagents)
실행 환경은?
├─ 사용자 로컬 머신 (개발자 도구)
│ ├─ 기본 신뢰 → per-action 승인 + 화이트리스트 (Cline)
│ ├─ 엄격한 격리 → OS 샌드박스 (Codex CLI)
│ └─ 단순함 추구 → git 자동 커밋 = 롤백 (Aider)
├─ 서버/CI → 컨테이너 격리 (OpenHands, smolagents)
└─ 하이브리드 → 4단계 funnel (Claude Code)
코드베이스 크기는?
├─ 작음 (<10K LOC) → 대화 로그 단순 누적 (gptme)
├─ 중간 (<100K LOC) → Repo map (Aider)
├─ 큼 → Embedding RAG (Continue) 또는
│ Repo map + 명시적 @-mention
└─ 매우 큼 + 여러 세션 → Event stream + condenser (OpenHands)
토큰 한계 접근 시?
├─ 단순 → Quarter compression (Cline)
└─ 정교 → Graduated compaction + circuit breaker (Claude Code)
┌─────────────────────────────────────────────────────────┐
│ L6. Extension Layer MCP, hooks, plugins, modes │
├─────────────────────────────────────────────────────────┤
│ L5. Permission Layer 4-stage funnel + sandbox │
├─────────────────────────────────────────────────────────┤
│ L4. Tool Layer self-declared concurrency │
├─────────────────────────────────────────────────────────┤
│ L3. Context Layer prompt slots + compaction │
├─────────────────────────────────────────────────────────┤
│ L2. Loop Layer AsyncGen + layered recovery │
├─────────────────────────────────────────────────────────┤
│ L1. Transport Layer streaming model client │
└─────────────────────────────────────────────────────────┘
| 레이어 | 최소 구성 | 출처 | 가치 |
|---|---|---|---|
| L1 Transport | 스트리밍 + 취소 가능 model client (한 인터페이스) | 모든 OSS 공통 | 모델 교체 자유 |
| L2 Loop | async function* + 4단 복구 사다리 + transition 메타 | Claude Code | 디버그/테스트 용이 |
| L3 Context | 우선순위 슬롯 + 캐시 가능/불가능 분리 + graduated compaction | Claude Code + SWE-agent ACI | 비용 + 정확도 동시 |
| L4 Tool | Tool 인터페이스에 concurrency, readOnly, destructive 강제 + edit-format fallback chain | Claude Code + Aider | 안전성 + 확장성 |
| L5 Permission | allowRules → hooks → classifier → UI filter chain + approval cache | Claude Code + Codex | 사용자 피로 ↓ |
| L6 Extension | MCP 클라이언트 내장 + lifecycle hooks (Pre/PostToolUse 등) + mode 추상화 | Claude Code + Cline/Roo + Continue | 사용자 정의 ↑ |
async function* 메인 루프 + AgentEvent 타입 정의name, inputSchema, call, concurrency, readOnly)✅ 이 시점에 단일 턴 + 단일 툴 호출이 동작.
hasAttemptedXxx)partitionToolCalls() (read-only 병렬, write 직렬)✅ 이 시점에 멀티턴 + 에러 복구가 동작.
cacheable: boolean 플래그 강제✅ 이 시점에 정확도가 큰 폭 상승.
✅ 이 시점에 사용자 정의 워크플로우 가능.
EnterWorktreeTool 패턴)새로운 기능을 하네스에 추가할 때마다 이 질문들을 통과시키세요.
| 빌드 하네스 패턴 | Claude Code 위치 | 일반화된 전략 |
|---|---|---|
| 컴파일 타임 인트린식 stub | stubs/bun-bundle.ts | 빌드 도구 종속을 1점에 격리 |
| 소스 사본 후 변환 | build.mjs Phase 1+2 | 원본 read-only 보존 |
| 반복 stub 생성 | build.mjs Phase 4 (5라운드) | 누락 의존성을 fail-loop로 채움 |
| MACRO 주입 2가지 방식 | transform.mjs (--define) vs build.mjs (replaceAll) | 빌더가 다르면 같은 결과를 다른 방식으로 |
| 네이티브 모듈 vendor stub | vendor/*-src/, bun-ffi.ts | 플랫폼 의존을 stub으로 우회 |
6 레이어 모델을 빌드 하네스에 매핑하면: L1=빌더(esbuild), L2=빌드 스크립트(반복 루프), L3=stub 디렉토리, L4=transform 규칙, L5=라이선스/보안 필터, L6=커스텀 빌드 hooks.
가장 큰 ROI는 L2(layered recovery) + L3(ACI 인터페이스 + graduated compaction) + L4(self-declared concurrency + edit-format fallback) 세 곳입니다. 이 셋만 잘 만들어도 다른 OSS 코딩 에이전트와 동급의 안정성/정확도가 나옵니다.