ViteHub is still experimental. Expect bugs and breaking changes.

Schedule

Declare static cron schedules and manage recurring Runtime Schedules for eligible targets.

Use a Static Schedule Definition for cron entries deployed with the app. Use Runtime Schedules when the app creates, updates, or removes recurring work while it runs.

A Schedule Target can start an Agent Invocation, but Schedule itself runs on the server. Give an Agent schedule access only through a Schedule Capability.

Quick start

Install

Terminal
pnpm add @vite-hub/schedule

Configure

vite.config.ts
import { hubSchedule } from '@vite-hub/schedule/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [hubSchedule()],
})

Start using it

server/schedules/daily-report.ts
import { defineSchedule } from '@vite-hub/schedule'

export default defineSchedule({
  cron: '0 8 * * *',
  async handler({ scheduledAt, waitUntil }) {
    await sendDailyReport(scheduledAt)
    waitUntil(recordDelivery())
  },
})

Public imports

ImportUse
defineSchedule from @vite-hub/scheduleDeclare a Static Schedule Definition.
defineScheduleTarget from @vite-hub/scheduleDeclare a cronless target for Runtime Schedules.
schedules, validateRuntimeScheduleCron from @vite-hub/schedule or @vite-hub/schedule/runtimeManage Runtime Schedules and validate cron strings.
executeSchedule, executeStaticSchedule, executeRuntimeSchedule, createScheduleRun from @vite-hub/scheduleExecute schedules from provider hooks or custom runtime wiring.
createMemoryRuntimeScheduleStore, createKVRuntimeScheduleStore from @vite-hub/scheduleConfigure Runtime Schedule storage.
createMemoryScheduleRunStore, createKVScheduleRunStore from @vite-hub/scheduleConfigure Schedule Run storage.
setRuntimeScheduleStore, setScheduleRunStore, setScheduleRuntimeRegistry from @vite-hub/scheduleWire custom runtime state.
installScheduleRuntime from @vite-hub/schedule/runtime/driverConnect stored Runtime Schedules to a host-owned wake driver.
createProcessScheduleWakeDriver from @vite-hub/schedule/runtime/processScan and wake due Runtime Schedules inside a long-running process.
hubSchedule, createScheduleNitroConfig from @vite-hub/schedule/viteRegister discovery and generated provider output.

Schedule Definition, Runtime Schedule, Schedule Run, and Schedule Store types are exported from @vite-hub/schedule.

Configure the Vite Integration

vite.config.ts
import { hubSchedule } from '@vite-hub/schedule/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    hubSchedule({
      runtime: {
        driver: 'process',
        prefix: 'my-app:schedule',
      },
    }),
  ],
})
OptionTypeDefaultDescription
providerOutputScheduleVitePluginOptions['providerOutput']autoControls generated provider cron output. Values: auto, standalone, nitro, false.
projectRootstringViteHub project rootResolves discovered schedule files and generated registry output from a custom project root.
runtimeScheduleProcessRuntimeOptionsNo runtime driverExplicitly installs the generated Nitro Process Runtime. Accepts driver: 'process', plus optional prefix (default vitehub:schedule), intervalMs (default 60_000), and concurrency (default 1).

Use createScheduleNitroConfig() when a Nitro integration owns config merging and needs Schedule to return Nitro-ready provider output.

The Process Runtime imports the discovered registry and runs Static Schedule Definitions alongside persisted Runtime Schedules through one driver queue. It creates the Runtime Schedule and Schedule Run stores through the default KV store configured by hubKv(), applies the Schedule prefix to both, reports errors through Nitro, and closes the driver during Nitro shutdown. It scans once per minute with one concurrent wake unless configured otherwise, and intervalMs cannot exceed the one-minute cron resolution. This setting is orthogonal to providerOutput; selecting one does not infer the other.

The Process Runtime requires exactly one long-lived process or replica. The KV run store records occurrences but does not provide distributed leader election or locking. Do not use this driver on request-scoped or serverless hosts that may stop between requests. It scans inside the Node.js process and does not create cron, systemd, or another operating-system schedule.

Provider output

