Repository navigation
feat(core): schedule prompts from agents, /schedule and the experimental API - #59457
Conversation
522d54f to
5158f2f
Compare
5158f2f to
2375715
Compare
There was a problem hiding this comment.
Works as described, and I found nothing that blocks a merge. I ran it from source with the simulated model:
- With
experimental.scheduleson,/schedule every hour for 8 hours check CIexpanded into the template. The model'sopencode.schedule_createcall returned an active hourly schedule with the right next run and end time. - With the flag off,
/scheduleisn't registered and the tool isn't listed. The base branch behaves the same way. - Core (31) and server (1) tests pass, and
tsc -bpasses in schema, core, protocol, server and client.
The flag is checked on create and on every other tool and HTTP call. The owned check makes another session's schedules read as not found, and a schedule whose session is gone also reads as not found.
Public API: everything is additive and experimental, so existing callers aren't affected.
- HTTP:
POST/GET /api/experimental/schedule, plusGETandDELETE /api/experimental/schedule/{scheduleID}andPOST .../{scheduleID}/pauseand/resume. These are in OpenAPI under theexperimentaltag, with a new 404ScheduleNotFoundError. - Client:
client.experimental.schedule.{create,list,get,pause,resume,delete}, and theScheduletypes are now exported from the generated client. - Schema: adds
Schedule.WhenInput,Schedule.RequestandSchedule.CreatePayload.Schedule.Durationloses its format check and becomes a plain annotated string (looser, still compatible). - Agent tools:
opencode.schedule_create,schedule_list,schedule_pause,schedule_resumeandschedule_delete. - Command:
/schedule.
One design point for before this leaves experimental: schedule_create doesn't ask for permission and has no per-session limit. A prompt-injected or confused agent could set up recurring prompts, and a schedule that fires could create more. A permission prompt or a cap might be worth it.
There was a problem hiding this comment.
The new commit only merges dev. Its conflicts were in the generated promise client and the client test, and both were settled by adding schedule to the new client.experimental grouping from #59072. Re-running generate in client and protocol changes nothing.
I checked it again on the new head:
- The client, protocol identifier, core and server schedule tests pass.
tsc -bpasses in schema, core, protocol, server and client.- In the TUI,
/schedulewithschedule_createstill creates an active schedule.
The public API is the same as in my last review: additive, experimental and backwards compatible. Nothing new to flag.
Resolves conflicts in generated migration files after #59457 and newer migrations landed on dev. The branch's migration is regenerated as 20261029140544_schedule_condition so it sorts after dev's migrations; it still only adds the nullable schedule.if and schedule.state columns. Verified: bun typecheck (core), test/schedule.test.ts and test/tool-schedule.test.ts (41 pass).
…route #59457 added the experimental schedule.create route, and Schedule.create can now fail with Permission.BlockedError or Permission.CorrectedError when a code condition's allowlisted tools are denied or rejected at creation. Map both to ForbiddenError so the route's error type stays within the API contract. Verified: bun typecheck (server, core), server test/schedule.test.ts.


Agents, people and apps can now schedule a prompt for later: ask "check CI every hour for the next 8 hours" and the session wakes itself.
Builds on #59401 (merged), which added the storage and timer. Everything here stays off unless
experimental.schedulesis on where the session lives.💡 Why
#59401 fires schedules, but nothing could create one yet. This PR gives the agent, the user and apps a way in.
🆕 Now you can
schedule_create,schedule_list,schedule_pause,schedule_resumeandschedule_deletein theopencodetool namespace. It can only see and change its own session's schedules./schedule every morning at 9 check CI(or just/scheduleto list them). The agent turns that into a schedule.client.experimental.schedule.{create,list,get,pause,resume,delete}(/api/experimental/schedule).whenthe short way:{ in: "20 minutes" },{ at: "2026-10-27T09:00" },{ every: "1 hour" }or{ cron: "0 9 * * 1-5" }, with an optionaltz, plusendsandprompt.🚦 How it ships
Off by default. With
experimental.schedulesoff, the tools and/schedulearen't registered, and the API returns 403. They appear or disappear when config changes, without a restart. Conditional checks (if/untilshell commands that need approval) come in the next PR.Visual
📊 Proof: new tool tests cover the flag gate, the full create → list → pause → resume → delete flow,
/schedule, and another session's schedule reading as missing. A server test drives the real HTTP API through the generated client, including 400 for a bad duration or two triggers in onewhen, and 403 where the flag is off. Tools recheck the flag on every call, so a config change takes effect mid-session. 31 core and 1 server tests pass.👀 Read:
packages/core/src/tool/plugin/schedule.ts, theownedcheck. Schedule ids aren't secret, so every tool call checks that the schedule belongs to the calling session. Also note that duration checks moved from the schema into the service, because generated clients can't run custom schema checks. The rest is routine (generated client and OpenAPI).Planned feature · no user reports · not measured
Checks run
bun run script/test.ts test/schedule.test.ts test/tool-schedule.test.ts(core): 31 passbun test test/schedule.test.ts(server): 1 passbun typecheckin schema, core, protocol, server and client: passbun run generatein client and protocol: regeneratedoxlinton changed files: 0 errors;bun script/sdk-docs.ts: 0 problemsAdded by the opencode bot (AI-generated).
Related: #41906, #53960
Public API: adds experimental /api/experimental/schedule endpoints, client.experimental.schedule., Schedule schema types, opencode.schedule_ agent tools and /schedule (backwards compatible)