Skip to content

[plugin sdk] Add scheduled session turns#75588

Closed
100yenadmin wants to merge 17 commits into
openclaw:mainfrom
electricsheephq:split/plugin-sdk-scheduled-turns
Closed

[plugin sdk] Add scheduled session turns#75588
100yenadmin wants to merge 17 commits into
openclaw:mainfrom
electricsheephq:split/plugin-sdk-scheduled-turns

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented May 1, 2026

Copy link
Copy Markdown
Contributor

Why this matters

OpenClaw plugins increasingly need to create follow-up work that happens later, in the same session lane the user is already operating in: remind me in an hour, reopen this investigation tomorrow, schedule a follow-up agent turn after an approval window, or clean up a family of pending nudges when the workflow resolves.

Without a generic seam, plugin authors end up with two bad options:

  • push product-specific scheduling logic into OpenClaw core; or
  • own host cron details themselves, which leaks infrastructure concerns into every plugin.

This PR adds the narrow host-scheduler seam in the plugin SDK. Plugins can ask the host to schedule a future session turn and later clean up plugin-owned scheduled jobs by tag, while OpenClaw keeps ownership of cron naming, lifecycle, delivery routing, and cleanup semantics.

What kinds of plugins this could support

This seam is intentionally generic. It can support many plugin families without adding plugin-specific scheduling routes to core:

  • Support and ops plugins: reopen or re-check incidents after a delay, schedule escalation nudges, or queue a follow-up diagnostic turn.
  • CRM and sales plugins: create reminder turns for deal follow-ups, pending replies, or renewal check-ins.
  • Approval and workflow plugins: schedule reminders while waiting on a human decision, then clean up all related nudges when the workflow resolves.
  • Digest and assistant plugins: schedule a future turn that produces a recap, check-in, or status refresh in the same session lane.
  • Import, migration, and reconciliation plugins: retry or re-check long-running work later without inventing a parallel scheduler stack.
  • Research and monitoring plugins: schedule a revisit turn after some cooling-off period when an external dependency is expected to change.

The important part: the plugin decides why the follow-up exists. OpenClaw only provides the bounded host seam for when it should happen and how it is cleaned up.

New SDK seam

This PR adds:

  • OpenClawPluginApi.scheduleSessionTurn(...)
  • OpenClawPluginApi.unscheduleSessionTurnsByTag(...)
  • typed schedule / cleanup params and result types in the SDK
  • host-side support for at, delayMs, and cron schedules
  • plugin-prefixed cron naming plus plugin-owned tag cleanup
  • a narrow post-registration carve-out so only these two helpers remain callable after register()

Example plugin usage:

const reminder = await api.scheduleSessionTurn({
  sessionKey,
  delayMs: 60 * 60 * 1000,
  text: "Re-check whether the customer replied.",
  tag: "followup",
  deliveryMode: "announce/last",
  deleteAfterRun: true,
});

await api.unscheduleSessionTurnsByTag({
  sessionKey,
  tag: "followup",
});

Runtime architecture

flowchart LR
  Plugin["Plugin callback, handler, or workflow"] --> SDK["scheduleSessionTurn / unscheduleSessionTurnsByTag"]
  SDK --> HostHooks["Host hook workflow"]
  HostHooks --> Cron["Host cron naming and persistence"]
  Cron --> FutureTurn["Future agent turn in the same session lane"]
  Plugin --> Cleanup["Plugin-owned tag cleanup"]
  Cleanup --> Cron
Loading

The plugin owns the follow-up intent and tag taxonomy. OpenClaw owns the cron contract, namespacing, and actual future-turn delivery.

Safety and boundaries

This stays intentionally narrow:

  • plugins do not get generic cron ownership or arbitrary background-job control
  • only scheduleSessionTurn and unscheduleSessionTurnsByTag stay callable after register()
  • cron job names are namespaced by plugin id so short tags do not clobber each other
  • reserved cleanup tags are rejected instead of silently widening semantics
  • invalid cron + deleteAfterRun: true combinations are rejected instead of guessing
  • scheduled-turn records stay plugin-owned until lifecycle cleanup confirms the host job is gone