ModeOutputNuance
autoSelects the appropriate generated output for the active build context.Default mode for Vite projects.
standaloneWrites standalone provider output outside Nitro.Use when ViteHub owns provider output directly.
nitroWrites Nitro Cloudflare module and plugin output.Use when Nitro owns Cloudflare cron wiring.
falseDisables generated provider output.Runtime helpers still work when you wire execution yourself.
HostStatic Schedule outputRuntime Schedule nuance
CloudflareCron trigger output and Cloudflare schedule runtime entry wiring.Runtime Schedules still need Provider Wake output or a long-running runner.
VercelVercel cron-compatible output for static schedules.Runtime Schedules still need Provider Wake output or a long-running runner.
DenoDeno.cron output loaded by generated Deno Agent server output.Runtime Schedules still need Provider Wake output or a long-running runner.
Provider Wake output requires a static five-field UTC cron string compatible with generated provider output. Runtime Schedules still need an existing Provider Wake or a long-running host to execute due schedules.

Define a static schedule

Use a Static Schedule Definition when the host needs build-time Provider Output such as cron entries or provider wake configuration.

server/schedules/daily-report.ts
import { defineSchedule } from '@vite-hub/schedule'

export default defineSchedule({
  cron: '0 8 * * *',
  async handler(context) {
    await sendDailyReport(context.scheduledAt)
  },
})

Cron expressions use the Schedule Time Base, currently UTC. The discovered file name provides the Static Schedule Definition identity.

Schedule Definition options

OptionTypeRequiredDescription
cronstringYesFive-field UTC cron expression for the Static Schedule Definition.
handlerScheduleHandlerYesFunction called with Schedule Run Context.
allowRuntimeSchedulesbooleanNoAllows Runtime Schedules to target this definition.

ScheduleRunContext includes id, scheduledAt, waitUntil, optional attemptId, optional runId, optional Runtime Schedule id, optional Runtime Schedule target, and optional Runtime Schedule input.

Use waitUntil(promise) for consequential work that can outlive the handler body. Direct and local execution settles registered work before recording the Schedule Run result; a rejection fails the run with the same diagnostics as a handler rejection. An installed wake runtime instead retains registered work after the handler returns, reports rejection through its onError hook, and drains outstanding work when the runtime closes.

Create recurring Runtime Schedules

Runtime Schedules are cron schedules stored by ViteHub. A Runtime Schedule can target only a Runtime Schedule Target that opted into runtime reuse. Set an IANA timeZone when the cron must follow local civil time and daylight-saving changes. Omit it to use UTC.

Use defineScheduleTarget() when the handler runs only through Runtime Schedules and doesn't need its own build-time cron. These targets don't emit static provider output.

server/schedules/report.ts
import { defineScheduleTarget } from '@vite-hub/schedule'

export default defineScheduleTarget<{ prompt: string }>({
  async handler({ input }) {
    if (input) await generateReport(input.prompt)
  },
})

defineSchedule() remains cron-required and can opt into runtime reuse with allowRuntimeSchedules:

server/schedules/daily-report.ts
import { defineSchedule } from '@vite-hub/schedule'

export default defineSchedule({
  allowRuntimeSchedules: true,
  cron: '0 8 * * *',
  async handler() {
    await sendDailyReport()
  },
})

Use the schedules Runtime Helper from server code.

server/api/schedules.post.ts
import { schedules } from '@vite-hub/schedule/runtime'

export default defineEventHandler(async () => {
  return schedules.create({
    cron: '30 8 * * 1-5',
    id: 'weekday-report',
    input: { prompt: 'Summarize yesterday' },
    target: 'report',
    timeZone: 'Europe/Copenhagen',
  })
})

Runtime Schedule input

InputTypeRequiredDescription
cronstringcreate onlyFive-field cron expression evaluated in timeZone, or UTC when timeZone is omitted.
targetScheduleTargetNamecreate onlyA defineScheduleTarget() declaration or Static Schedule Definition that set allowRuntimeSchedules: true.
idstringNoStable Runtime Schedule id. ViteHub generates one when omitted.
enabledbooleanNoWhether the Runtime Schedule executes. Defaults to true on create.
inputunknownNoOpaque input passed to the target handler as context.input.
timeZonestringNoNamed IANA time zone used to evaluate the cron expression. Numeric offsets such as +01:00 are rejected. Defaults to UTC.

RuntimeScheduleUpdateInput accepts cron, target, enabled, input, and timeZone. Create stores an input snapshot. Providing input on update replaces the complete snapshot; omitting it preserves the existing value. Schedule does not merge or interpret input, and the configured store must support the value's serialization requirements. Omitting timeZone on update preserves the stored zone; set it explicitly to UTC to reset UTC evaluation.

