mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 00:42:20 +00:00
feat(cron): support custom session IDs and auto-bind to current session (#16511)
feat(cron): support persistent session targets for cron jobs (#9765) Add support for `sessionTarget: "current"` and `session:<id>` so cron jobs can bind to the creating session or a persistent named session instead of only `main` or ephemeral `isolated` sessions. Also: - preserve custom session targets across reloads and restarts - update gateway validation and normalization for the new target forms - add cron coverage for current/custom session targets and fallback behavior - fix merged CI regressions in Discord and diffs tests - add a changelog entry for the new cron session behavior Co-authored-by: kkhomej33-netizen <kkhomej33-netizen@users.noreply.github.com> Co-authored-by: ImLukeF <92253590+ImLukeF@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
61d171ab0b
commit
e7d9648fba
@@ -25,7 +25,9 @@ Troubleshooting: [/automation/troubleshooting](/automation/troubleshooting)
|
||||
- Jobs persist under `~/.openclaw/cron/` so restarts don’t lose schedules.
|
||||
- Two execution styles:
|
||||
- **Main session**: enqueue a system event, then run on the next heartbeat.
|
||||
- **Isolated**: run a dedicated agent turn in `cron:<jobId>`, with delivery (announce by default or none).
|
||||
- **Isolated**: run a dedicated agent turn in `cron:<jobId>` or a custom session, with delivery (announce by default or none).
|
||||
- **Current session**: bind to the session where the cron is created (`sessionTarget: "current"`).
|
||||
- **Custom session**: run in a persistent named session (`sessionTarget: "session:custom-id"`).
|
||||
- Wakeups are first-class: a job can request “wake now” vs “next heartbeat”.
|
||||
- Webhook posting is per job via `delivery.mode = "webhook"` + `delivery.to = "<url>"`.
|
||||
- Legacy fallback remains for stored jobs with `notify: true` when `cron.webhook` is set, migrate those jobs to webhook delivery mode.
|
||||
@@ -86,6 +88,14 @@ Think of a cron job as: **when** to run + **what** to do.
|
||||
2. **Choose where it runs**
|
||||
- `sessionTarget: "main"` → run during the next heartbeat with main context.
|
||||
- `sessionTarget: "isolated"` → run a dedicated agent turn in `cron:<jobId>`.
|
||||
- `sessionTarget: "current"` → bind to the current session (resolved at creation time to `session:<sessionKey>`).
|
||||
- `sessionTarget: "session:custom-id"` → run in a persistent named session that maintains context across runs.
|
||||
|
||||
Default behavior (unchanged):
|
||||
- `systemEvent` payloads default to `main`
|
||||
- `agentTurn` payloads default to `isolated`
|
||||
|
||||
To use current session binding, explicitly set `sessionTarget: "current"`.
|
||||
|
||||
3. **Choose the payload**
|
||||
- Main session → `payload.kind = "systemEvent"`
|
||||
@@ -147,12 +157,13 @@ See [Heartbeat](/gateway/heartbeat).
|
||||
|
||||
#### Isolated jobs (dedicated cron sessions)
|
||||
|
||||
Isolated jobs run a dedicated agent turn in session `cron:<jobId>`.
|
||||
Isolated jobs run a dedicated agent turn in session `cron:<jobId>` or a custom session.
|
||||
|
||||
Key behaviors:
|
||||
|
||||
- Prompt is prefixed with `[cron:<jobId> <job name>]` for traceability.
|
||||
- Each run starts a **fresh session id** (no prior conversation carry-over).
|
||||
- Each run starts a **fresh session id** (no prior conversation carry-over), unless using a custom session.
|
||||
- Custom sessions (`session:xxx`) persist context across runs, enabling workflows like daily standups that build on previous summaries.
|
||||
- Default behavior: if `delivery` is omitted, isolated jobs announce a summary (`delivery.mode = "announce"`).
|
||||
- `delivery.mode` chooses what happens:
|
||||
- `announce`: deliver a summary to the target channel and post a brief summary to the main session.
|
||||
@@ -321,12 +332,42 @@ Recurring, isolated job with delivery:
|
||||
}
|
||||
```
|
||||
|
||||
Recurring job bound to current session (auto-resolved at creation):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Daily standup",
|
||||
"schedule": { "kind": "cron", "expr": "0 9 * * *" },
|
||||
"sessionTarget": "current",
|
||||
"payload": {
|
||||
"kind": "agentTurn",
|
||||
"message": "Summarize yesterday's progress."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Recurring job in a custom persistent session:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Project monitor",
|
||||
"schedule": { "kind": "every", "everyMs": 300000 },
|
||||
"sessionTarget": "session:project-alpha-monitor",
|
||||
"payload": {
|
||||
"kind": "agentTurn",
|
||||
"message": "Check project status and update the running log."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `schedule.kind`: `at` (`at`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`).
|
||||
- `schedule.at` accepts ISO 8601 (timezone optional; treated as UTC when omitted).
|
||||
- `everyMs` is milliseconds.
|
||||
- `sessionTarget` must be `"main"` or `"isolated"` and must match `payload.kind`.
|
||||
- `sessionTarget`: `"main"`, `"isolated"`, `"current"`, or `"session:<custom-id>"`.
|
||||
- `"current"` is resolved to `"session:<sessionKey>"` at creation time.
|
||||
- Custom sessions (`session:xxx`) maintain persistent context across runs.
|
||||
- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun` (defaults to true for `at`),
|
||||
`delivery`.
|
||||
- `wakeMode` defaults to `"now"` when omitted.
|
||||
|
||||
@@ -219,13 +219,13 @@ See [Lobster](/tools/lobster) for full usage and examples.
|
||||
|
||||
Both heartbeat and cron can interact with the main session, but differently:
|
||||
|
||||
| | Heartbeat | Cron (main) | Cron (isolated) |
|
||||
| ------- | ------------------------------- | ------------------------ | -------------------------- |
|
||||
| Session | Main | Main (via system event) | `cron:<jobId>` |
|
||||
| History | Shared | Shared | Fresh each run |
|
||||
| Context | Full | Full | None (starts clean) |
|
||||
| Model | Main session model | Main session model | Can override |
|
||||
| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Announce summary (default) |
|
||||
| | Heartbeat | Cron (main) | Cron (isolated) |
|
||||
| ------- | ------------------------------- | ------------------------ | ----------------------------------------------- |
|
||||
| Session | Main | Main (via system event) | `cron:<jobId>` or custom session |
|
||||
| History | Shared | Shared | Fresh each run (isolated) / Persistent (custom) |
|
||||
| Context | Full | Full | None (isolated) / Cumulative (custom) |
|
||||
| Model | Main session model | Main session model | Can override |
|
||||
| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Announce summary (default) |
|
||||
|
||||
### When to use main session cron
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ the workspace is writable. See [Memory](/concepts/memory) and
|
||||
- Legacy `group:<id>` keys are still recognized for migration.
|
||||
- Inbound contexts may still use `group:<id>`; the channel is inferred from `Provider` and normalized to the canonical `agent:<agentId>:<channel>:group:<id>` form.
|
||||
- Other sources:
|
||||
- Cron jobs: `cron:<job.id>`
|
||||
- Cron jobs: `cron:<job.id>` (isolated) or custom `session:<custom-id>` (persistent)
|
||||
- Webhooks: `hook:<uuid>` (unless explicitly set by the hook)
|
||||
- Node runs: `node-<nodeId>`
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ x-i18n:
|
||||
- 任务持久化存储在 `~/.openclaw/cron/` 下,因此重启不会丢失计划。
|
||||
- 两种执行方式:
|
||||
- **主会话**:入队一个系统事件,然后在下一次心跳时运行。
|
||||
- **隔离式**:在 `cron:<jobId>` 中运行专用智能体轮次,可投递摘要(默认 announce)或不投递。
|
||||
- **隔离式**:在 `cron:<jobId>` 或自定义会话中运行专用智能体轮次,可投递摘要(默认 announce)或不投递。
|
||||
- **当前会话**:绑定到创建定时任务时的会话 (`sessionTarget: "current"`)。
|
||||
- **自定义会话**:在持久化的命名会话中运行 (`sessionTarget: "session:custom-id"`)。
|
||||
- 唤醒是一等功能:任务可以请求"立即唤醒"或"下次心跳时"。
|
||||
|
||||
## 快速开始(可操作)
|
||||
@@ -83,6 +85,14 @@ openclaw cron add \
|
||||
2. **选择运行位置**
|
||||
- `sessionTarget: "main"` → 在下一次心跳时使用主会话上下文运行。
|
||||
- `sessionTarget: "isolated"` → 在 `cron:<jobId>` 中运行专用智能体轮次。
|
||||
- `sessionTarget: "current"` → 绑定到当前会话(创建时解析为 `session:<sessionKey>`)。
|
||||
- `sessionTarget: "session:custom-id"` → 在持久化的命名会话中运行,跨运行保持上下文。
|
||||
|
||||
默认行为(保持不变):
|
||||
- `systemEvent` 负载默认使用 `main`
|
||||
- `agentTurn` 负载默认使用 `isolated`
|
||||
|
||||
要使用当前会话绑定,需显式设置 `sessionTarget: "current"`。
|
||||
|
||||
3. **选择负载**
|
||||
- 主会话 → `payload.kind = "systemEvent"`
|
||||
@@ -129,12 +139,13 @@ Cron 表达式使用 `croner`。如果省略时区,将使用 Gateway网关主
|
||||
|
||||
#### 隔离任务(专用定时会话)
|
||||
|
||||
隔离任务在会话 `cron:<jobId>` 中运行专用智能体轮次。
|
||||
隔离任务在会话 `cron:<jobId>` 或自定义会话中运行专用智能体轮次。
|
||||
|
||||
关键行为:
|
||||
|
||||
- 提示以 `[cron:<jobId> <任务名称>]` 为前缀,便于追踪。
|
||||
- 每次运行都会启动一个**全新的会话 ID**(不继承之前的对话)。
|
||||
- 每次运行都会启动一个**全新的会话 ID**(不继承之前的对话),除非使用自定义会话。
|
||||
- 自定义会话(`session:xxx`)可跨运行保持上下文,适用于如每日站会等需要基于前次摘要的工作流。
|
||||
- 如果未指定 `delivery`,隔离任务会默认以“announce”方式投递摘要。
|
||||
- `delivery.mode` 可选 `announce`(投递摘要)或 `none`(内部运行)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user