멀티 Agent는 지속해야 하는 그래프
내보내는 것은 노드이고, 변은 태어날 때부터 Open입니다. 편지는 먼저 큐에 들어가고, followup이 깨웁니다. 끄는 것은 변이고, 역사는 남아요.
send_message는 큐에만 넣고 followup_task가 깨우며, 종료는 런타임을 내리고 변을 닫아야 그래프에서 이름이 지워집니다. 내일도 말을 이어갈 수 있는지는 변을 먼저 보세요.
thread_spawn_edges 기록이 없습니다.- 다음 층 깊이를 계산하고 ThreadSpawn을 씀registry.rs L87
- task_name으로 절대 경로 /root/explore_auth를 만듦multi_agents_common.rs L117
- 과학자 명단에서 별명 Hypatia를 뽑음control/spawn.rs L32
- 임시가 아닌 세션은 바로 Open 변을 upsertcontrol.rs L776
- send_message는 QueueOnly로 싸고 큐에만 넣음message_tool.rs L103
- 우편함은 세션급 큐, 넣은 뒤 Mailbox 활동을 냄input_queue.rs L127
- followup_task가 trigger_turn을 켜야 깨움handlers.rs L98
- 자식 turn이 끝나면 부모에게 Result를 보내고 깨우지 않음session/mod.rs L1977
- 종료는 변을 안 바꿈. 변 닫기는 대상 자신의 입변만 Closedlegacy.rs L6
- 재시작은 Open 후손의 신원만 레지스트리에 다시 넣음control/spawn.rs L158
루트 세션이 탐색자를 보내 auth 모듈을 보게 합니다. 모델이 spawn_agent를 호출하면 도구가 작업 이름 /root/explore_auth와 별명 Hypatia를 돌려줘요. 몇 분 뒤 wait_agent를 누르면 우편함에 FINAL_ANSWER가 누워 있습니다.
다음 날 같은 thread를 엽니다. 자식 세션의 런타임은 이미 내려가 있어요. 시스템이 어제 호출이 한 번 있었다는 것만 기억하면 이 경로를 찾지 못합니다. send_message를 다시 내면 제어면이 live agent path not found를 신고해요.
Codex는 부모–자식을 방향 변으로 만듭니다. 변은 값이 둘뿐이에요. Open은 열린 spawned agent로 복구할 수 있다는 뜻입니다. Closed는 그래프 시선에서 이미 닫혔다는 뜻입니다. 직렬화는 open과 closed예요.
출처:codex-rs/agent-graph-store/src/types.rs 4–12행
그래프는 SQLite의 thread_spawn_edges 표에 있습니다. child_thread_id가 기본 키라 아이 하나는 부모 둘에 걸릴 수 없어요. 같은 아이를 다시 spawn하면 부모와 상태가 새 값으로 덮입니다. 세션 본문은 이 표에 안 들어갑니다. 자식 agent의 모델 컨텍스트는 자기 rollout을 걸어요. 그래프는 누가 누구를 낳았는지, 이 변이 지금 열려 있는지 닫혀 있는지만 답합니다.
출처:codex-rs/state/migrations/0021_thread_spawn_edges.sql 1–8행
임시가 아닌 세션은 스레드가 만들어진 뒤 바로 Open 변을 upsert합니다. 쓰기 실패는 warn만 쳐요. 자식 thread는 이미 돌고 있고, 그래프는 나중에 메울 수 있습니다. 메움은 ON CONFLICT DO NOTHING이라 이미 Closed인 변을 되돌리지 않습니다.
출처:codex-rs/core/src/agent/control.rs 767–780행
후손을 나열할 때 필터는 걷는 모든 변에 작용합니다. Some(Open)은 Open만 따라가요. 부모 변이 이미 Closed인 서브트리는 손자 변이 아직 Open이어도 나열되지 않습니다.
출처:codex-rs/agent-graph-store/src/store.rs 49–54행
경로가 주소 키이고, 별명은 사람이 알아보는 이름입니다. 루트는 /root예요. 상대 이름은 현재 경로 뒤에 붙습니다. ..와 .는 거절되므로 상대 경로로 형제에게 기어갈 수 없어요. 역할은 능력을 줄일 수 있지만 부모 세션의 권위를 대신할 수는 없습니다. 내장 살아 있는 역할은 default, explorer, worker입니다. explorer.toml은 빈 파일이에요.
출처:codex-rs/core/src/agent/role.rs 1–4행
재시작 뒤에는 물을 수 있어야 합니다. 이 아이가 아직 살아 있나. spawn 사건 한 번만 기억해서는 답할 수 없어요. Open만 복구 목록에 들어갑니다. Closed는 서브트리 순회에서 사라집니다. child_id를 기본 키로 두면 그래프는 나무로 남고, 순회는 깊이 BFS를 할 수 있어요. 다른 언어로 다시 써도 최소 형태는 여전히 세 열짜리 표입니다. parent, child, status.
자식 agent가 끝나면 FINAL_ANSWER를 돌려보내야 합니다. 이 편지가 깨움을 스스로 달고 있으면, 부모가 사용자가 보는 최종 답을 쓰는 중에 자식 결과가 억지로 한 턴을 엽니다. 사용자는 말의 반쪽을 보고, 갑자기 끼어든 완료 알림을 한 장 더 봐요.
통신 종류에는 태그 네 개입니다. Spawn, Message, Followup, Result. OTEL이 쓰는 이름이에요. 프로토콜 쪽에는 InterAgentCommunication 하나뿐이고, trigger_turn으로 깨울지를 가릅니다.
send_message는 QueueOnly라 큐에만 넣습니다. followup_task는 TriggerTurn이라 그때 깨워요. 빈 메시지는 바로 거절합니다. followup_task는 루트 노드를 칠 수 없습니다.
출처:codex-rs/core/src/tools/handlers/multi_agents_v2/message_tool.rs 11–24행
처리 함수는 먼저 큐에 넣고, 일을 시작할지 나중에 정합니다. trigger_turn이 거짓이면 편지는 계속 누워 있어요. 참이거나 세션에 끝나지 않은 durable sleep이 있을 때만 일을 시작합니다. V2의 완료 알림은 Result이고 trigger_turn은 거짓입니다. 부모가 말하는 중이면 이 편지는 우편함 위상으로 줄을 서요.
출처:codex-rs/core/src/session/handlers.rs 89–99행
우편함은 세션급 큐입니다. 사용자 끼어들기는 pending_input으로, 자식 메일은 mailbox_pending_mails로 — 칸이 둘이에요. 형제는 절대 경로, 예를 들어 /root/worker_b만 쓰면 서로 보낼 수 있습니다. 상대 이름 worker_b는 자기 뒤에 붙어 자기 아이가 됩니다.
출처:codex-rs/core/src/session/input_queue.rs 76–80행
깨움권은 드뭅니다. 한 턴을 열 수 있는 자는 함부로 열면 안 돼요. 배달과 일 시작을 나누면, 완료 알림은 기본으로 깨우지 않고 깨움권은 followup_task와 사용자에게 남습니다. 다른 메시지 버스에도 이 불리언이 씁니다. wake인가, 큐에만 넣는가.
V2 상주 자리가 가득 차면 LRU로 아이 하나를 내립니다. 런타임을 내리며 변까지 Closed로 찍으면 그 아이는 Open 서브트리에서 사라져요. 다음 복구가 찾지 못합니다. 사용자가 닫은 적이 없는데 시스템이 이름을 지운 것입니다.
shutdown_live_agent는 살아 있는 agent를 끄고, rollout을 쓸며, Shutdown을 보내고, 관리자에서 thread를 떼어냅니다. 변은 여전히 Open이에요. 다음 복구도 살아 있는 서브트리 구성원으로 봅니다.
출처:codex-rs/core/src/agent/control/legacy.rs 6–8행
close_agent는 먼저 대상 자신의 입변을 Closed로 찍고 종료합니다. 후손의 변은 여기서 Closed로 찍히지 않아요. 부모 turn이 정상 끝나면 TurnComplete를 걷고 close_agent를 호출하지 않습니다. 아이는 계속 돌고, 변은 Open을 유지해요.
출처:codex-rs/core/src/agent/control/legacy.rs 48–58행
V2 복구는 두 걸음입니다. 먼저 Open 후손의 신원을 레지스트리에 넣고 런타임은 다시 열지 않아요. 누군가 정말 send_message나 followup_task를 할 때서야 rollout으로 thread를 다시 겁니다.
출처:codex-rs/core/src/agent/control/spawn.rs 144–162행
런타임이 살아 있는 것과 그래프에서 이름이 지워지는 것은 독립된 일입니다. 상주 LRU, 프로세스 재시작, 사용자가 창을 닫는 것 — 모두 런타임을 내릴 수 있어요. 오케스트레이터가 분명히 close해야 Closed로 씁니다. 복구는 Open 변을 따라갑니다. 이 분개는 Rust에 의존하지 않아요.
DSH: 이음새가 먼저, 그래프는 나열 결과
DSH는 자식 agent를 갈아 끼울 수 있는 provider 이음새로 만듭니다. SubagentProvider는 name, capabilities, inheritsParentContext, start를 달고 있어요. 프로세스 안 fork, Claude Code, Codex, ACP — 같은 인터페이스 위의 다른 구현입니다.
아이와 후손도 나열할 수 있고, 살아 있는 session store와 선택적 persistence에서 읽기 전용으로 열거합니다. thread_spawn_edges 같은 Open / Closed 변 표는 없어요. 토폴로지는 session header의 origin: subagent에 사후에 접어 낸 것입니다. 구현을 바꾸는 것은 싸고, 변으로 복구하는 일은 따로 해야 합니다.
Claude Code: 도구 호출에 transcript 곁사슬
모델이 보는 도구의 지금 이름은 Agent입니다. 옛 선 이름은 여전히 Task라 권한 규칙, hook, 복구 중인 세션과 호환돼요. Explore / Plan은 한 번이고, 부모는 이어 달리지 않습니다.
런타임은 아이마다 agentId를 줍니다. 별도의 spawn-edge 표는 없어요. 복구는 그 id의 transcript를 읽습니다. 부모가 살아 있는 아이를 나열하려면 곁사슬을 훑어야 하고, 변으로 거르는 일은 없습니다. Codex는 표 하나와 상태 두 벌을 더 써서, 재시작 뒤에도 그래프로 말할 수 있게 합니다.
Closed 부모 변 아래 Open 손자 변은 아직 있나
세 층 나무를 그리세요. 루트에서 A는 Closed, A에서 B는 Open. Some(Open)과 None으로 후손을 한 번씩 나열합니다. B가 나타날까요?
필터는 걷는 모든 변에 작용합니다. 그다음 codex-rs/agent-graph-store/src/store.rs 49–54행 주석을 대조하고 두 결과를 적으세요.