Coding

Delivery Mirror

Try it

Mirror cron/script deliveries into agent session transcripts so scheduled and isolated-cron messages persist in context. Session continuity, no core changes.

What it does

Mirror cron/script deliveries into agent session transcripts so scheduled and isolated-cron messages persist in context. Session continuity, no core changes.

The skill document

delivery-mirror

A deterministic, no-core-changes helper for the gap between delivering a message and the agent remembering it.

All commands: bash "{baseDir}/scripts/send-mirrored.sh"

The problem

--command crons and external scripts call openclaw message send … directly. The message reaches the chat — but it bypasses the agent's run loop, so the agent's session JSONL never records it. Next time that agent wakes in the chat/topic, it has no idea the message was ever sent. Classic case: calendar agenda dispatchers and reminder scripts whose sessions run with delivery.mode: none and send via CLI.

Messages delivered through OpenClaw's own delivery layer (agent replies, isolated/cron agentTurn delivery) don't have this problem: that layer passes a mirror context and core calls appendAssistantMessageToSessionTranscript, which writes a delivery-mirror row. A plain openclaw message send from a script passes no mirror context — and the CLI has no flag to set one — so nothing is mirrored. This skill closes that one gap, without touching OpenClaw core.

What it does

  1. Sends the message exactly as before (openclaw message send … --json).
  2. On success, resolves the owning agent's current session file from agents//sessions/sessions.json (.sessionFile — follows compaction rotation).
  3. Appends one delivery-mirror assistant row to that transcript — the same shape core produces via appendAssistantMessageToSessionTranscript (provider: "openclaw", model: "delivery-mirror", zeroed usage, stopReason: "stop"), parentId-chained to the last record. It also attaches the openclawDeliveryMirror: {kind:"channel-final"} marker that core adds optionally on real deliveries, with sourceMessageId when the send returns one.
  4. Optional idempotency: --idem skips the whole op if that key was already handled (guards against double-delivery on cron retry).

Mirroring is best-effort: if the session can't be resolved, delivery still succeeded and the helper exits 0 with a warning — it never fails a send because of a mirror problem.

Why a skill, not a plugin

A true runtime plugin would mean changing/extending OpenClaw core. This stays a self-contained script you drop next to your other command-cron scripts, so it works on any OpenClaw host and upgrades independently.

Permissions & write scope

This skill performs local filesystem writes and runs the openclaw CLI. Exactly what it touches, and nothing else:

  • Reads: /agents//sessions/sessions.json — only to resolve the target session's sessionFile.
  • Appends (never edits or deletes): one JSONL line to that sessionFile — the agent's own transcript.
  • Writes: idempotency state …/delivery-mirror/state/.seen, a log …/delivery-mirror/mirror.log, and advisory lock files (*.mirror.lock, .seen.lock).
  • Executes: openclaw message send to deliver the message.
  • Does NOT: make network calls of its own, run any model, read ambient environment for data (all inputs come from this script's flags and are passed to the embedded Python as positional argv, not env), or modify/delete any existing transcript record.

--openclaw-home confines every path above — point it at a scratch dir to dry-run safely. Treat the caller (your cron/script) as the trust source: message text is stored verbatim. Full threat model in SECURITY.md.

Usage

scripts/send-mirrored.sh \
  --agent ula \                       # agent id that owns the session (sessions dir)
  --account ula \                     # channel account for send (defaults to --agent)
  --to -1003971971641 \               # telegram chat id
  --thread-id 131 \                   # telegram forum topic (omit for non-forum)
  --source agenda-dispatch \          # label for logs / tracing
  --idem "agenda:131:$(date +%F):morning" \  # optional dedupe key
  --message "$MSG"

Message input: --message "…", --message-file PATH, or --message-file - (stdin).

In a --command cron

Replace a bare openclaw message send … with:

/home/opc/.openclaw/skills/delivery-mirror/scripts/send-mirrored.sh \
  --agent ula --account ula --to -1003971971641 --thread-id 131 \
  --source agenda-dispatch --message "$MSG"

Flags

flagmeaning
--message / --message-filemessage text (file or - for stdin)
--tochannel target (telegram chat id) — required
--agentagent id owning the session — required
--accountchannel account id for send (default: --agent)
--channelchannel (default telegram)
--thread-idtelegram forum topic id
--session-keyexplicit session key (else auto-resolved)
--sourcelabel recorded in the helper log (not in the row)
--idemidempotency key; skip if already handled (exit 3)
--openclaw-homeOpenClaw home (default $OPENCLAW_HOME or ~/.openclaw)
--openclaw-binopenclaw binary (default openclaw on PATH)
--dry-runprint the plan, do nothing
--no-sendmirror only (testing)
--no-mirrorsend only (= plain send)

Exit codes

codemeaning
0delivered (mirrored, or mirror skipped best-effort with warning)
2bad usage / missing required args
3idempotency: --idem key already handled, nothing done
4send failed (nothing mirrored)

Session resolution

The helper finds the transcript by, in order: explicit --session-key; auto-constructed key (agent:::group::topic:, then :group:, then :direct:); finally a scan of sessions.json matching deliveryContext.to / route.target.to (substring on --to) + thread id. It always appends to the entry's sessionFile, so it follows compaction rotation automatically.

Caveats (read before trusting it blindly)

  • It reproduces a core row from bash. The delivery-mirror row is a first-class core concept (written by appendAssistantMessageToSessionTranscript, matched by core's isDeliveryMirror predicate, which keys only on provider+model). The coupling is only that we hand-append the JSONL instead of calling that internal function — no CLI or tool exposes it. We reproduce core's usage shape and attach the optional openclawDeliveryMirror marker; the append is newline-safe; re-verify after a major OpenClaw upgrade. --source is recorded only in the helper's log, not in the row.
  • Concurrency. Appends are serialized with flock on .mirror.lock. The gateway may not take that lock, so avoid mirroring into a topic while its agent is actively mid-run; dispatcher-style schedules (agent idle) are the safe, intended case.
  • State. Idempotency keys live in /delivery-mirror/state/.seen; log in /delivery-mirror/mirror.log.

Test

scripts/send-mirrored.sh --dry-run  --agent X --to  --thread-id  --message "hi"
scripts/send-mirrored.sh --no-send  --agent X --to  --thread-id  --message "hi"  # mirror only

Related skills

Isolated crons, scripts, reminders, and status checkers sent it but the agent forgot? Write deliveries into the session transcript.

4 installs1 stars

Use the OpenClaw cron tool for scheduling reminders, delayed follow-ups, and recurring periodic checks. Covers one-shot and recurring schedules, session targeting, delivery modes, and wake events.

Proactive cron job health monitoring, failure detection, and auto-repair delegation. Triggers: 'cron failed', 'cron health', 'fix cron', 'consecutive errors

2 installs

Run a hosted agent on a cron schedule — daily digests, uptime monitors, recurring scrapes, periodic reports — that fire on their own and bill exactly-once wi...

1 installs

实现『用 A 会话调起/注入 B 会话』的能力(sessions_send):cron 任务、外部进程或另一会话可向目标会话注入消息,唤醒其 agent 带完整上下文继续推进。适用于 OpenClaw 与 QClaw(同内核)。含 Windows 下 visibility 配置检查/修复、正确重启网关(SIGUSR1 bug 规避)、cron job 设计。当用户要求「定时任务完成后回到本对话继续推进」「cron 回调当前会话」「让任务结果上报到同一对话」时使用。

1 installs

Schedule jobs that pick up current prompt, policy, delivery, and model changes on every run.

21 installs