Messages and delivery

명령 대기열

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

필요한 이유

  • 자동 응답 실행에는 많은 비용(LLM 호출)이 들 수 있으며, 여러 인바운드 메시지가 짧은 간격으로 도착하면 충돌할 수 있습니다.
  • 직렬화하면 공유 리소스(세션 파일, 로그, CLI 표준 입력)를 두고 경합하는 상황을 방지하고 업스트림 속도 제한에 걸릴 가능성을 줄일 수 있습니다.

작동 방식

  • 레인 인식 FIFO 큐가 구성 가능한 동시 실행 한도에 따라 각 레인을 비웁니다(구성되지 않은 레인의 기본값은 1이며, main의 기본값은 4, subagent의 기본값은 8).
  • runEmbeddedAgent세션 키(레인 session:<key>)별로 큐에 추가하여 세션당 활성 실행이 하나만 존재하도록 보장합니다.
  • 이후 각 세션 실행은 전역 레인(기본값은 main)에 큐잉되므로 전체 병렬 처리 수준은 agents.defaults.maxConcurrent로 제한됩니다.
  • 상세 로깅이 활성화된 경우, 큐에 추가된 실행은 시작 전 약 2초 넘게 대기하면 짧은 알림을 출력합니다.
  • 입력 중 표시기는 큐에 추가되는 즉시 계속 표시되므로(채널에서 지원하는 경우), 실행이 차례를 기다리는 동안에도 사용자 경험은 달라지지 않습니다.

기본값

설정하지 않으면 모든 인바운드 채널 표면에서 다음을 사용합니다.

  • mode: "steer"
  • debounceMs: 500
  • cap: 20
  • drop: "summarize"

동일 턴 조정이 기본값입니다. 실행 도중 프롬프트가 도착하면 해당 실행이 조정을 수락할 수 있는 경우 활성 런타임에 주입되므로 두 번째 세션 실행이 시작되지 않습니다. 활성 실행이 조정을 수락할 수 없으면 OpenClaw은 활성 실행이 끝날 때까지 기다린 후 프롬프트를 시작합니다.

큐 모드

/queue는 세션에 이미 활성 실행이 있을 때 일반 인바운드 메시지를 처리하는 방식을 제어합니다.

  • steer: 메시지를 활성 런타임에 주입합니다. OpenClaw은 다음 LLM 호출 전에 현재 어시스턴트 턴이 도구 호출 실행을 마친 후 대기 중인 모든 조정 메시지를 전달하며, Codex app-server는 일괄 처리된 turn/steer 하나를 수신합니다. 실행이 활발하게 스트리밍 중이 아니거나 조정을 사용할 수 없으면 OpenClaw은 활성 실행이 끝날 때까지 기다린 후 프롬프트를 시작합니다.
  • followup: 조정하지 않습니다. 현재 실행이 끝난 후 처리할 이후 에이전트 턴을 위해 각 메시지를 큐에 추가합니다.
  • collect: 조정하지 않습니다. 대기 시간 구간이 지난 후 큐에 추가된 메시지를 단일 후속 턴으로 병합합니다. 메시지가 서로 다른 채널이나 스레드를 대상으로 하면 라우팅을 유지하기 위해 개별적으로 처리합니다.
  • interrupt: 해당 세션의 활성 실행을 중단한 후 가장 최신 메시지를 실행합니다.

런타임별 타이밍 및 종속성 동작은 조정 큐를 참조하세요. 명시적 /steer <message> 명령은 조정을 참조하세요.

messages.queue를 통해 전역 또는 채널별로 구성합니다.

json5
{  messages: {    queue: {      mode: "steer",      debounceMs: 500,      cap: 20,      drop: "summarize",      byChannel: { discord: "collect" },    },  },}

큐 옵션

