Command Queue (2026-01-16)
우리는 session 간 안전한 병렬 처리를 허용하면서 여러 agent 실행이 충돌하지 않도록 작은 인프로세스 큐를 통해 인바운드 자동 응답 실행(모든 channel)을 직렬화합니다.
이유
- 자동 응답 실행은 비용이 많이 들 수 있으며(LLM 호출) 여러 인바운드 메시지가 가까운 시간에 도착하면 충돌할 수 있습니다.
- 직렬화는 공유 리소스(session 파일, 로그, CLI stdin)를 놓고 경쟁하는 것을 방지하고 업스트림 rate limit에 걸릴 가능성을 줄입니다.
작동 방식
- Lane 인식 FIFO 큐는 구성 가능한 동시성 한도로 각 lane을 처리합니다(구성되지 않은 lane의 기본값 1; main은 기본값 4, subagent는 8).
- runEmbeddedPiAgent는 session key로 대기열에 넣어(lane session:<key>) session당 하나의 활성 실행만 보장합니다.
- 각 session 실행은 전역 lane(main이 기본값)에 대기열에 들어가므로 전체 병렬 처리는 agents.defaults.maxConcurrent로 제한됩니다.
- verbose 로깅이 활성화된 경우 대기열에 있는 실행은 시작하기 전에 ~2초 이상 대기한 경우 짧은 알림을 발생시킵니다.
- 입력 표시기는 여전히 대기열에 넣을 때 즉시 발생하므로(channel이 지원하는 경우) 사용자 경험은 차례를 기다리는 동안 변경되지 않습니다.
큐 모드 (channel별)
인바운드 메시지는 현재 실행을 조종하거나, 후속 턴을 기다리거나, 둘 다 수행할 수 있습니다:
- steer: 현재 실행에 즉시 주입(다음 tool 경계 후 대기 중인 tool 호출 취소). streaming이 아닌 경우 followup으로 폴백합니다.
- followup: 현재 실행이 끝난 후 다음 agent 턴을 위해 대기열에 넣습니다.
- collect: 대기열에 있는 모든 메시지를 단일 followup 턴으로 병합합니다(기본값). 메시지가 다른 channel/thread를 대상으로 하는 경우 라우팅을 유지하기 위해 개별적으로 처리됩니다.
- steer-backlog (또는 steer+backlog): 지금 조종하고 그리고 후속 턴을 위해 메시지를 보존합니다.
- interrupt (레거시): 해당 session의 활성 실행을 중단한 다음 최신 메시지를 실행합니다.
- queue (레거시 별칭): steer와 동일합니다.
Steer-backlog는 조종된 실행 후에 후속 응답을 받을 수 있으므로 streaming 표면은 중복처럼 보일 수 있습니다. 인바운드 메시지당 하나의 응답을 원하는 경우 collect/steer를 선호하세요. 독립 실행형 명령(session별)으로 /queue collect를 보내거나 messages.queue.byChannel.discord: "collect"를 설정하세요.
기본값 (config에서 설정하지 않은 경우):
- 모든 표면 → collect
messages.queue를 통해 전역 또는 channel별로 구성:
{
messages: {
queue: {
mode: "collect",
debounceMs: 1000,
cap: 20,
drop: "summarize",
byChannel: { discord: "collect" }
}
}
}
큐 옵션
옵션은 followup, collect 및 steer-backlog에 적용됩니다(steer가 followup으로 폴백할 때도 적용):
- debounceMs: 후속 턴을 시작하기 전에 조용해질 때까지 기다립니다("continue, continue" 방지).
- cap: session당 최대 대기 중인 메시지.
- drop: 오버플로 정책 (old, new, summarize).
Summarize는 삭제된 메시지의 짧은 글머리 기호 목록을 유지하고 합성 followup 프롬프트로 주입합니다. 기본값: debounceMs: 1000, cap: 20, drop: summarize.
Session별 재정의
- 현재 session에 대한 모드를 저장하려면 독립 실행형 명령으로 /queue <mode>를 보내세요.
- 옵션을 결합할 수 있습니다: /queue collect debounce:2s cap:25 drop:summarize
- /queue default 또는 /queue reset은 session 재정의를 지웁니다.
범위 및 보장
- gateway 응답 파이프라인을 사용하는 모든 인바운드 channel(WhatsApp web, Telegram, Slack, Discord, Signal, iMessage, webchat 등)에서 자동 응답 agent 실행에 적용됩니다.
- 기본 lane(main)은 인바운드 + main heartbeat에 대해 프로세스 전체이며, agents.defaults.maxConcurrent를 설정하여 여러 session을 병렬로 허용합니다.
- 추가 lane이 존재할 수 있으므로(예: cron, subagent) 백그라운드 작업이 인바운드 응답을 차단하지 않고 병렬로 실행될 수 있습니다.
- Session별 lane은 주어진 시간에 하나의 agent 실행만 주어진 session을 터치하도록 보장합니다.
- 외부 종속성이나 백그라운드 워커 스레드 없음; 순수 TypeScript + promise.
문제 해결
- 명령이 멈춘 것 같으면 verbose 로그를 활성화하고 "queued for …ms" 라인을 찾아 큐가 처리되고 있는지 확인하세요.
- 큐 깊이가 필요한 경우 verbose 로그를 활성화하고 큐 타이밍 라인을 확인하세요.