LibreChat/client/src
Marco Beretta 44d97f859d
feat: Custom Cron Cadence for Scheduled Chats (#15084)
* feat: custom cron cadence for scheduled chats

Scheduled chats could only be built from four fixed presets, each pinned to a
single hour and minute, so anything outside that shape (twice a day, every 15
minutes, the 1st of the month) was not expressible. This adds a Custom cadence
that takes a raw five-field cron expression.

The cadence schema becomes a discriminated union on `frequency`. A cron row
carries `expression` instead of the hour and minute it cannot represent, since
there is no single hour for `0 9,17 * * 1-5`, and the Mongo schema requires each
field only for the shape that has it: a blanket `required` would reject every
cron write, and dropping it entirely would let a structured cadence silently
fire at 00:00 with a missing hour.

Five fields only. croner also reads a six-field form carrying seconds and a
seven-field form that pins a year, and both are refused. Seconds would promise a
precision the engine does not keep, since it polls on a thirty-second tick and
offsets each schedule by up to two minutes of jitter. A pinned year makes a
cadence that runs out, and every place that computes a next run reads "no next
occurrence" as a cadence it cannot read.

Compilation, validation, next-run previews and interval measurement live in
packages/data-provider so the dialog and the engine share one parser and cannot
drift. The dialog previews the next occurrences, enforces the admin interval
floor and disables its own submit from the same functions the server validates
with, so it cannot offer a Create the API answers 400 to.

The interval floor now covers cron, and measures it twice, taking the smaller.
The nominal gap is probed in UTC and discounted by the same worst-case DST
allowance the structured branches carry, which keeps `0 9 * * *` reporting
exactly what the Daily preset reports. Real elapsed time is then measured in the
schedule's own zone across each of that zone's transitions, because
spring-forward compresses a gap that straddles one: `0 0,12 * * *` in
America/New_York is 11 hours that day, not 12, and a floor between the two would
otherwise be bypassed. The floor ships with the schedules list so the dialog can
mirror it rather than surfacing it as a 400 after submit.

Radio gains a wrap variant, since five frequency segments no longer fit one row
in a phone-width dialog and a translated label can push even a desktop one over.
Its indicator follows the selection across rows; the single-row default is
unchanged.

* fix: mark the cron input invalid when the interval floor rejects it

A floor-violating expression disabled Create and rendered the cadence
message, but the input itself still said aria-invalid=false and its
aria-describedby never reached that message, leaving a screen reader
user with a disabled Create and no stated reason.
2026-08-23 14:35:18 -04:00
..
@types
a11y
common 🪡 fix: Thread Parent Message ID Through MCP Request-Scoped Bodies (#15095) 2026-08-21 16:33:01 -04:00
components feat: Custom Cron Cadence for Scheduled Chats (#15084) 2026-08-23 14:35:18 -04:00
constants
data-provider 📡 feat: Stream Detached Subagent Activity (#15111) 2026-08-22 09:45:27 -04:00
hooks 📱 feat: Offer a Dismissable Strip of Chat Beside the Mobile Drawer (#14990) 2026-08-23 14:34:39 -04:00
lib/rum
locales feat: Custom Cron Cadence for Scheduled Chats (#15084) 2026-08-23 14:35:18 -04:00
polyfills
Providers perf: Warm Feature Catalogs in the Background After First Paint (#15047) 2026-08-21 20:33:29 -04:00
routes 📱 feat: Offer a Dismissable Strip of Chat Beside the Mobile Drawer (#14990) 2026-08-23 14:34:39 -04:00
store 📱 feat: Offer a Dismissable Strip of Chat Beside the Mobile Drawer (#14990) 2026-08-23 14:34:39 -04:00
utils 📡 feat: Stream Detached Subagent Activity (#15111) 2026-08-22 09:45:27 -04:00
App.jsx 🔀 perf: Swap the Transcript With the URL on Conversation Switch (#15054) 2026-08-21 11:28:18 -04:00
main.jsx
mobile.css 📐 feat: Match the Message Column to the Composer (#14851) 2026-08-15 07:19:34 +02:00
style.css 📱 feat: Swipe the Mobile Drawer Open and Closed (#14902) 2026-08-16 19:45:35 -04:00
vite-env.d.ts