옵션은 큐잉된 전달에 적용됩니다. debounceMssteer 모드에서 Codex 조정 대기 시간 구간도 설정합니다.

  • debounceMs: 큐잉된 후속 메시지나 수집 배치를 처리하기 전의 대기 시간 구간입니다. Codex steer 모드에서는 일괄 처리된 turn/steer를 보내기 전의 대기 시간 구간입니다. 단위 없는 숫자는 밀리초이며, /queue 옵션에서는 ms, s, m, h, d 단위를 사용할 수 있습니다.
  • cap: 세션별 큐잉 메시지의 최대 개수입니다. 1 미만의 값은 무시됩니다.
  • drop: "summarize"(기본값): 필요에 따라 가장 오래된 큐 항목을 삭제하고, 간결한 요약을 보존하여 합성 후속 프롬프트로 주입합니다.
  • drop: "old": 요약을 보존하지 않고 필요에 따라 가장 오래된 큐 항목을 삭제합니다.
  • drop: "new": 큐가 이미 가득 찬 경우 가장 최신 메시지를 거부합니다.

기본값: debounceMs: 500, cap: 20, drop: summarize.

조정 및 스트리밍

채널 스트리밍이 partial 또는 block이면 활성 실행이 런타임 경계에 도달하는 동안 조정이 여러 개의 짧고 가시적인 응답처럼 보일 수 있습니다.

  • partial: 미리보기가 일찍 완료된 후 조정이 수락되면 새 미리보기가 시작될 수 있습니다.
  • block: 초안 크기의 블록에서도 동일하게 순차적으로 표시될 수 있습니다.
  • 스트리밍을 사용하지 않고 런타임이 동일 턴 조정을 수락할 수 없는 경우, 활성 실행이 끝난 후 후속 처리로 대체됩니다.

steer는 실행 중인 도구를 중단하지 않습니다. 가장 최신 메시지가 현재 실행을 중단해야 하는 경우 /queue interrupt를 사용하세요.

우선순위

모드를 선택할 때 OpenClaw은 다음 순서로 결정합니다.

  1. 인라인 또는 저장된 세션별 /queue 재정의.
  2. messages.queue.byChannel.<channel>.
  3. messages.queue.mode.
  4. 기본값 steer.

옵션의 경우 인라인 또는 저장된 /queue 옵션이 구성보다 우선합니다. 그런 다음 채널별 디바운스(messages.queue.debounceMsByChannel), Plugin 디바운스 기본값, 전역 messages.queue 옵션, 내장 기본값 순으로 적용됩니다. capdrop은 전역/세션 옵션이며 채널별 구성 키가 아닙니다.

세션별 재정의

  • /queue <steer|followup|collect|interrupt>를 독립 실행형 명령으로 보내 현재 세션의 큐 모드를 저장합니다.
  • 옵션을 함께 사용할 수 있습니다: /queue collect debounce:0.5s cap:25 drop:summarize
  • /queue default 또는 /queue reset은 세션 재정의를 지웁니다.

큐잉된 턴 취소

프롬프트가 followup/collect 큐에 있는 동안(예: 다른 턴이 활성 상태일 때 도착한 TUI 또는 웹 채팅 chat.send) Gateway는 큐잉된 콘텐츠가 실행되거나 삭제될 때까지 해당 클라이언트 runId에 대한 Gateway 소유 취소 ID를 유지합니다. 이 ID는 오버플로 요약에 병합된 콘텐츠를 따라갑니다.

  • 특정 runId가 지정된 chat.abort는 해당 턴이 아직 큐에 있는 동안 요청자에게 권한이 있으면 이를 취소합니다(활성 실행과 동일한 소유권 규칙 적용).
  • runId 없이 세션에 대해 실행한 chat.abort권한이 있는 큐잉된 턴을 먼저 취소한 후 권한이 있는 활성 실행을 중단합니다. 이 순서를 통해 큐 처리가 작업을 절반만 중지된 세션으로 승격하는 것을 방지합니다.
  • 요청자별 검사 없이 전체 세션 큐를 지우는 방식은 소유자가 여러 명인 세션의 중지 경로가 아닙니다.
  • 큐 대기는 sessions.list에서 활성 에이전트 실행으로 표시되지 않으며 활성 실행 제한 시간 의미 체계를 소유하지 않습니다. 활성 단계만 이를 소유합니다.

