OMC (oh-my-claudecode) · OMC 고유 기능: Teams 런타임/브리지 (프로세스 기반 멀티에이전트 + 거버넌스)
OMC (oh-my-claudecode) · OMC 고유 기능: Teams 런타임/브리지 (프로세스 기반 멀티에이전트 + 거버넌스)
한 줄 요약
OMC의 “팀 리더(메인 Claude)“가 tmux 창마다 진짜 codex/gemini CLI를 별도 프로세스로 띄워 워커로 부리되, 출입증(권한)·CCTV(감사로그)·사규(거버넌스)로 통제하는 멀티에이전트 런타임이다. 왜 배우나: “여러 AI를 동시에 일 시키는 것”과 “그걸 안전하게 통제하는 것”이 어떻게 한 시스템에서 만나는지 보여주는 OMC의 시그니처 설계이기 때문이다.
그림
flowchart TD A["사용자: /team N:codex 작업"] --> B["리더가 런타임 잡 기동<br/>spawn(runtime-cli.cjs)"] B --> C["tmux 패인 N개 생성<br/>(창 분할)"] C --> D["각 패인: team-bridge.cjs<br/>--config config.json"] D --> E["워커 폴 루프 while(true)<br/>심장박동: polling→ready→executing"] E --> F["할 일 집기<br/>findNextTask: 파일락으로 원자 클레임"] F --> G["프롬프트 만들기<br/>TASK 태그 + 보안 sanitize"] G --> H["진짜 CLI 실행<br/>codex exec --json / gemini --yolo"] H --> I["결과 추출<br/>parseCodexOutput → outbox + 결과파일"] I --> J["리더가 결과 파일 읽고 다음 단계로"] E -.매 전이마다.-> K["감사로그 audit()<br/>team-bridge-팀명.jsonl<br/>(추가만 가능, 0o600)"] F -.작업이 막연하면.-> L["isBroadTeamTaskText →<br/>자동 위임 플랜 부여"]
쉽게 풀기
작은 사무실 하나를 떠올려 보자. 팀장 한 명이 있고, 일이 몰리면 외주 작업자를 여러 명 불러 나눠 시킨다. OMC의 Teams 런타임이 바로 이 사무실이다.
1) 워커를 만드는 두 가지 길이 있다.
- (a) 네이티브 방식: Claude Code가 기본으로 주는 도구(
TeamCreate / SendMessage / Task*)로, Claude “안에서” 가벼운 서브에이전트를 만든다. 같은 회사 사람을 잠깐 빌려 쓰는 느낌이다. - (b) 브리지 방식(이게 OMC 시그니처): tmux(터미널 창을 여러 칸으로 쪼개는 도구)의 칸마다 진짜
claude/codex/gemini프로그램을 별개의 OS 프로세스로 띄운다. 즉 OpenAI Codex, Google Gemini 같은 타사 AI도 외주 작업자로 합류시킨다.
2) 왜 일부러 따로 두었나.
(b) 방식은 일반 Claude 도구 묶음(t MCP 서버)과 섞이지 않고, 별도의 ‘team’ MCP 서버에 산다. 이유는 단순하다 — 위험하고 무거운 “진짜 프로세스 띄우기” 기능을, 평소 쓰는 가벼운 도구들과 한 바구니에 담지 않으려는 안전 격리다.
3) 그냥 프로세스만 띄우는 게 아니다. 사무실로 비유하면 세 가지 통제 장치가 동시에 돈다.
- 출입증(권한): 워커가 건드려도 되는 폴더/명령만 허용한다.
- CCTV(감사로그): 누가 언제 무엇을 했는지 한 줄씩 기록을 남긴다. 이 기록은 “추가만 가능(append-only)“이라 나중에 몰래 고칠 수 없다.
- 사규(거버넌스): “팀장은 직접 코딩 말고 위임만 해라”, “팀은 세션당 하나만” 같은 규칙을 강제한다.
4) 막연한 일은 자동으로 쪼개게 만든다. “코드베이스 좀 정리해줘”처럼 두루뭉술한 지시가 들어오면, 판별기(isBroadTeamTaskText)가 “이건 너무 광범위하다”고 판단해 자동으로 위임 플랜을 붙인다. 작업자가 혼자 끙끙대지 않고 병렬로 나눠 탐색하게 유도하는 장치다.
[!note] 지금은 일부가 은퇴(retired) 상태 현 코드 기준
team-mcp.cjs의 MCP 런타임 도구 4종(omc_run_team_start/status/wait/cleanup)은 DEPRECATED로 표시되어 CLI(omc team ...)로 이관됐다. 설치 레지스트리도bridge/team-mcp.cjs경로의 MCP 엔트리를 은퇴 처리해 자동 제거한다(RETIRED_TEAM_MCP_PATH_PATTERN). 다만 워커 실행체(team-bridge.cjs)와 거버넌스/감사 형식은 그대로 살아 있다. 즉 “리모컨(MCP 도구)“은 CLI로 옮겨졌지만 “엔진(브리지)“은 동일하다.
핵심 정리
거버넌스 기본값 — 무엇을 강제하나
| 규칙 | 기본값 | 한 줄 의미 |
|---|---|---|
delegation_only | false | 리더가 직접 작업 못 하고 위임만 |
plan_approval_required | false | 실행 전 계획 승인 게이트 |
nested_teams_allowed | false | 워커가 또 팀 만드는 중첩 금지 |
one_team_per_leader_session | true | 리더 세션당 팀 1개 |
cleanup_requires_all_workers_inactive | true | 모두 쉴 때만 정리 허용 |
[!note] 전송/런타임 정책(
TeamTransportPolicy) 기본값
display_mode=split_pane(tmux 표시 방식, 또는auto)worker_launch_mode=interactive(또는prompt)dispatch_mode=hook_preferred_with_fallback(또는transport_direct)dispatch_ack_timeout_ms=15000(디스패치 ACK 대기 한도)레거시 매니페스트에서는 transport와 governance가 한 객체에 섞여 있었고,
normalizeTeamGovernance()가governance ?? legacyPolicy ?? DEFAULT순으로 흡수한다.
워커 config.json — 브리지가 읽는 항목
| 필드 | 필수 | 기본값 / 메모 |
|---|---|---|
teamName / workerName | sanitizeName 처리됨 | |
provider | codex 또는 gemini (claude는 별 경로) | |
workingDirectory | 반드시 git worktree 안 | |
pollIntervalMs | – | 3000 |
taskTimeoutMs | – | 600000 (CLI 작업 타임아웃) |
maxConsecutiveErrors | – | 3 초과 시 self-quarantine |
permissionEnforcement | – | off / audit / enforce |
permissions | – | allowedPaths/deniedPaths/allowedCommands (**,* 위험 패턴 거부) |
감사 이벤트(AuditEvent) — CCTV 기록 한 줄의 구성
[!note] 형식과 보관 저장 형식은 append-only JSONL, 권한 0o600, 5MB 초과 시 절반만 보존하며 회전. 경로는
getOmcRoot(cwd)/logs/team-bridge-<teamName>.jsonl. 한 줄에는timestamp(ISO)·eventType·teamName·workerName(필수)과taskId·details(선택)가 담긴다.
AuditEventType 19종 (모든 상태 전이에서 한 줄씩 기록):
- 생애주기:
bridge_start·bridge_shutdown·worker_ready·worker_idle·worker_quarantined - 작업:
task_claimed·task_started·task_completed·task_failed·task_permanently_failed - 메일함:
inbox_rotated·outbox_rotated - CLI 실행:
cli_spawned·cli_timeout·cli_error - 종료/권한:
shutdown_received·shutdown_ack·permission_violation·permission_audit
막연한 작업 판별(isBroadTeamTaskText) — 어떻게 “광범위”로 보나
[!note] 판정 규칙
- 단어 4개 미만 → 광범위 아님
- 좁은 코드 타깃(파일명·심볼)이 있고 단어 < 12 → 광범위 아님
- broad 동사(investigate/analyze/debug/review/refactor/build/implement 등) 매칭 → 광범위
fix+ broad 객체(runtime/system/codebase/architecture/tests 등) 동시 매칭 → 광범위- 광범위로 판정되면
{mode:'auto', required_parallel_probe:true, skip_allowed_reason_required:true, child_report_format:'bullets'}부여
실제 예시
워커가 진짜 CLI를 띄우는 핵심 (시그니처)
// bridge/team-bridge.cjs (spawnCliProcess)
function spawnCliProcess(provider, prompt, model, cwd, timeoutMs) {
validateProvider(provider);
validateModelName(model);
let args; let cmd;
if (provider === "codex") {
cmd = "codex";
args = ["exec", "-m", model || getBuiltinExternalDefaultModel("codex"),
"--json", "--dangerously-bypass-approvals-and-sandbox", "--skip-git-repo-check"];
} else {
cmd = "gemini";
args = ["--approval-mode", "yolo"];
if (model) args.push("--model", model);
}
const child = (0, import_child_process5.spawn)(cmd, args, { stdio: ["pipe","pipe","pipe"], cwd });
// ...stdin에 prompt 주입, stdout JSON 이벤트 파싱(parseCodexOutput), timeoutMs로 SIGTERM kill...
}
거버넌스 기본값
// src/team/governance.ts
export const DEFAULT_TEAM_GOVERNANCE: TeamGovernance = {
delegation_only: false,
plan_approval_required: false,
nested_teams_allowed: false,
one_team_per_leader_session: true,
cleanup_requires_all_workers_inactive: true,
};
’team’ 도구가 메인 ‘t’ 서버에서 일부러 빠진 이유 (주석)
// src/mcp/tool-registry.ts (헤더 주석)
* Team runtime tools (omc_run_team_start, omc_run_team_status) are intentionally
* excluded: they live in the separate "team" MCP server (bridge/team-mcp.cjs).
실제로 .mcp.json은 t 서버(bridge/mcp-server.cjs)만 등록한다 — team 런타임은 분리된 서버/CLI다.
복붙용 최소 워커 config (브리지가 --config로 읽는 형식)
{
"teamName": "demo-team",
"workerName": "w1",
"provider": "codex",
"model": "gpt-5.3-codex",
"workingDirectory": "/abs/path/to/git/worktree",
"pollIntervalMs": 3000,
"taskTimeoutMs": 600000,
"maxConsecutiveErrors": 3,
"permissionEnforcement": "enforce",
"permissions": {
"allowedPaths": ["src/**", "tests/**"],
"deniedPaths": [".git/**", ".env*", "**/secrets/**"],
"allowedCommands": [],
"maxFileSize": 1048576
}
}
거버넌스 매니페스트 조각
{
"schema_version": 2,
"governance": {
"delegation_only": true,
"plan_approval_required": true,
"nested_teams_allowed": false,
"one_team_per_leader_session": true,
"cleanup_requires_all_workers_inactive": true
},
"policy": { "display_mode": "split_pane", "worker_launch_mode": "interactive",
"dispatch_mode": "hook_preferred_with_fallback", "dispatch_ack_timeout_ms": 15000 }
}
직접 만들 때 체크리스트
- 메인 도구 서버(
t)에 team 런타임 도구를 넣지 말 것 — 별도 ‘team’ 서버/CLI로 분리(tool-registry 주석 원칙). - 워커 프로세스는
child_process.spawn으로 진짜 CLI(codex exec --json/gemini --approval-mode yolo)를 띄운다. - config 경로는
~/.claude또는~/.omc하위만 허용, cwd는 git worktree 안인지 검증. - 감사 로그: append-only JSONL, 0o600,
getOmcRoot/logs/team-bridge-<team>.jsonl, 5MB 회전,validateResolvedPath로 traversal 차단. - 이벤트 타입은 enum으로 고정(bridge_start … permission_audit 19종), 모든 상태 전이에서
audit()호출. - 작업 클레임은 파일락(
acquireTaskLock, O_EXCL, stale 30s + PID 생존확인)으로 원자화. - 프롬프트는
<TASK_*>태그 +sanitizePromptContent로 인젝션 무력화. - 광범위 작업(
isBroadTeamTaskText)이면 자동 위임 플랜 부여. - 거버넌스 기본값:
one_team_per_leader_session/cleanup_requires_all_workers_inactive만 true, 나머지 false. - SIGINT/SIGTERM에서 heartbeat 삭제 +
unregisterMcpWorker로 정리.
요약 & 셀프체크
3줄 요약:
- OMC Teams는 tmux 패인마다 진짜 codex/gemini CLI를 별도 프로세스로 띄워 타사 AI까지 워커로 합류시킨다.
- 위험한 프로세스 기능이라 일반 ‘t’ 도구 서버와 격리된 별도 ‘team’ 서버/CLI에 두고, 권한·감사로그·거버넌스로 통제한다.
- 막연한 작업은
isBroadTeamTaskText가 자동으로 위임 플랜을 붙여 병렬 탐색을 유도한다.
스스로 답해보기:
- 네이티브 방식과 브리지 방식의 가장 큰 차이는 무엇이고, 브리지 방식이 가능케 하는 일은? (힌트: 별개 OS 프로세스, 타사 AI 합류)
- team 런타임 도구를 메인 ‘t’ 서버에서 일부러 뺀 이유를 한 문장으로?
- 감사로그가 “추가만 가능(append-only)·0o600”인 것이 거버넌스 관점에서 왜 중요한가?
연결
- 권한/가드레일 연계: OMC_60_guardrails-permissions
- 워커가 만드는 가벼운 서브에이전트와의 대비: OMC_30_subagents
- 분리된 도구 서버(
t)의 맥락: OMC_50_mcp-tool-system
[!info] 근거 파일
/home/seunghyeong/harness-work/oh-my-claudecode/bridge/team-mcp.cjs(handleStart spawn runtime-cli.cjs, TOOLS 4종 DEPRECATED,new Server({name:"team"}), sendToWorker send-keys)/home/seunghyeong/harness-work/oh-my-claudecode/bridge/team-bridge.cjs(runBridge 폴 루프, spawnCliProcess codex/gemini, formatPromptTemplate, audit, main config 검증)/home/seunghyeong/harness-work/oh-my-claudecode/src/team/governance.ts(DEFAULT_TEAM_GOVERNANCE, DEFAULT_TEAM_TRANSPORT_POLICY, normalize*)/home/seunghyeong/harness-work/oh-my-claudecode/src/team/audit-log.ts(AuditEventType enum, AuditEvent, 0o600 JSONL, rotateAuditLog)/home/seunghyeong/harness-work/oh-my-claudecode/src/team/delegation-evidence.ts(isBroadTeamTaskText, BROAD_TASK_DELEGATION_PLAN, 정규식)/home/seunghyeong/harness-work/oh-my-claudecode/src/team/types.ts(TeamGovernance/TeamTransportPolicy/TeamTaskDelegationPlan/McpWorkerMember/HeartbeatData/TeamManifestV2)/home/seunghyeong/harness-work/oh-my-claudecode/src/team/permissions.ts(SECURE_DENY_DEFAULTS, isPathAllowed, findPermissionViolations — team-bridge.cjs에 인라인)/home/seunghyeong/harness-work/oh-my-claudecode/src/mcp/tool-registry.ts(team 런타임 도구 의도적 제외 주석)/home/seunghyeong/harness-work/oh-my-claudecode/src/installer/mcp-registry.ts(RETIRED_TEAM_MCP_PATH_PATTERN, isRetiredTeamMcpEntry)/home/seunghyeong/harness-work/oh-my-claudecode/.mcp.json(t서버만 등록)/home/seunghyeong/harness-work/oh-my-claudecode/skills/omc-teams/SKILL.md(CLI-team 사용법, claude/codex/gemini)