Command Queue (2026-01-16)

우리는 session 간 안전한 병렬 처리를 허용하면서 여러 agent 실행이 충돌하지 않도록 작은 인프로세스 큐를 통해 인바운드 자동 응답 실행(모든 channel)을 직렬화합니다.

이유

  • 자동 응답 실행은 비용이 많이 들 수 있으며(LLM 호출) 여러 인바운드 메시지가 가까운 시간에 도착하면 충돌할 수 있습니다.
  • 직렬화는 공유 리소스(session 파일, 로그, CLI stdin)를 놓고 경쟁하는 것을 방지하고 업스트림 rate limit에 걸릴 가능성을 줄입니다.

작동 방식

  • Lane 인식 FIFO 큐는 구성 가능한 동시성 한도로 각 lane을 처리합니다(구성되지 않은 lane의 기본값 1; main은 기본값 4, subagent는 8).
  • runEmbeddedPiAgentsession 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, collectsteer-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 로그를 활성화하고 큐 타이밍 라인을 확인하세요.