Message lifecycle refactor
기준일: 2026-07-26
공식 기준: Message lifecycle refactor
Message lifecycle refactor 문서는 OpenClaw 공식 문서(concepts/message-lifecycle-refactor)를 한국어로 정리한 가이드입니다. Status of the durable message receive/send lifecycle: what shipped, what changed from the original design, and what remains open 명령·설정 키·코드 예시는 공식 문서를 그대로 보존하며, 해석과 절차 안내는 한국어로 제공합니다. 최종 동작은 설치된 CLI 버전과 공식 원문을 확인하세요.
핵심 요약
Status of the durable message receive/send lifecycle: what shipped, what changed from the original design, and what remains open
한국어 가이드 범위: concepts/message-lifecycle-refactor 경로의 설정·명령·제약·예시를 학습용으로 재구성합니다.
문서 구성
공식 문서의 주요 섹션은 다음과 같습니다.
- Why this refactor happened
- What shipped
- Send context
- Receive context
- Live preview
- Durable receipts
- Public SDK reduction
- Where the implementation diverged from the original design
- Concrete migration hazards (still relevant)
- Failure classification
- Open questions
- 관련 문서
상세 내용
본문
This page originated as a forward-looking design proposal. The core of that design has since shipped in src/channels/message/* and the public openclaw/plugin-sdk/channel-outbound / channel-inbound subpaths. For the current API, use Channel outbound API and Channel inbound API. This page tracks what shipped, where the implementation diverged from the original sketch, and what is still open.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
Why this refactor happened
The channel stack grew from several local fixes: separate inbound helpers per maturity level (runtime.channel.inbound.run for simple adapters, runtime.channel.inbound.runPreparedReply for rich ones), legacy reply-dispatch helpers (dispatchInboundReplyWithBase, recordInboundSessionAndDispatchReply), channel-specific preview streaming, and final-delivery durability bolted onto existing reply-payload paths. That shape produced too many public concepts and too many places where delivery semantics could drift.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
Telegram polling update acked
-> assistant final text exists
-> process restarts before sendMessage succeeds
-> final response is lost
What shipped
The internal domain lives in src/channels/message/*:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
| File | Owns |
|---|---|
types.ts |
Adapter, send-context, receipt, and durable-intent type contracts |
send.ts |
withDurableMessageSendContext / sendDurableMessageBatch — the durable send context |
receive.ts |
createMessageReceiveContext — inbound ack-policy state machine |
live.ts |
Live preview state and finalize-in-place-or-fall-back logic |
state.ts |
classifyDurableSendRecoveryState — recovery classification after interruption |
receipt.ts |
Normalizes platform send results into MessageReceipt |
capabilities.ts |
Derives required durable-final capabilities from a payload |
contracts.ts |
Contract-proof verification for declared adapter capabilities |
adapter.ts |
defineChannelMessageAdapter |
outbound-bridge.ts |
createChannelMessageAdapterFromOutbound — wraps legacy sendText/sendMedia/sendPayload/sendPoll functions |
ingress-queue.ts |
createChannelIngressQueue — durable inbound event queue |
durable-receive.ts |
createDurableInboundReceiveJournal — accept/pending/complete/release journal for inbound dedupe |
inbound-reply-dispatch.ts |
dispatchChannelInboundReply and legacy-named wrappers |
reply-pipeline.ts |
createChannelReplyPipeline, reply-prefix and typing-callback helpers |
Send context
withDurableMessageSendContext gives channel code render, previewUpdate, send, edit, delete, commit, and fail steps around one outbound message. sendDurableMessageBatch is the common-case wrapper: render, send, then commit on sent/suppressed or fail on error.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
| Status | Meaning |
|---|---|
sent |
At least one visible platform message was delivered |
suppressed |
No platform message should be treated as missing (hook-cancelled, dry-run, etc.) |
partial_failed |
At least one message delivered before a later payload or side effect failed |
failed |
No platform receipt was produced |
Receive context
createMessageReceiveContext tracks ack/nack state per inbound event with an idempotent ack() and explicit nack(error). The ack policy (ChannelMessageReceiveAckPolicy) is one of:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
| Policy | Acks when |
|---|---|
after_receive_record |
Core persisted enough inbound metadata to dedupe/route a redelivery |
after_agent_dispatch |
The agent run has been dispatched |
after_durable_send |
The durable outbound send for this turn committed |
manual |
Caller controls ack timing explicitly (the default for adapters that do not declare a policy) |
Live preview
src/channels/message/live.ts models preview/edit/finalize as one lifecycle: createLiveMessageState, markLiveMessagePreviewUpdated, markLiveMessageFinalized, markLiveMessageCancelled, and deliverFinalizableLivePreviewAdapter (build a final edit from a draft, apply it, and fall back to a normal send when the edit is not possible or fails). LiveMessageState.phase is idle | previewing | finalizing | finalized | cancelled; canFinalizeInPlace gates whether a preview can become the final message via edit instead of a fresh send.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
Durable receipts
MessageReceipt (src/channels/message/types.ts) normalizes one or more platform message ids from a single logical send into platformMessageIds plus per-part parts (kind, index, thread id, reply-to id). A primary id is kept for threading and later edits. This is what makes multi-part deliveries (text plus media, chunked text, card fallback) replayable and de-duplicatable after a restart.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
Public SDK reduction
The refactor absorbed or deprecated: reply-runtime, reply-dispatch-runtime, reply-reference, reply-chunking, reply-payload helpers exposed as public API, inbound-reply-dispatch, channel-reply-pipeline, and most public uses of the old outbound facade. src/plugin-sdk/channel-message.ts is now a @deprecated re-export barrel pointing at channel-outbound / channel-inbound; channel.turn runtime aliases were removed and the old /plugins/sdk-channel-turn doc page redirects to Channel inbound API. New plugin code should target channel-outbound and channel-inbound directly.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
Where the implementation diverged from the original design
The design sketch below never shipped as literally described. Record kept for historical accuracy; do not treat these type names as current API.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- No
MessageOrigin/shouldDropOpenClawEcho. The original plan called - No unified
core.messages.receive/send/live/statenamespace. The - **No generic
ChannelMessage/MessageTarget/MessageRelation - Ack policy names differ from the sketch. Shipped:
- **
DurableFinalDeliveryRequirementMapcapability keys replaced the sketched
Concrete migration hazards (still relevant)
These channel-specific side effects predate the refactor and must keep working through the new send paths. They are not hypothetical: each is implemented and load-bearing today.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- iMessage (
extensions/imessage/src/monitor/echo-cache.ts, - Tlon (
extensions/tlon/src/monitor/index.ts): appends an optional model - Discord and other prepared dispatchers already own direct delivery and
- Telegram silent fallback delivery must deliver the whole projected
- LINE, Zalo, Nostr, and similar helper paths can have reply-token
- Direct-DM helpers can have a reply callback that is the only correct
Failure classification
Adapters classify transport failures into DeliveryFailureKind-style closed categories (transient, rate limit, auth, permission, not found, invalid payload, conflict, cancelled, unknown). Core policy:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Retry transient and rate-limit failures.
- Do not retry invalid-payload failures unless a render fallback exists.
- Do not retry auth or permission failures until configuration changes.
- On not-found, let live finalization fall back from edit to a fresh send when
- On conflict, use receipt/idempotency state to decide whether the message
- Any error after the platform call may have succeeded but before receipt
Open questions
runner with a fully durable polling source that controls platform-level redelivery, not only OpenClaw's persisted restart watermark (safeCompletedUpdateId).
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Whether Telegram should eventually replace the grammY (
1.43.0) polling - Whether live preview state should live in the same record as the final send
- Whether gateway-failure echo suppression in shared bot-enabled rooms needs
- Which channels have native origin/metadata support for cross-bot echo
관련 문서
주요 항목:
- Messages
- Streaming and chunking
- Progress drafts
- Retry policy
- Channel outbound API
- Channel inbound API
실습 체크리스트
- 공식 문서와 로컬 버전을 대조합니다:
https://docs.openclaw.ai/concepts/message-lifecycle-refactor - 관련 CLI는
openclaw --help및 하위 명령--help로 옵션을 확인합니다. - 설정 변경 시
openclaw config/openclaw doctor로 유효성을 검사합니다. - Gateway·채널·플러그인 변경 후에는 필요 시 Gateway를 재시작합니다.
자주 쓰는 명령·설정 예시
Telegram polling update acked
-> assistant final text exists
-> process restarts before sendMessage succeeds
-> final response is lost
관련 링크
이 가이드는 공식 문서를 한국어 학습용으로 재구성한 것입니다. 옵션 기본값·플래그 이름은 설치 버전에 따라 달라질 수 있습니다.