This is a scheduler seam for future session turns, not a generic host automation API.

What changed

  • Added OpenClawPluginApi.scheduleSessionTurn and unscheduleSessionTurnsByTag plus SDK exports for schedule/cleanup params and results.
  • Added host cron payload construction for at, delayMs, and cron schedules, with optional delivery mode.
  • Added plugin-prefixed cron names and tag cleanup so two plugins can reuse short tags without clobbering each other.
  • Rejected reserved : cleanup tags and rejected cron + deleteAfterRun: true instead of silently widening the host contract.
  • Preserved plugin-owned scheduled-turn records until lifecycle cleanup confirms the host job is gone, instead of time-pruning those records early.
  • Preserved the loader's closed-after-register guard for normal registration mutations, but let only scheduleSessionTurn and unscheduleSessionTurnsByTag remain callable after register() so a real bundled plugin can capture them during register() and invoke them later from a registered callback/handler.
  • Added real-loader regression coverage for post-registration gateway-handler dispatch plus the invalid-tag / invalid-deleteAfterRun cases.
  • Moved the changelog entry back under ## Unreleased and regenerated the plugin-sdk API baseline after the rebase.

Relationship to adjacent seams

This does not try to solve every workflow problem in one slice. It is intentionally narrower than session actions, run-context lifecycle, attachments, or SessionEntry projection. Those remain split into follow-up PRs so maintainers can review one host seam at a time.

Non-goals

  • Does not widen the generic post-registration plugin API beyond these two operational helpers.
  • Does not give plugins arbitrary cron ownership or a general background-job runner.
  • Does not include session actions, host-mediated attachments, finalization retry/run-context lifecycle, tool-derived paths, SessionEntry projection, or advanced workflow composition fixtures.
  • Does not claim to close broader Control UI/native-page RFC scope; this only adds the generic scheduled-turn seam.

Real behavior proof

Behavior or issue addressed:
This keeps the post-registration seam narrow while proving the scheduled-turn helpers work in a real host runtime. After this patch, a bundled plugin can capture scheduleSessionTurn and unscheduleSessionTurnsByTag during register(), invoke them later from a registered callback/handler, create real host cron jobs, reject invalid cleanup inputs without widening the contract, and clean up the tagged jobs deterministically.

Real environment tested:
On 2026-05-09, I started a real loopback gateway from /Users/lume/openclaw-review-worktrees/pr-75588-rebase and loaded a real bundled proof-scheduler plugin through loadOpenClawPlugins.

Exact steps or command run after this patch:

  1. Loaded the bundled proof-scheduler plugin and let it capture api.scheduleSessionTurn plus api.unscheduleSessionTurnsByTag during register().
  2. Invoked the registered plugin gateway handler proof-scheduler.exercise so the plugin exercised those captured helpers after registration.
  3. Called cron.list immediately after scheduling to inspect the real host jobs.
  4. Ran the cleanup phase through unscheduleSessionTurnsByTag, then called cron.list again to verify that the tagged jobs were gone.

Evidence after fix:

  • The schedule phase returned two real handles:
    • 7d2d8bf2-f028-4a44-ad08-c75fd966b965
    • ca448912-25a0-4aec-9e87-88587464e0dc
  • The invalid tag: "bad:tag" case returned null.
  • The invalid cron + deleteAfterRun: true case returned null.
  • cron.list immediately after schedule showed two real host jobs named plugin:proof-scheduler:tag:proof:agent:main:main:*, both targeting session:agent:main:main, both deleteAfterRun: true, with delivery modes announce/last and none, and with scheduled at timestamps 2026-05-09T17:08:00.334Z and 2026-05-09T17:09:00.356Z.
  • The cleanup phase returned { removed: 2, failed: 0 }.
  • cron.list after cleanup returned [].

Copied live output after fix:

$ proof-scheduler.exercise
first.id=7d2d8bf2-f028-4a44-ad08-c75fd966b965
second.id=ca448912-25a0-4aec-9e87-88587464e0dc
badTag=null
badDelete=null

