created
2026-03-28
tags
cli-jaw
agent-runtime
orchestration
acp
aliases
CLI-JAW Agent Spawn
agent runtime
ACP orchestration
📚 INDEX · 동기화 체크리스트 · 커맨드 · 서버 API · str_func
Agent Spawn — agent/ · orchestrator/ · cli/acp-client · goal/
CLI spawn + ACP 분기 + Pi RPC + 스트림 + 큐 + 메모리 flush + PABCD 오케스트레이션 + goal-mode autonomy
현재 기준: src/agent/ 46개 TS 파일, src/orchestrator/ 15개 파일 (+attestation.ts), src/goal/ 5개 파일 (+pause-gate.ts), src/cli/acp-client.ts
src/agent/* — Spawn & Session
File
Line count
Role
src/agent/spawn.ts
2476L
spawn/ACP/Pi RPC/stream/DB/broadcast + queue drain 핵심
src/agent/lifecycle-handler.ts
1072L
child lifecycle, fallback, retry, queue resume, goal continuation
src/agent/args.ts
465L
CLI별 신규/재개 인자 생성
src/agent/pi-runtime.ts
460L
Pi profile normalization, isolated PI_CODING_AGENT_DIR config generation, model discovery, JSONL RPC parser/spawner
src/agent/kiro-runtime.ts
386L
kiro-code plain-text stdout parser (tool lines, assistant blocks, parallel tool merge, tail-buffer flush)
src/agent/kiro-auth.ts
230L
Kiro CLI data path resolution, session ID extraction from v2 sqlite store, conversation listing
src/agent/kiro-models.ts
98L
kiro-cli chat --list-models --format json dynamic model inventory
src/agent/codex-app-client.ts
274L
Codex App stdio server client (JSON-RPC thread/turn)
src/agent/codex-app-events.ts
291L
Codex App turn/tool/message/reasoning event adapter
src/agent/cursor-runtime.ts
242L
Cursor installed model inventory + model/effort → full model-id resolver
src/agent/memory-flush-controller.ts
185L
memory flush lock + post-response trigger
src/agent/opencode-diagnostics.ts
156L
OpenCode binary/permission 점검 + raw event 버퍼
src/agent/grok-trace-backfill.ts
167L
Grok streaming-json tool_calls/tool_result backfill from trace archive
src/agent/spawn-env.ts
148L
AGY plain-text NO_COLOR=1, OpenCode/Gemini 전용 env/permission 보정
src/agent/agy-capabilities.ts
126L
AGY --help/--version capability probe + cached optional flag support map
src/agent/smoke-detector.ts
148L
smoke response 감지 + auto-continue 판단
src/agent/watchdog.ts
113L
idle/progress watchdog; progress extends deadline within 4h hard cap
src/agent/resume-classifier.ts
81L
CLI별 stale session regex
src/agent/alert-escalation.ts
86L
alert escalation event helper
src/agent/session-persistence.ts
78L
main session persistence gate
src/agent/live-run-state.ts
108L
active run snapshot / hydrate helper
src/agent/claude-e-runtime.ts
46L
jaw_runtime helper event → legacy agent:claude-e:* runtime broadcast 변환
src/agent/error-classifier.ts
52L
stderr/result 기반 에러 분류 helper
src/agent/tool-timeout.ts
33L
tool inactivity timeout helper
src/agent/agy-runtime.ts
175L
AGY timeout stdout/close-text 판별 + 최종 planner 기준 timeout suffix 정규화 + session id 추출 + intermediate planner(my_tool_call_analysis:) 최종답 차단 (#251)
src/agent/cli-helpers.ts
9L
Claude-like CLI 판별 helper
src/agent/spawn/ — Extracted Submodules
File
Line count
Role
spawn/queue.ts
373L
Message queue controller (factory pattern, fair policy, race fix)
spawn/resume.ts
84L
ACP heartbeat helper + resume bucket decision (pure functions)
spawn/process-kill.ts
22L
Recursive process tree kill via pgrep -P (no shell injection)
src/agent/events/ — Event Adapters
File
Line count
Role
events/index.ts
353L
Event dispatcher + public API (extractSessionId, extractOutputChunk, extractFromEvent)
events/helpers.ts
322L
syncLiveTools, emitAgentTool, pushTrace, buildPreview, appendAssistantRawText (plain claude text_delta), resolveSpawnOutputText
events/tool-labels.ts
343L
tool label extraction + summarizeToolInput
events/grok.ts
344L
Grok streaming-json text/thought/end/error + duplicate suppression
events/claude.ts
264L
Claude/claude-e complete-message parsing + text_delta live path + rate limit handling
events/codex.ts
96L
Codex NDJSON event adapter
events/cursor.ts
196L
Cursor stream-json event adapter
events/acp.ts
219L
ACP session/update / subagent lifecycle mapping
events/opencode.ts
196L
OpenCode event adapter
events/summary.ts
139L
logEventSummary helper
events/types.ts
—
Type-only re-export boundary
실행 중 체크 → cli/model/effort 결정 (opts → activeOverrides → perCli → default) → origin 설정
→ mainManaged / employeeSessionId / forceNew 판정
→ resume면 buildResumeArgs, 아니면 buildArgs
→ history는 `working_dir` 스코프 + legacy `NULL` row fallback으로 조회
→ employee spawn이면 tmp cwd + AGENTS.md/CLAUDE.md/GEMINI.md/CONTEXT.md + .claude/CLAUDE.md 주입
→ copilot면 ACP branch, `codex-app`면 app stdio branch, `pi`면 Pi RPC branch, `kiro-code`면 plain stdout branch, 아니면 일반 stdio branch
→ 종료 시 session 저장 / smoke auto-continue / goal continuation / fallback retry / processQueue
buildHistoryBlock(currentPrompt, workingDir)는 최근 기록을 읽고, compact marker를 만나면 요약을 넣은 뒤 중단. 누적 상한 maxTotalChars = 8000.
workingDir는 히스토리 조회, 메시지 저장, 세션 persistence, 메모리 flush의 공통 스코프.
enqueueMessage()는 queue push 직후 processQueue()를 즉시 재호출 (race fix).
processQueue()는 queueProcessing 플래그 + queueMicrotask 재드레인으로 중복 방지, source+target 첫 그룹만 처리하는 fair policy.
killProcessTree()는 execFileSync를 사용 (shell injection 방지).
~/.copilot/config.json에 model과 reasoning_effort를 동기화.
AcpClient({ model, workDir, permissions, env })를 생성, initialize() 후 resume/new session 분기.
loadSession() 성공 시 replay 모드, 실패 시 createSession() + withHistoryPrompt() fallback.
session/update는 extractFromAcpUpdate()로 변환. subagent는 tool_call + rawInput.agent_type === 'task'로 감지.
acp.shutdown() 전에 persistMainSession() 선행.
CodexAppClient는 codex app-server --listen stdio://를 띄우고 JSON-RPC 사용.
thread config: model_reasoning_summary="detailed", hide_agent_reasoning=false, show_raw_agent_reasoning=true.
codex-app-events.ts는 reasoning summaryTextDelta/textDelta/summaryPartAdded를 toolType:"thinking"으로 정규화.
kiro-cli chat --no-interactive plain stdout 표면.
Fresh run은 cli-jaw operational context + withHistoryPrompt() bounded history를 args prompt에 포함한다.
Resume run은 --resume-id <sessionId>와 현재 prompt만 전달한다. Kiro native session이 이전 대화 상태를 이미 보유하므로 operational context/history를 매 턴 다시 붙이지 않는다.
kiro-runtime.ts가 tool line(using tool:), completion marker, > assistant block + continuation lines, parallel tool merge, tail-buffer flush를 파싱해 agent_tool + agent_output으로 브로드캐스트.
Live preview는 raw delta (liveOutputText, bullet inject 없음), session/finalize용 raw capture는 fullText, authoritative body는 resolveSpawnOutputText() (parsed Kiro 우선).
동적 모델 목록은 kiro-cli chat --list-models --format json (kiro-models.ts).
Quota는 quota-kiro-reverse.ts (CodeWhisperer GetUsageLimits) 경로.
Session ID는 stdout regex 또는 v2 sqlite store에서 추출 (kiro-auth.ts).
Resume 인자는 런타임별로 분리된다. Native kiro-code는 resume turn에서 kiro-cli chat --no-interactive --resume-id <sessionId> ...를 사용한다.
ai-e의 Kiro provider branch는 ai-e kiro p ... --resume <sessionId>를 사용한다. 이 path는 native kiro-code의 --resume-id와 혼동하면 안 된다.
pi는 top-level runtime이며 ai-e provider가 아니다.
src/agent/pi-runtime.ts가 settings.pi profile을 정규화하고, JAW_HOME/pi/runtime/<profileId> 아래 models.json + settings.json을 생성한다.
실행은 pi --mode rpc --no-session --no-context-files --provider <profileId> --model <model> --api-key <key> 형태다.
binary resolution은 PI_CODING_AGENT_BIN → PATH pi → npm exec --yes --package @earendil-works/pi-coding-agent pi -- 순서이며, npm fallback은 shell string이 아니라 command/baseArgs tuple로 spawn된다.
RPC stdin은 get_state, optional set_thinking_level, prompt JSONL 명령을 보내고, stdout JSONL 이벤트에서 message_update, tool_execution_*, agent_end를 파싱해 agent_output/agent_tool/completion으로 매핑한다.
이 구현은 per-run RPC child다. agent_end 뒤 stdin을 닫고 grace window 이후 남은 child를 종료한다. persistent Pi RPC daemon은 후속 과제다.
CLI
표면
특이사항
pi
pi --mode rpc
isolated PI_CODING_AGENT_DIR, profile/model from settings.pi, npm-exec fallback
claude
stdin에 withHistoryPrompt() + stream-json
text_delta → appendAssistantRawText → live agent_output; claudeStreamedText prevents duplicate on complete assistant
claude-e
claude-e run --jsonl --output-format stream-json --idle-timeout-ms 600000 --hard-timeout-ms 3600000
jaw_runtime 이벤트 가로채기, resume --resume <sessionId>
agy
agy -p <prompt> [--model <id>] --print-timeout 10m --log-file <tmp>
plain text stdout; optional flags are emitted only when agy-capabilities.ts detects support (--model observed in AGY 1.0.12); session id from stdout/log; resume --conversation <id>
cursor
cursor-agent -p --trust --output-format stream-json --model <resolvedModelId>
effort는 full model id로 해석, runtimeModel session bucket
kiro-code
kiro-cli chat --no-interactive [--resume-id <id>]
fresh: operational context + withHistoryPrompt(); resume: current prompt only
codex
stdin에 [User Message] 블록 (fresh only)
—
grok
-p, optional -m, --output-format streaming-json, --no-alt-screen
trace backfill on exit, ai-e alias
opencode
diagnostics + raw event buffer
—
Session persistence / resume classifier
persistMainSession()는 forceNew, employeeSessionId, !sessionId, isFallback, 비정상 exit를 모두 차단.
저장: cli, sessionId, model, permissions, workingDir, effort.
shouldInvalidateResumeSession()는 code === 0이면 무조건 false, 실패 시 generic + CLI별 matcher 검사.
Resume 무효화: claude, claude-e, agy, codex, cursor, grok, opencode, copilot, kiro-code 각각 분기.
AGY guarded native resume (#261): 기본은 native resume OFF(DB history 유지). perCli.agy.nativeResume: "guarded" opt-in 시 canGuardedAgyResume()(src/agent/spawn/resume.ts) 전 가드 통과에서만 --conversation 재개 — capability probe, TTL 72h, model+cwd identity, session_buckets.last_run_clean=1(plannerOnly/checkpointSeen false), fresh-bootstrap 아님. stale conversation 출력 감지 시 bucket clear 후 fresh 경로로 1회 재시도. replay stripping은 무조건 유지.
src/shared/tool-log-sanitize.ts가 live/persisted tool UI의 공유 cap/truncate boundary:
Surface
Sanitization path
WS agent_tool
core/bus.ts → sanitizeToolLogEntry()
WS agent_done.toolLog
core/bus.ts → sanitizeToolLogForDurableStorage()
/api/orchestrate/snapshot.activeRun.toolLog
routes/orchestrate.ts → getSafeLiveRun()
Retry and trace redaction boundary
lifecycle-handler.ts uses _retryAttempt rather than a boolean retry flag.
Main 429 retries use exponential backoff up to MAIN_MAX_RETRIES = 3; employee
429/Claude-rate-limit/transient-startup retries use a shorter exponential
backoff up to EMP_MAX_RETRIES = 2. Public agent_retry events include
attempt/max retry metadata so the UI can distinguish first retry from exhausted
attempts.
src/trace/redact.ts is the trace-storage redaction boundary for structured
trace values and previews. It redacts AWS AKIA... keys, Anthropic
sk-ant-* keys, JWT-like tokens, and secret-looking object keys before values
are persisted or displayed through trace helpers.
src/cli/acp-client.ts — Copilot ACP JSON-RPC Client (382L)
Method
Role
buildSpawnArgs()
--acp + model + permission flags
spawn()
copilot --acp 생성 + stdout NDJSON 파싱
request()
JSON-RPC request/response
requestWithActivityTimeout()
idle + absolute 이중 타임아웃
_handleLine()
response / notification / agent request 분기
initialize()
protocolVersion 1 handshake
createSession()
session/new
loadSession()
session/load
prompt()
session/prompt + activity timeout
shutdown()
shutdown 후 kill
permissions === 'auto' 또는 'yolo'이면 --allow-all-tools --allow-all-paths --allow-all-urls.
prompt() activity timeout: idle 1200000ms, max 14400000ms.
session/request_permission은 항상 자동 승인.
src/goal/* — Goal-Mode Autonomy (5 files)
File
Line count
Role
goal/store.ts
158L
active goal CRUD, checkpoint, history, completion evidence gate
goal/heartbeat.ts
92L
goal-aware heartbeat continuation builder (stale detection, worker/orc state check)
goal/pause-gate.ts
26L
derived armed gate: active + agentPauseCount >= 1 → pause_gate_pending
goal/runtime.ts
55L
goal runtime helpers
goal/types.ts
42L
GoalState, GoalHistory, GoalCheckpoint, GoalBudget types
lifecycle-handler.ts는 agent 종료 후 active goal이 있으면 buildGoalContinuation()으로 자동 재스폰 판단.
Pause gate (P0 2026-06-27): armed gate (describeGoalPauseGate())이면 buildGoalContinuation() returns shouldContinue: true with reason: "pause_gate_pending" for one audit/finalizer turn; if a goal-continuation turn exits with the gate still armed, goal_pause_gate_pending is broadcast and further automatic continuation is not scheduled. agentPauseCount는 productive goal events에서만 reset — assistant text alone does not clear gate.
completeGoal()은 goalHasCompletionEvidence()가 true일 때만 goal을 완료 처리 (verification evidence gate).
Goal continuation은 GOAL_CONT_MAX_ATTEMPTS = 20 회 제한, goal ID 변경 시 카운터 리셋.
src/orchestrator/* — PABCD Orchestration (18 files)
File
Line count
Role
orchestrator/pipeline.ts
657L
PABCD sole entry point + interview first-turn init + <interview_tracker> 추출
orchestrator/state-machine.ts
692L
IPABCD state + prompts + interview tracker → OrcContext + audit/verification verdict + attestation gate
orchestrator/attestation.ts
178L
--attest / <phase_attestation> parse + checkAttestationGate() (narrative did, C→D checkOutput)
orchestrator/distribute.ts
615L
employee dispatch + parallel safety
orchestrator/worker-registry.ts
241L
worker ownership + replay registry + sanitized progress snapshots
orchestrator/gateway.ts
155L
queue / intent gateway
orchestrator/parser.ts
176L
legacy subtask JSON 파서 + intent matcher + numeric reference + verdict 파서
orchestrator/seed.ts
107L
ontology schema + seed metadata for interview-driven planning
orchestrator/workspace-context.ts
136L
task에서 repo path hint 추출, project root resolve
orchestrator/friction.ts
76L
friction signature ledger + retry/escalate/stop verdict
orchestrator/collect.ts
66L
orchestrate 결과 수집
orchestrator/worker-progress.ts
58L
worker progress safe-summary sanitizer + snapshot types
orchestrator/worker-monitor.ts
58L
stall/disconnect/timeout monitor
orchestrator/sanitize.ts
52L
stripInterviewTracker() — interview tracker/perspective tags from visible text
orchestrator/scope.ts
17L
scope stub — 항상 'default' 반환
orchestrate(prompt, meta)
├─ pending worker replay drain
├─ scope resolution (`workingDir` 포함) + current state read
├─ PABCD entry는 explicit only
├─ 첫 planning turn이면 `getStatePrompt('P')` + 원본 요청 조합
├─ prompt prefix injection
├─ `buildMemoryInjection()`에서 boss snapshot 추출
├─ spawnAgent(prompt, { origin, _skipInsert, memorySnapshot })
└─ result broadcast + worklog/state update
PABCD 진입은 명시적 (/orchestrate, /pabcd, LLM tool call).
state === 'P'이고 ctx.plan이 비어 있으면 resolveNumericReference()로 사용자 지시를 직전 numbered list에서 치환.
buildApprovedPlanPromptBlock()이 A/B/C 상태에서 ctx.plan을 prompt 최상단에 붙임.
orchestrateContinue()는 active PABCD면 continue prompt, IDLE이면 worklog-based resume.
orchestrateReset()는 agent/worker/queue/registry/employee session/state/worklog 모두 reset.
OrcStateName: IDLE | P | A | B | C | D.
OrcContext: auditStatus, verificationStatus, userApproved, worklogPath, planHash, planUpdatedAt, taskAnchor, resolvedSelection, interview(request/round/known/unknown).
Interview(I): 라운드당 1~3개 질문, <interview_tracker> 블록을 정규식 파싱 → ctx.interview 갱신 → orc_state WS payload로 Web UI 전달.
Worker registry / monitor
claimWorker() → running, finishWorker() → done + pendingReplay=true.
worker-progress.ts로 thinking/detail 제거한 안전 요약만 노출.
startWorkerMonitor(): stallThresholdMs: 120_000, maxDurationMs: 600_000.
Employee resume-session recovery
Persisted employee resume sessions are targeted by employeeSessionId; fresh
retry paths must not become Boss main-managed runs.
If an employee resume exits before SessionStart, lifecycle handling clears
only that employee's stored session row and retries once with _skipResume.
spawn.ts treats _skipResume as disabling employee employeeSessionId
reuse, and mainManaged stays false whenever opts.agentId is present.
Generic employee transient backoff remains for non-resume failures, but is
suppressed after the one-shot fresh-session retry.
Friction ledger (friction.ts)
recordFriction(tool, error) → normalized error fingerprint → count 기반 verdict (retry/escalate/stop).
같은 tool+error 패턴이 반복되면 자동 escalate/stop.
Seed / Ontology (seed.ts)
Interview 결과에서 ontology schema (entities, relationships, invariants)와 seed metadata를 구조화.
Planning phase에서 interview evidence를 structured plan input으로 변환.
prompt/builder.ts — System Prompt & Skills (946L)
Function
Role
loadActiveSkills()
활성 스킬 로드
loadSkillRegistry()
skills_ref registry 로드
getMergedSkills()
active + ref 병합
initPromptFiles()
A-1/A-2/HEARTBEAT 프롬프트 초기화
getSystemPrompt()
메인 시스템 프롬프트 구성
loadRecentMemories()
flush memory 로드
getEmployeePrompt(emp)
경량 실행자 프롬프트
getEmployeePromptV2(emp, role, phase)
v2 employee prompt (phase/role-aware skill)
regenerateB()
B 프롬프트 + workspace AGENTS.md 재생성
promptCache 키: empId/name:cli:role:phase:workingDir:mutability:scope:taskTags:mcpHash. CLI별 employee prompt, mutable/read-only override, scope, task tag attachment, MCP tool summary changes가 모두 cache invalidation에 들어간다.
Boss prompt는 cli-jaw dispatch --agent ... --task ... 경로만 설명.
Employee prompt는 CLI 자체 sub-agent(Task/Agent tool)를 내부 병렬화 용도로 허용.
Memory Flush Controller (memory-flush-controller.ts, 184L)
Export
Role
incrementMemoryFlush()
카운터 증가
resetMemoryFlushCounter()
카운터 리셋 + cycle 증가
triggerMemoryFlush()
flush lock + spawnAgent로 memory flush 실행
setSpawnRef(fn, procs)
circular import 회피용 forward reference 설정
_flushLock으로 동시 flush 방지.
_lastFlushedMessageId로 중복 flush 방지.
lifecycle-handler.ts가 agent 종료 후 memoryFlushCounter 기준으로 flush 트리거.
Dashboard home resolution으로 flush 결과를 dashboard notes에도 반영 가능.
Lifecycle Handler 주요 책임 (1072L)
관심사
구현
Session persistence
persistMainSession() 호출 + bucket 저장
Smoke auto-continue
buildContinuationPrompt() → 같은 엔진 재스폰
Goal continuation
active goal + completion evidence 미충족 시 buildGoalContinuation() → 재스폰 (max 20회)
Fallback retry
error classification → retry with different model/effort
Grok trace backfill
정상 종료 후 grok trace --local --json 호출
Memory flush trigger
memoryFlushCounter 기준 triggerMemoryFlush()
Queue resume
processQueue() 호출
Interview tracker strip
stripInterviewTracker() from broadcast text
Tool-log sanitize
sanitizeToolLogForDurableStorage() before DB insert
Trace finalization
finalizeTraceRun() + linkTraceRunToMessage()
Kiro output resolution
resolveSpawnOutputText() (parsed Kiro body 우선)