Local cron matching follows conventional daylight-saving behavior: a local time missing during a DST gap is skipped, while both distinct instants in a repeated local time during a DST overlap run.

Runtime helper methods

MethodDescription
schedules.create(input)Creates a Runtime Schedule.
schedules.list()Lists Runtime Schedules.
schedules.get(id)Reads one Runtime Schedule.
schedules.update(id, input)Updates a Runtime Schedule.
schedules.delete(id)Deletes a Runtime Schedule.
schedules.enable(id)Sets enabled to true.
schedules.disable(id)Sets enabled to false.
schedules.run(id, options?)Executes one Runtime Schedule immediately.
schedules.listRuns()Lists Schedule Run records.
schedules.getRun(id)Reads one Schedule Run record.
schedules.listAttempts(runId)Lists attempts for one Schedule Run.

One-time delayed execution is not part of the first-version Scheduling vocabulary; use a recurring cron schedule, Queue delay, or Workflow design when that matches the actual behavior.

Connect a Runtime Schedule wake driver

Host integrations use a wake driver when the host can create and remove native schedule registrations at runtime.

server/runtime/schedule.ts
import { installScheduleRuntime } from '@vite-hub/schedule/runtime/driver'

const controller = await installScheduleRuntime({
  createDriver: context => hostScheduler.driver(context),
  registry: scheduleRegistry,
  runtimeScheduleStore,
  scheduleRunStore,
  staticRegistry: scheduleRegistry,
})

createDriver(context) returns a driver with reconcile(schedules). Pass staticRegistry when the driver also schedules discovered Static Schedule Definitions. Each reconciliation then receives those definitions with the complete stored Runtime Schedule snapshot, including disabled records. Installation waits for the first reconciliation.

The installed runtime processes Runtime Schedule creates, updates, and deletes one at a time. It saves each change before reconciling the wake driver. If reconciliation fails, ViteHub restores the previous record and rejects the change. Manual schedules.run() calls execute immediately and don't reconcile the driver.

When the host fires a native wake, call context.wake({ scheduleId, scheduledAt }) with the exact stored Runtime Schedule id and occurrence time. Call controller.close() during host shutdown to release process resources; closing does not delete definitions, schedules, or run history.

Use createProcessScheduleWakeDriver() from @vite-hub/schedule/runtime/process when a custom long-running host wants the same in-process wake behavior without generated Nitro wiring.

startScheduleRunner() has been removed. Existing self-hosted processes must install createProcessScheduleWakeDriver() through installScheduleRuntime() and await controller.close() during host shutdown.

Static provider output remains build-time configuration; selecting the Process Runtime also executes discovered Static Schedule Definitions without requiring provider output.

Storage

StoreConfigure withNuance
Memory Runtime Schedule StorecreateMemoryRuntimeScheduleStore()Default in-process behavior; useful for tests and local runtime only.
KV Runtime Schedule StorecreateKVRuntimeScheduleStore(options?)Persists Runtime Schedule records through a KV-compatible storage object.
Memory Schedule Run StorecreateMemoryScheduleRunStore()Default in-process run history; useful for tests and local runtime only.
KV Schedule Run StorecreateKVScheduleRunStore(options?)Persists Schedule Runs and attempts through KV-compatible storage.
Custom StoresetRuntimeScheduleStore(store), setScheduleRunStore(store)Implement RuntimeScheduleStore or ScheduleRunStore directly.

Connect Schedule to Agents

The Schedule Capability can let an Agent read or manage allowed Runtime Schedules through Capability policy. Inline Agent Schedules start the owning Agent with Schedule Invocation Input, not a synthetic user message.

Attach a Schedule Capability only when a model needs to manage schedules. Read Official capabilities for Capability modes and write policy.

Production checks

Schedule Runs, Schedule Run Attempts, retry policy, overlap policy, and dedupe policy belong to Schedule. Naming a policy does not imply every policy is configurable in the first version.

Static Schedule Definitions and Provider Wake output remain UTC. Runtime Schedules use UTC by default and can persist an IANA timeZone when local clock time must follow daylight-saving changes.

Next steps

  • Use Queue when a provider-supported enqueue delay is enough.
  • Use Workflows for durable orchestration.
  • Learn trigger language in Channels API.