$ cron.list
plugin:proof-scheduler:tag:proof:agent:main:main:* deleteAfterRun=true delivery=announce/last at=2026-05-09T17:08:00.334Z
plugin:proof-scheduler:tag:proof:agent:main:main:* deleteAfterRun=true delivery=none at=2026-05-09T17:09:00.356Z

$ unscheduleSessionTurnsByTag
{ removed: 2, failed: 0 }

$ cron.list
[]

Observed result after fix:
The real bundled plugin was able to use the two late-call helpers after register() without reopening the rest of the plugin API. Real host cron jobs were created, visible in cron.list, and then removed cleanly by the tagged cleanup path. Invalid cleanup tags and invalid cron + deleteAfterRun: true combinations were rejected instead of silently widening the contract.

What was not tested:
This proof did not cover a full user-facing UI flow or a long-lived production cron lifecycle across process restarts. The live proof here is intentionally limited to real post-registration helper capture, real host cron job creation/listing, invalid-input rejection, and tagged cleanup in a live OpenClaw runtime.

Validation

  • pnpm test src/plugins/contracts/scheduled-turns.contract.test.ts src/plugins/contracts/host-hooks.contract.test.ts
  • pnpm plugin-sdk:api:gen
  • pnpm plugin-sdk:api:check
  • pnpm lint:core
  • git diff --check
  • npm run check:no-conflict-markers

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation size: L labels May 1, 2026
@clawsweeper

clawsweeper Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge.

Summary
Review failed before ClawSweeper could summarize the requested change.

Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path.

Real behavior proof
Not applicable: Real behavior proof was not assessed because the Codex review failed.

Next step before merge
Review did not complete, so no work-lane recommendation was made.

Review details

Best possible solution:

Retry the Codex review after fixing the execution failure.

Do we have a high-confidence way to reproduce the issue?

Unclear. The review failed before ClawSweeper could establish a reproduction path.

Is this the best way to solve the issue?

Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction.

What I checked:

  • failure reason: codex execution failed.
  • codex failure detail: Command failed: /usr/bin/git status --porcelain=v1 --untracked-files=all.
  • codex stdout: Per-item Codex failure; continuing with the rest of the shard.

Likely related people:

  • unknown: Codex failed before it could trace repository history. (role: review did not complete; confidence: low)

Remaining risk / open question:

  • No close action taken because the review did not complete.

Codex review notes: model gpt-5.5, reasoning high; reviewed against eddf56361152.

@100yenadmin

Copy link
Copy Markdown
Contributor Author

Fixed in cc23de28b73. Scheduled announce turns now send cron-valid delivery metadata with channel: "last", and unscheduleSessionTurnsByTag pages through cron.list using hasMore/nextOffset so matching tagged jobs past the first page are removed. Tests: src/plugins/contracts/scheduled-turns.contract.test.ts covers cron validator acceptance and paginated cleanup; src/gateway/server-methods/cron.validation.test.ts was run alongside it. Validation: pnpm test src/plugins/contracts/scheduled-turns.contract.test.ts src/gateway/server-methods/cron.validation.test.ts, pnpm plugin-sdk:api:check, pnpm lint:core, git diff --check, npm run check:no-conflict-markers. pnpm check:test-types is currently blocked by unrelated fresh-main errors in src/commands/onboard-non-interactive.ts.

@100yenadmin
100yenadmin marked this pull request as ready for review May 1, 2026 12:39
Copilot AI review requested due to automatic review settings May 1, 2026 12:39
@100yenadmin
100yenadmin force-pushed the split/plugin-sdk-scheduled-turns branch from cc23de2 to a51d1e8 Compare May 1, 2026 12:41

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc23de28b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/host-hook-workflow.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Plugin SDK seam that lets bundled plugins schedule future session turns via the host cron surface, and deterministically clean them up by a plugin-owned tag, with contract coverage and SDK baseline updates.

Changes:

  • Extend OpenClawPluginApi with scheduleSessionTurn and unscheduleSessionTurnsByTag, plus exported param/result types.
  • Implement host-side cron job name construction, scheduling, stale-unload rollback, and tag-based cleanup.
  • Add focused contract tests and update SDK API baseline + changelog.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/plugins/types.ts Adds new API methods and re-exports scheduling/unscheduling types.
