Inferred commitments are marked sent in DB but not actually delivered to the active session
Environment
- OpenClaw version: 2026.6.1
- Node: v24.13.1
- OS: Linux ubuntu 6.8.0-124-generic (x64)
- Channel: Telegram (tested with single-user direct chat)
Summary
Inferred commitments in the commitments DB are being marked with status: "sent" and a sentAtMs timestamp, but the corresponding message never reaches the user in the active chat session. The delivery chain appears to break between the "queue" step and the actual outbound channel send.
Steps to reproduce
- Set up an agent that creates inferred commitments (e.g.
kind: "event_check_in", source: "inferred_user_context")
- Provide a
dueWindow and wait for it to lapse
- Observe the commitment being transitioned to
status: "sent" with a sentAtMs set
- Check the user's chat session — the message is not there
Expected behaviour
When a commitment is status: "sent", the suggested text should be delivered to the user via the chat session specified by sessionKey / to.
Actual behaviour
The commitment is queued and the DB status is updated, but no message is pushed to the user's chat. The commitment effectively disappears.
Evidence / logs
DB observation
A commitments DB snapshot taken at the time of this report contained 43 entries with the following status counts:
{'dismissed': 13, 'sent': 11, 'expired': 12, 'pending': 6, 'delivered': 1}
Note: only 1 of 43 commitments was actually delivered to the user. The 11 sent commitments are effectively orphaned — the status flipped without reaching the chat.
Example commitment object (truncated)
{
"id": "cm_…",
"kind": "event_check_in",
"source": "inferred_user_context",
"status": "sent",
"suggestedText": "<example check-in text>",
"dueWindow": { "earliestMs": 1781082000000, "latestMs": 1781092800000, "timezone": "Europe/London" },
"createdAtMs": 1781022478760,
"sentAtMs": 1781085502537,
"attempts": 1,
"lastAttemptAtMs": 1781085502537
}
dueWindow.latestMs: 11:00 BST (Wed)
sentAtMs: 12:18 BST (Wed) — 1h 18m after the due window closed
- No corresponding outbound Telegram message was found in the active session transcript for the active session at that time.
Delivery mechanism confirmation
The heartbeat system IS functioning for other content types. For example, one agent's "Daily check-in" cron (schedule: 30 10 * * 1-7 Europe/London) runs at 10:30am daily and has a confirmed delivery track record (lastDelivered: true, lastDeliveryStatus: "delivered"). Multiple other scheduled jobs in the same environment also show successful delivery to Telegram.
This confirms the bug is specific to inferred commitments — the delivery mechanism itself works for other content, but inferred commitments are getting orphaned in the queue.
Workaround applied
Modified each agent's HEARTBEAT.md to add an explicit step 2 that queries the commitments DB and delivers pending/sent commitments. The exact text added:
### 2. Send OpenClaw commitments:
— Query `/home/openclaw/.openclaw/commitments/commitments.json` for any commitment matching ALL of:
- `agentId` = "<this agent>"
- `to` = "telegram:<chat_id>"
- `status` = "pending" OR "sent" (never send "dismissed", "delivered", or "acknowledged")
- `dueWindow.latestMs` ≤ now
— For each match, decide: is it **still relevant** and **not an issue previously addressed** (resolved already, overtaken by events, superseded, etc.)?
- **Yes** → send `suggestedText` via `message` tool (action=send, target=telegram:<chat_id>), then update that commitment's `status` to "delivered" in commitments.json
- **No** → do not deliver; mark the commitment as `dismissed` in commitments.json with a short reason (e.g. "stale — issue resolved elsewhere")
— If nothing matches → skip this step
— If anything fails or you're unsure, fall back to HEARTBEAT_OK and log the issue
This works as a stopgap, but the underlying delivery contract should not depend on each agent re-implementing it via heartbeat polling.
Proposed fix
- Make
status: "sent" honest — only flip a commitment to sent once the channel send has actually been confirmed, not on queue/attempt.
- Drive delivery from the active session — when an active session is idle (between user turns), the runtime should check for queued commitments to that session and deliver them, rather than relying on heartbeat prompts to discover them.
- Expose a delivery health metric — e.g.
deliveredAtMs vs sentAtMs deltas, surfaced per agent, so this kind of silent drop is visible in normal operation.
Severity
Medium — silently breaks a user-facing feature (proactive check-ins) without surfacing any error to the user or the agent.
Affected versions
Reproduced on 2026.6.1. Likely affects all versions that introduced the inferred-commitments extractor.
Inferred commitments are marked
sentin DB but not actually delivered to the active sessionEnvironment
Summary
Inferred commitments in the commitments DB are being marked with
status: "sent"and asentAtMstimestamp, but the corresponding message never reaches the user in the active chat session. The delivery chain appears to break between the "queue" step and the actual outbound channel send.Steps to reproduce
kind: "event_check_in",source: "inferred_user_context")dueWindowand wait for it to lapsestatus: "sent"with asentAtMssetExpected behaviour
When a commitment is
status: "sent", the suggested text should be delivered to the user via the chat session specified bysessionKey/to.Actual behaviour
The commitment is queued and the DB status is updated, but no message is pushed to the user's chat. The commitment effectively disappears.
Evidence / logs
DB observation
A commitments DB snapshot taken at the time of this report contained 43 entries with the following status counts:
Note: only 1 of 43 commitments was actually delivered to the user. The 11
sentcommitments are effectively orphaned — the status flipped without reaching the chat.Example commitment object (truncated)
{ "id": "cm_…", "kind": "event_check_in", "source": "inferred_user_context", "status": "sent", "suggestedText": "<example check-in text>", "dueWindow": { "earliestMs": 1781082000000, "latestMs": 1781092800000, "timezone": "Europe/London" }, "createdAtMs": 1781022478760, "sentAtMs": 1781085502537, "attempts": 1, "lastAttemptAtMs": 1781085502537 }dueWindow.latestMs: 11:00 BST (Wed)sentAtMs: 12:18 BST (Wed) — 1h 18m after the due window closedDelivery mechanism confirmation
The heartbeat system IS functioning for other content types. For example, one agent's "Daily check-in" cron (schedule:
30 10 * * 1-7Europe/London) runs at 10:30am daily and has a confirmed delivery track record (lastDelivered: true,lastDeliveryStatus: "delivered"). Multiple other scheduled jobs in the same environment also show successful delivery to Telegram.This confirms the bug is specific to inferred commitments — the delivery mechanism itself works for other content, but inferred commitments are getting orphaned in the queue.
Workaround applied
Modified each agent's
HEARTBEAT.mdto add an explicit step 2 that queries the commitments DB and delivers pending/sent commitments. The exact text added:This works as a stopgap, but the underlying delivery contract should not depend on each agent re-implementing it via heartbeat polling.
Proposed fix
status: "sent"honest — only flip a commitment tosentonce the channel send has actually been confirmed, not on queue/attempt.deliveredAtMsvssentAtMsdeltas, surfaced per agent, so this kind of silent drop is visible in normal operation.Severity
Medium — silently breaks a user-facing feature (proactive check-ins) without surfacing any error to the user or the agent.
Affected versions
Reproduced on
2026.6.1. Likely affects all versions that introduced the inferred-commitments extractor.