클라이언트(TUI 포함)는 실행 도중 도착한 프롬프트를 전달하고 Gateway가 큐 모드를 적용하도록 합니다. Esc//stop은 세션 범위 중단을 사용하므로 로컬 핸들을 잃더라도 아직 큐에 있는 프롬프트가 실행되는 상황을 방지할 수 있습니다.

범위 및 보장 사항

  • Gateway 응답 파이프라인을 사용하는 모든 인바운드 채널(WhatsApp 웹, Telegram, Slack, Discord, Signal, iMessage, 웹 채팅 등)의 자동 응답 에이전트 실행에 적용됩니다.
  • 기본 레인(main)은 인바운드 및 기본 Heartbeat에 대해 프로세스 전체에서 공유됩니다. 여러 세션을 병렬로 허용하려면 agents.defaults.maxConcurrent를 설정하세요.
  • 백그라운드 작업이 인바운드 응답을 차단하지 않고 병렬로 실행될 수 있도록 추가 레인(예: cron, cron-nested, nested, subagent)이 존재할 수 있습니다. 격리된 Cron 에이전트 턴은 내부 에이전트 실행이 cron-nested를 사용하는 동안 cron 슬롯을 점유하며, 둘 다 cron.maxConcurrentRuns를 사용합니다. 공유되는 Cron 이외의 nested 흐름은 자체 레인 동작을 유지합니다. 이러한 분리된 실행은 백그라운드 작업으로 추적됩니다.
  • 세션별 레인은 한 번에 하나의 에이전트 실행만 특정 세션에 접근하도록 보장합니다.
  • 외부 종속성이나 백그라운드 워커 스레드가 없으며, 순수 TypeScript와 프로미스만 사용합니다.

문제 해결

  • 명령이 멈춘 것처럼 보이면 상세 로그를 활성화하고 "queued for ...ms" 줄을 확인하여 큐가 처리되고 있는지 확인하세요.
  • 턴을 수락한 후 진행 상황 출력을 중단하는 Codex app-server 실행은 Codex 어댑터에 의해 중단되므로, 활성 세션 레인은 외부 실행 제한 시간을 기다리는 대신 해제될 수 있습니다.
  • 진단이 활성화된 경우, 관찰된 응답, 도구, 상태, 블록 또는 ACP 진행 상황 없이 diagnostics.stuckSessionWarnMs를 초과하여 processing 상태로 남아 있는 세션은 현재 활동에 따라 분류됩니다.
    • 최근 진행 상황이 있는 활성 작업은 session.long_running으로 기록됩니다. 소유자가 있는 무응답 모델 호출도 느리거나 스트리밍하지 않는 제공자가 너무 일찍 중단된 것으로 보고되지 않도록 diagnostics.stuckSessionAbortMs까지 session.long_running으로 유지됩니다.
    • 최근 진행 상황이 없는 활성 작업은 session.stalled로 기록됩니다. 소유자가 있는 모델 호출, 차단된 도구 호출 및 멈춘 임베디드 실행은 중단 임계값에 도달하거나 이를 초과하면 session.stalled로 전환됩니다. 소유자가 없는 오래된 모델/도구 활동은 장기 실행으로 숨겨지지 않습니다.
    • session.stuck은 소유자가 없는 오래된 모델/도구 활동이 있는 유휴 큐잉 세션을 포함하여 복구 가능한 오래된 세션 상태 기록에만 사용됩니다.
    • session.stuck은 항상 영향을 받은 세션 레인을 해제할 수 있는 복구를 트리거합니다. diagnostics.stuckSessionAbortMs를 초과한 session.stalled 분류(차단된 도구 호출, 멈춘 모델 호출 또는 멈춘 임베디드 실행)도 활성 중단 복구를 트리거할 수 있으므로 session.stuck뿐만 아니라 두 분류 모두 큐의 멈춤을 해제할 수 있습니다.
    • 세션이 변경되지 않은 상태로 유지되는 동안 반복되는 session.stucksession.long_running 경고 로그 줄에는 지수 백오프가 적용됩니다. 복구 시도는 이 백오프와 관계없이 매 Heartbeat 틱마다 계속 실행됩니다.

관련 항목

Was this useful?
On this page

On this page