src/plugins/registry.ts Wires new API methods through the plugin registry with side-effect gating + active-registry protection.
src/plugins/host-hooks.ts Defines new scheduling/unscheduling types (schedule union + params/results).
src/plugins/host-hook-workflow.ts Implements cron payload construction, cron.add/cron.remove integration, name/tag logic, and tag cleanup via cron.list paging.
src/plugins/contracts/scheduled-turns.contract.test.ts Contract tests for naming, cron payload validity, paging cleanup, stale rollback, and API wiring.
src/plugins/captured-registration.ts Extends captured plugin registration test shim with the new API methods.
src/plugins/api-builder.ts Adds handlers + no-op defaults for the new API methods.
src/plugin-sdk/plugin-test-api.ts Extends test plugin API factory with the new async methods.
src/plugin-sdk/plugin-entry.ts Re-exports new scheduling/unscheduling types from the SDK entry surface.
src/plugin-sdk/core.ts Re-exports new scheduling/unscheduling types from the core SDK barrel.
docs/.generated/plugin-sdk-api-baseline.sha256 Updates SDK API baseline hashes to reflect the new surface.
CHANGELOG.md Documents the new bundled-plugin scheduling seam and tag-based unscheduling.

Comment thread src/plugins/host-hooks.ts Outdated
Comment thread src/plugins/host-hook-workflow.ts Outdated
Comment thread src/plugins/types.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04a4332aae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/host-hook-workflow.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Comment thread src/plugins/host-hook-workflow.ts
Comment thread src/plugins/host-hook-workflow.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 35216dcf6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/host-hook-workflow.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread src/plugins/host-hook-workflow.ts Outdated
Comment thread src/plugins/host-hook-workflow.ts Outdated
Comment thread src/plugins/captured-registration.ts
@100yenadmin

Copy link
Copy Markdown
Contributor Author

Audit follow-up fixed in 25da9537025: moved the scheduled-job preservation hardening into this scheduled-turn slice so it is independently mergeable. preserveJobIds now skips old cleanup callbacks even across generation mismatches, preventing reload cleanup from removing a live newer scheduled job. Test: src/plugins/contracts/host-hooks.contract.test.ts (does not invoke old scheduler cleanup for a preserved newer generation).

@100yenadmin
100yenadmin requested a review from Copilot May 1, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comment thread src/plugins/captured-registration.ts
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
Eva and others added 17 commits May 10, 2026 13:33
The "blocks registration-time" test was flaky in full-file runs because
setActivePluginRegistry queues cleanup via fire-and-forget dynamic
imports (cleanupPreviousPluginHostRegistry). When the prior test's
"wires schedule and unschedule" mock returned {ok:true} for cron.remove
without a `removed` field, the cleanup callback in
schedulePluginSessionTurn would throw, leaving the runtime scheduler-job
state map populated. The runtime-state cleanup loop then runs twice (in
runPluginHostCleanup at lines 406 and 430), and the second pass's
cron.remove call settles AFTER the prior test's afterEach mockReset,
contaminating the next test.

Two surgical fixes:
- Make the "wires schedule" mock return {ok:true,removed:true} for
  cron.remove so the cleanup callback's didCronCleanupJob check passes,
  the state-map entry gets deleted, and the duplicate cleanup pass
  finds nothing to do.
- Defensively drain pending microtasks at the start of the failing test
  and reset the mock again, so any remaining queued cleanup cannot
  pollute the test's call-history assertion.

This is a test-only change; production runtime behavior is unchanged.
@100yenadmin
100yenadmin force-pushed the split/plugin-sdk-scheduled-turns branch from 71093ce to fa63242 Compare May 10, 2026 06:48
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@100yenadmin

Copy link
Copy Markdown
Contributor Author

Superseded by #80229, which folds this scheduled-turn slice into the cleanup-first Plugin SDK consolidation branch tied to #80219.

@100yenadmin

Copy link
Copy Markdown
Contributor Author

Superseded by #80229, which now carries the combined proof and merge-ready consolidation branch.

@100yenadmin 100yenadmin reopened this May 10, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants