How Kimi CLI Handles DMail (Delayed Mail) for Checkpointed Replies
Kimi CLI implements delayed mail through a DenwaRenji manager that validates, stores, and later attaches a single pending DMail to a specific checkpoint after the current tool execution cycle completes.
Kimi CLI supports DMail (delayed mail) to enable AI agents to send messages that retroactively attach to previously created checkpoints. According to the MoonshotAI/kimi-cli source code, this mechanism centers on a validation manager and a tool interface that decouples message recording from UI attachment, ensuring deterministic ordering even when multiple tool calls execute in a single turn.
Core Architecture
The DMail system relies on three coordinated components defined in src/kimi_cli/soul/denwarenji.py and src/kimi_cli/tools/dmail/__init__.py:
DMail Model
The DMail Pydantic schema (lines 6-10 in denwarenji.py) defines the payload structure for delayed messages. It requires two fields:
message: The text content to attach to the checkpointcheckpoint_id: The integer index of the target checkpoint
from kimi_cli.soul.denwarenji import DMail
mail = DMail(
message="Analysis complete. See attached files.",
checkpoint_id=2
)
DenwaRenji Manager
The DenwaRenji class (lines 15-40 in denwarenji.py) acts as the central gatekeeper. It maintains:
_pending_dmail: Storage for a single in-flight DMail_n_checkpoints: The current checkpoint count for validationset_n_checkpoints(n): Method called by the soul to update checkpoint awarenesssend_dmail(dmail): Validation and storage methodfetch_pending_dmail(): Retrieval method called by the main loop
from kimi_cli.soul.denwarenji import DenwaRenji
renji = DenwaRenji()
renji.set_n_checkpoints(5) # Inform manager of available checkpoints
SendDMail Tool
The SendDMail tool class exposes DMail capability to LLM-generated tool calls. Located in src/kimi_cli/tools/dmail/__init__.py, this tool receives the DMail payload and forwards it to the DenwaRenji manager without directly modifying UI state.
from kimi_cli.tools.dmail import SendDMail
send_tool = SendDMail(renji)
await send_tool(mail) # May raise DenwaRenjiError on validation failure
Execution Flow
Kimi CLI processes delayed mail through four distinct phases:
-
Checkpoint Registration – When the soul creates checkpoints (e.g., after tool execution), it calls
DenwaRenji.set_n_checkpoints(n)to synchronize the manager with the current checkpoint count. -
LLM Tool Invocation – The LLM requests
SendDMailwith a JSON payload matching theDMailschema, specifying both the message text and target checkpoint index. -
Validation and Storage –
SendDMail.__call__invokesDenwaRenji.send_dmail(dmail), which performs three strict validations:- Only one pending DMail may exist (
_pending_dmail is None) checkpoint_idmust be non-negativecheckpoint_idmust be less than_n_checkpoints
Any violation raises
DenwaRenjiError, propagating as a tool error to the LLM. - Only one pending DMail may exist (
-
Runtime Attachment – After the current turn completes,
KimiSoul(the core loop insrc/kimi_cli/soul/kimisoul.py) callsDenwaRenji.fetch_pending_dmail(). If a pending mail exists, the manager clears the slot and returns the DMail for attachment to the specified checkpoint, which then appears in the UI reply.
Validation Guarantees
The DenwaRenji implementation enforces critical architectural constraints:
-
Single-Mail Guarantee: The manager rejects multiple DMail requests within the same turn. If
send_dmail()is called while_pending_dmailis populated, it raisesDenwaRenjiError, preventing race conditions. -
Checkpoint Bounds Checking: The manager validates that
checkpoint_idfalls within the valid range[0, _n_checkpoints), ensuring agents cannot reference non-existent checkpoints. -
Side-Effect-Free Tool Execution:
SendDMailonly records the request; actual checkpoint attachment happens later in the soul's main loop, keeping tool execution deterministic and free of UI side effects.
Code Examples
Manually Invoking the Tool
from kimi_cli.tools.dmail import SendDMail
from kimi_cli.soul.denwarenji import DenwaRenji, DMail
# Initialize the manager (normally created by the runtime)
renji = DenwaRenji()
renji.set_n_checkpoints(5) # Assume 5 checkpoints exist
# Build the DMail payload
mail = DMail(message="Deferred answer", checkpoint_id=3)
# Call the tool – this registers the pending DMail
send_tool = SendDMail(renji)
await send_tool(mail) # Raises DenwaRenjiError if invalid
Fetching Pending Mail
# Later in the main loop
pending = renji.fetch_pending_dmail()
if pending:
# Attach pending.message to checkpoint pending.checkpoint_id
attach_to_checkpoint(pending.checkpoint_id, pending.message)
Testing Validation Logic
def test_send_dmail_validation(send_dmail_tool: SendDMail):
# Valid DMail
await send_dmail_tool(DMail(message="ok", checkpoint_id=0))
# Duplicate DMail in same turn → error
with pytest.raises(DenwaRenjiError):
await send_dmail_tool(DMail(message="second", checkpoint_id=0))
Summary
- Kimi CLI DMail uses a three-component system: the
DMailschema,DenwaRenjimanager, andSendDMailtool. - Single-mail enforcement prevents concurrent delayed messages within a single turn, ensuring deterministic ordering.
- Validation occurs at two levels: schema validation via Pydantic and runtime validation of checkpoint bounds against
set_n_checkpoints(). - Decoupled attachment means tools record intent in
src/kimi_cli/soul/denwarenji.pywhileKimiSoulhandles actual UI integration after the turn completes. - Error handling uses
DenwaRenjiErrorto communicate validation failures back to the LLM as tool errors.
Frequently Asked Questions
What is DMail in Kimi CLI?
DMail (delayed mail) is a mechanism that allows an AI agent to send a message attached to a previously created checkpoint rather than the current conversation turn. This enables agents to defer status updates or summaries until after tool execution completes, implemented through the DenwaRenji manager in src/kimi_cli/soul/denwarenji.py.
How does Kimi CLI prevent multiple delayed mails in one turn?
The DenwaRenji class enforces a single-mail guarantee by checking if _pending_dmail is None before accepting new mail. If the LLM attempts to send a second DMail before the current turn completes, send_dmail() raises a DenwaRenjiError, which propagates as a tool execution error.
Can a DMail reference any checkpoint in the conversation history?
No. The checkpoint_id must be non-negative and strictly less than the current number of checkpoints tracked by DenwaRenji._n_checkpoints. The soul updates this count via set_n_checkpoints() during checkpoint creation, ensuring agents can only reference checkpoints that actually exist in the current session.
Why is DMail attachment decoupled from the tool execution?
The SendDMail tool in src/kimi_cli/tools/dmail/__init__.py only records the request to DenwaRenji, while the actual attachment to the checkpoint UI happens later when KimiSoul calls fetch_pending_dmail(). This separation ensures tool execution remains side-effect-free and deterministic, allowing the runtime to control exactly when and how deferred messages appear in the conversation flow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →