Cron jobs (Gateway scheduler)
Cron hay Heartbeat? Xem Cron vs Heartbeat để biết khi nào nên dùng cái nào.
Cron là scheduler tích hợp sẵn của Gateway. Nó lưu trữ các jobs, đánh thức agent đúng thời điểm, và có thể gửi kết quả về chat.
Nếu các bạn muốn “chạy cái này mỗi sáng” hoặc “nhắc agent sau 20 phút”, thì cron chính là cơ chế để làm việc đó.
TL;DR
- Cron chạy bên trong Gateway (không chạy trong model).
- Jobs được lưu dưới
~/.openclaw/cron/nên khi restart sẽ không mất lịch. - Hai kiểu thực thi:
- Main session: đưa system event vào hàng đợi, rồi chạy ở heartbeat tiếp theo.
- Isolated: chạy một agent turn riêng trong
cron:<jobId>, có thể gửi output ra ngoài.
- Wakeups là tính năng chính thức: một job có thể yêu cầu “wake now” hoặc “next heartbeat”.
Bắt đầu nhanh (thực hành)
Tạo một reminder chạy một lần, kiểm tra nó tồn tại, và chạy ngay:
openclaw cron add \
--name "Reminder" \
--at "2026-02-01T16:00:00Z" \
--session main \
--system-event "Reminder: check the cron docs draft" \
--wake now \
--delete-after-run
openclaw cron list
openclaw cron run <job-id> --force
openclaw cron runs --id <job-id>
Lên lịch một isolated job lặp lại với delivery:
openclaw cron add \
--name "Morning brief" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize overnight updates." \
--deliver \
--channel slack \
--to "channel:C1234567890"
Tool-call tương đương (Gateway cron tool)
Để xem các JSON shapes chuẩn và ví dụ, xem JSON schema for tool calls.
Cron jobs được lưu ở đâu
Cron jobs được lưu trên Gateway host tại ~/.openclaw/cron/jobs.json theo mặc định. Gateway load file vào bộ nhớ và ghi lại khi có thay đổi, nên việc chỉnh sửa thủ công chỉ an toàn khi Gateway đã dừng. Nên dùng openclaw cron add/edit hoặc cron tool call API để thay đổi.
Tổng quan dễ hiểu
Hãy nghĩ về một cron job như: khi nào chạy + làm gì.
-
Chọn lịch trình
- Reminder chạy một lần →
schedule.kind = "at"(CLI:--at) - Job lặp lại →
schedule.kind = "every"hoặcschedule.kind = "cron" - Nếu ISO timestamp không có timezone, nó sẽ được coi là UTC.
- Reminder chạy một lần →
-
Chọn nơi chạy
sessionTarget: "main"→ chạy trong heartbeat tiếp theo với main context.sessionTarget: "isolated"→ chạy một agent turn riêng trongcron:<jobId>.
-
Chọn payload
- Main session →
payload.kind = "systemEvent" - Isolated session →
payload.kind = "agentTurn"
- Main session →
Tùy chọn: deleteAfterRun: true sẽ xóa các one-shot jobs thành công khỏi store.
Các khái niệm
Jobs
Một cron job là một bản ghi được lưu trữ với:
- một schedule (khi nào nó chạy),
- một payload (nó làm gì),
- delivery tùy chọn (output sẽ được gửi đến đâu).
- agent binding tùy chọn (
agentId): chạy job dưới một agent cụ thể; nếu thiếu hoặc không tìm thấy, gateway sẽ dùng agent mặc định.
Jobs được định danh bằng một jobId ổn định (dùng bởi CLI/Gateway APIs). Trong agent tool calls, jobId là chuẩn; id cũ vẫn được chấp nhận để tương thích. Jobs có thể tự động xóa sau khi chạy thành công một lần thông qua deleteAfterRun: true.
Schedules
Cron hỗ trợ ba loại schedule:
at: timestamp chạy một lần (ms kể từ epoch). Gateway chấp nhận ISO 8601 và chuyển sang UTC.every: khoảng thời gian cố định (ms).cron: biểu thức cron 5 trường với timezone IANA tùy chọn.
Biểu thức cron dùng croner. Nếu timezone bị bỏ qua, timezone local của Gateway host sẽ được dùng.
Main vs isolated execution
Main session jobs (system events)
Main jobs đưa một system event vào hàng đợi và có thể đánh thức heartbeat runner. Chúng phải dùng payload.kind = "systemEvent".
wakeMode: "next-heartbeat"(mặc định): event chờ heartbeat được lên lịch tiếp theo.wakeMode: "now": event kích hoạt heartbeat chạy ngay lập tức.
Đây là lựa chọn tốt nhất khi các bạn muốn heartbeat prompt bình thường + main-session context. Xem Heartbeat.
Isolated jobs (dedicated cron sessions)
Isolated jobs chạy một agent turn riêng trong session cron:<jobId>.
Các hành vi chính:
- Prompt được thêm prefix
[cron:<jobId> <job name>]để dễ theo dõi. - Mỗi lần chạy bắt đầu một session id mới (không có lịch sử hội thoại trước đó).
- Một tóm tắt được đăng vào main session (prefix
Cron, có thể cấu hình). wakeMode: "now"kích hoạt heartbeat ngay sau khi đăng tóm tắt.- Nếu
payload.deliver: true, output được gửi đến một channel; nếu không nó sẽ ở nội bộ.
Dùng isolated jobs cho các tác vụ ồn ào, thường xuyên, hoặc “công việc nền” mà không nên làm spam lịch sử chat chính.
Payload shapes (cái gì chạy)
Hai loại payload được hỗ trợ:
systemEvent: chỉ dành cho main-session, được định tuyến qua heartbeat prompt.agentTurn: chỉ dành cho isolated-session, chạy một agent turn riêng.
Các trường agentTurn phổ biến:
message: text prompt bắt buộc.model/thinking: overrides tùy chọn (xem bên dưới).timeoutSeconds: timeout override tùy chọn.deliver:trueđể gửi output đến một channel target.channel:lasthoặc một channel cụ thể.to: target cụ thể của channel (phone/chat/channel id).bestEffortDeliver: tránh làm job thất bại nếu delivery thất bại.
Các tùy chọn isolation (chỉ cho session=isolated):
postToMainPrefix(CLI:--post-prefix): prefix cho system event trong main.postToMainMode:summary(mặc định) hoặcfull.postToMainMaxChars: số ký tự tối đa khipostToMainMode=full(mặc định 8000).
Model và thinking overrides
Isolated jobs (agentTurn) có thể override model và thinking level:
model: Provider/model string (ví dụ:anthropic/claude-sonnet-4-20250514) hoặc alias (ví dụ:opus)thinking: Thinking level (off,minimal,low,medium,high,xhigh; chỉ GPT-5.2 + Codex models)
Lưu ý: Các bạn có thể set model trên main-session jobs, nhưng nó sẽ thay đổi model của main session chung. Mình khuyên chỉ dùng model overrides cho isolated jobs để tránh context shifts không mong muốn.
Thứ tự ưu tiên:
- Job payload override (cao nhất)
- Hook-specific defaults (ví dụ:
hooks.gmail.model) - Agent config default
Delivery (channel + target)
Isolated jobs có thể gửi output đến một channel. Job payload có thể chỉ định:
channel:whatsapp/telegram/discord/slack/mattermost(plugin) /signal/imessage/lastto: recipient target cụ thể của channel
Nếu channel hoặc to bị bỏ qua, cron có thể fallback về “last route” của main session (nơi cuối cùng agent đã trả lời).
Lưu ý về delivery:
- Nếu
tođược set, cron tự động gửi output cuối cùng của agent ngay cả khideliverbị bỏ qua. - Dùng
deliver: truekhi các bạn muốn last-route delivery mà không cótorõ ràng. - Dùng
deliver: falseđể giữ output nội bộ ngay cả khi cóto.
Nhắc nhở về định dạng target:
- Slack/Discord/Mattermost (plugin) targets nên dùng prefixes rõ ràng (ví dụ:
channel:<id>,user:<id>) để tránh nhầm lẫn. - Telegram topics nên dùng dạng
:topic:(xem bên dưới).
Telegram delivery targets (topics / forum threads)
Telegram hỗ trợ forum topics qua message_thread_id. Để cron delivery, các bạn có thể encode topic/thread vào trường to:
-1001234567890(chỉ chat id)-1001234567890:topic:123(khuyên dùng: topic marker rõ ràng)-1001234567890:123(viết tắt: numeric suffix)
Prefixed targets như telegram:... / telegram:group:... cũng được chấp nhận:
telegram:group:-1001234567890:topic:123
JSON schema for tool calls
Dùng các shapes này khi gọi Gateway cron.* tools trực tiếp (agent tool calls hoặc RPC). CLI flags chấp nhận human durations như 20m, nhưng tool calls dùng epoch milliseconds cho atMs và everyMs (ISO timestamps được chấp nhận cho at times).
cron.add params
One-shot, main session job (system event):
{
"name": "Reminder",
"schedule": { "kind": "at", "atMs": 1738262400000 },
"sessionTarget": "main",
"wakeMode": "now",
"payload": { "kind": "systemEvent", "text": "Reminder text" },
"deleteAfterRun": true
}
Recurring, isolated job với delivery:
{
"name": "Morning brief",
"schedule": { "kind": "cron", "expr": "0 7 * * *", "tz": "America/Los_Angeles" },
"sessionTarget": "isolated",
"wakeMode": "next-heartbeat",
"payload": {
"kind": "agentTurn",
"message": "Summarize overnight updates.",
"deliver": true,
"channel": "slack",
"to": "channel:C1234567890",
"bestEffortDeliver": true
},
"isolation": { "postToMainPrefix": "Cron", "postToMainMode": "summary" }
}
Lưu ý:
schedule.kind:at(atMs),every(everyMs), hoặccron(expr,tztùy chọn).atMsvàeveryMslà epoch milliseconds.sessionTargetphải là"main"hoặc"isolated"và phải khớp vớipayload.kind.- Các trường tùy chọn:
agentId,description,enabled,deleteAfterRun,isolation. wakeModemặc định là"next-heartbeat"khi bị bỏ qua.
cron.update params
{
"jobId": "job-123",
"patch": {
"enabled": false,
"schedule": { "kind": "every", "everyMs": 3600000 }
}
}
Lưu ý:
jobIdlà chuẩn;idđược chấp nhận để tương thích.- Dùng
agentId: nulltrong patch để xóa agent binding.
cron.run và cron.remove params
{ "jobId": "job-123", "mode": "force" }
{ "jobId": "job-123" }
Storage & history
- Job store:
~/.openclaw/cron/jobs.json(Gateway-managed JSON). - Run history:
~/.openclaw/cron/runs/<jobId>.jsonl(JSONL, tự động dọn dẹp). - Override store path:
cron.storetrong config.
Configuration
{
cron: {
enabled: true, // mặc định true
store: "~/.openclaw/cron/jobs.json",
maxConcurrentRuns: 1, // mặc định 1
},
}
Tắt cron hoàn toàn:
cron.enabled: false(config)OPENCLAW_SKIP_CRON=1(env)
CLI quickstart
One-shot reminder (UTC ISO, tự động xóa sau khi thành công):
openclaw cron add \
--name "Send reminder" \
--at "2026-01-12T18:00:00Z" \
--session main \
--system-event "Reminder: submit expense report." \
--wake now \
--delete-after-run
One-shot reminder (main session, wake ngay lập tức):
openclaw cron add \
--name "Calendar check" \
--at "20m" \
--session main \
--system-event "Next heartbeat: check calendar." \
--wake now
Recurring isolated job (gửi đến WhatsApp):
openclaw cron add \
--name "Morning status" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize inbox + calendar for today." \
--deliver \
--channel whatsapp \
--to "+15551234567"
Recurring isolated job (gửi đến Telegram topic):
openclaw cron add \
--name "Nightly summary (topic)" \
--cron "0 22 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize today; send to the nightly topic." \
--deliver \
--channel telegram \
--to "-1001234567890:topic:123"
Isolated job với model và thinking override:
openclaw cron add \
--name "Deep analysis" \
--cron "0 6 * * 1" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Weekly deep analysis of project progress." \
--model "opus" \
--thinking high \
--deliver \
--channel whatsapp \
--to "+15551234567"
Agent selection (multi-agent setups):
# Gắn job vào agent "ops" (fallback về default nếu agent đó thiếu)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops
# Chuyển hoặc xóa agent trên một job hiện có
openclaw cron edit <jobId> --agent ops
openclaw cron edit <jobId> --clear-agent
Manual run (debug):
openclaw cron run <jobId> --force
Chỉnh sửa một job hiện có (patch fields):
openclaw cron edit <jobId> \
--message "Updated prompt" \
--model "opus" \
--thinking low
Run history:
openclaw cron runs --id <jobId> --limit 50
System event ngay lập tức mà không tạo job:
openclaw system event --mode now --text "Next heartbeat: check battery."
Gateway API surface
cron.list,cron.status,cron.add,cron.update,cron.removecron.run(force hoặc due),cron.runsĐể tạo system events ngay lập tức mà không cần job, dùngopenclaw system event.
Troubleshooting
”Không có gì chạy”
- Kiểm tra cron đã được bật:
cron.enabledvàOPENCLAW_SKIP_CRON. - Kiểm tra Gateway đang chạy liên tục (cron chạy bên trong Gateway process).
- Với
cronschedules: xác nhận timezone (--tz) so với host timezone.
Telegram gửi sai chỗ
- Với forum topics, dùng
-100…:topic:<id>để rõ ràng và không nhầm lẫn. - Nếu các bạn thấy prefixes
telegram:...trong logs hoặc “last route” targets được lưu, đó là bình thường; cron delivery chấp nhận chúng và vẫn parse topic IDs đúng.