⏰ feat: Scheduled Chats - Agent-Centric Recurring Runs - #14373
Closed
danny-avila wants to merge 125 commits into
Closed
danny-avila wants to merge 125 commits into
danny-avila wants to merge 125 commits into
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
I added Scheduled Chats: users bind a prompt to one of their agents and a recurring cadence, and the server fires it on time as the owning user, producing a real conversation that appears in the sidebar like any other chat. The scheduler is Mongo-arbitrated end to end, so it is correct on a single instance without Redis and on N replicas with it, with one new runtime dependency (
croner, zero transitive deps) used solely for timezone-aware next-occurrence math.ScheduleandScheduleRunschemas (packages/data-schemas): structured cadence object (hourly/daily/weekdays/weekly+ hour/minute/daysOfWeek) with required IANA timezone, materializednextRunAt, lease fields, typeddisabledReason, and a run collection with a unique{scheduleId, scheduledFor}index plus a 90-day TTL.findOneAndUpdateCAS takes a 5-minute lease (exactly one replica wins per occurrence), and the run-doc insert against the unique index is the durable idempotency record, so crash-retry never double-fires. The deterministicclientRequestId(sched:{id}:{scheduledFor}) additionally collapses retries in the existingclaimGenerationlayer.packages/api/src/schedules/): 30s self-rescheduling tick with jittered sleep, per-tick claim cap, deterministic per-schedule fire jitter (herd shaping), misfire skip-forward, and job-store-aware reconciliation that surfaces HITL pauses asrequires_action(which deliberately does not block the next fire), marks crashed runsinterrupted, and resolves resumed runs./api/agents/chat/agentswith a scope-claimed short-lived JWT, so the full middleware chain (moderation, PII filter, agent ACL, convo ownership, idempotency) applies unchanged; persistence, title generation, Meili indexing, and client resume all ride the existingResumableAgentControllerpath with no client connected.skipped_balanceand auto-disable withinsufficient_balanceafter 5 consecutive), overlap skip while a prior run is stillstarted, agent-deletion auto-disable, and consecutive-failure auto-disable, every disable carrying a typed reason the client localizes.LIMIT_MESSAGE_IP/LIMIT_MESSAGE_USERvia a signature-verified scope claim, so the scheduler's own caps (hourly floor,maxPerUser10, admin-overridable underinterface.schedules) are the single throttle and legitimate fires cannot be rate-limited into auto-disable.SCHEDULESpermission type (USE/CREATE), interface flag seeding, and/api/schedulesCRUD plusPOST /:id/run(run-now), with server-side validation of timezone, interval floor, agent access, and file ownership.file_ids, fires re-resolve the file docs at run time and proceed without deleted files (recordingdroppedFileIdson the run) instead of failing the run. The dialog uploader affordance is a fast-follow; the API and fire path are complete.Recorded limitations for follow-ups: the dialog attachment uploader, run-history UI (the collection already captures it), a per-schedule tool allowlist (schema field reserved), and resumed-run success attribution beyond the job store's completed-job TTL window.
Change Type
Testing
cd packages/api && npx jest cadence— 23 tests: preset→cron mapping, DST spring-forward/fall-back behavior in America/New_York asserted via Intl wall-clock formatting, weekday skips, jitter determinism/bounds, timezone validation.cd packages/data-schemas && npx jest schedule.methods— 16 tests on mongodb-memory-server with real indexes: concurrent claim contention, lease expiry, claim ordering, E11000 idempotency, success/error/threshold auto-disable, balance-skip threshold,transitionRunStatusCAS,hasActiveRunlifecycle.npm run build(Turborepo) green across all workspaces;tsc --noEmitclean inpackages/api,packages/data-schemas, andclient; eslint clean on all touched files.interface.schedules.minIntervalMinutes), watch the engine claim and fire it, confirm the conversation appears with a generated title, confirm run-now works, kill the server mid-generation and confirm the run reconciles tointerruptedwith no duplicate on restart.Test Configuration:
USE_REDISis enabled).Checklist