v0.7.6: calendar scheduled tasks, new hubspot tools, virtualized chat, db perf improvements#5042
v0.7.6: calendar scheduled tasks, new hubspot tools, virtualized chat, db perf improvements#5042waleedlatif1 wants to merge 17 commits into
Conversation
waleedlatif1
commented
Jun 14, 2026
- perf(mothership): virtualize chat transcript and isolate input from stream re-renders (perf(mothership): virtualize chat transcript and isolate input from stream re-renders #5019)
- fix(db-part-3): bound cross-request shared promises against pool wedge (fix(db-part-3): bound cross-request shared promises against pool wedge #5021)
- fix(db): correct misleading error message when DATABASE_REPLICA_URL is malformed (fix(db): correct misleading error message when DATABASE_REPLICA_URL is malformed #5023)
- improvement(react-query): codebase-wide audit — server-state hooks, webhook coherence, resume migration (improvement(react-query): codebase-wide audit — server-state hooks, webhook coherence, resume migration #5024)
- fix(skills): reuse shared upload field in skill import modal; logo-only Quartr icon (fix(skills): reuse shared upload field in skill import modal; logo-only Quartr icon #5026)
- fix(chat): escape attachment filename and validate file URL scheme to prevent XSS (fix(chat): escape attachment filename and validate file URL scheme to prevent XSS #5028)
- fix(db-part-4): enforce consistent cross-resource lock ordering (fix(db-part-4): enforce consistent cross-resource lock ordering #5027)
- fix(mothership): streaming completion-flash fix + Tavily brand icon (fix(mothership): streaming completion-flash fix + Tavily brand icon #5030)
- feat(blocks): add external-service url to block metadata (feat(blocks): add external-service url to block metadata #5032)
- improvement(sim-trigger): change execution terminology to run (improvement(sim-trigger): change execution terminology to run #5033)
- feat(scheduled-tasks): calendar views + persisted, runnable tasks (feat(scheduled-tasks): calendar views + persisted, runnable tasks #4979)
- feat(hubspot): add notes, emails, properties & associations tools (feat(hubspot): add notes, emails, properties & associations tools #5037)
- improvement(permissions): permission groups scoped to organization level (improvement(permissions): permission groups scoped to organization level #5035)
- feat(billing): gate programmatic workflow execution behind a paid plan (feat(billing): gate programmatic workflow execution behind a paid plan #5036)
- improvement(perms): followup to org scoping of permission groups
- improvement(salesforce): align tools + block with Salesforce API and harden CRUD/analytics (improvement(salesforce): align tools + block with Salesforce API and harden CRUD/analytics #5040)
- feat(scheduled-tasks): minute-granular calendar + user timezone preference (feat(scheduled-tasks): minute-granular calendar + user timezone preference #5038)
…tream re-renders (#5019) * perf(mothership): virtualize chat transcript and isolate input from stream re-renders Long chats rendered every message into the DOM with no windowing — a custom rAF "progressive list" only smeared the mount cost across frames without capping it. At ~1000 messages this was 52k DOM nodes and a 21s main-thread block on open, and the input toolbar re-rendered on every streamed token. - Virtualize the message list with @tanstack/react-virtual using dynamic measureElement, stable per-row keys, and a tuned size estimate. Only the visible window mounts, so load cost is now flat regardless of transcript length. Remove the now-redundant useProgressiveList hook. - Memoize UserInput and stabilize its callbacks (useCallback in MothershipChat and home) so streaming ticks no longer re-render the entire input toolbar. - Keep the existing useAutoScroll for streaming stick-to-bottom (it reads the virtualizer's real scrollHeight) and add a per-chat scrollToIndex for initial positioning before paint. Measured on a cloned 1032-message chat: time-to-rendered 26.3s -> 1.7s, main-thread blocked 21.4s -> 0.8s, DOM nodes 52k -> 1.4k, typing-while- streaming p-max 104ms -> 26ms. Adds scripts/perf/ harness used to validate. * address review: pending-chat scroll, flash on tail unmount, empty-row gap, per-role estimate - Pending-chat initial scroll (Cursor, high): seed the scrolled-chat guard with a unique sentinel so a not-yet-persisted chat (undefined chatId) with messages still scrolls to bottom instead of being treated as already-scrolled. - Streaming-row flash (review of virtualization): pin the last row in the rendered window via rangeExtractor so scrolling it out of the overscan window and back mid-stream can't unmount/remount it and re-fire the reveal fade. - Empty assistant row gap (Greptile): move the row gap from the virtual-item wrapper into the row content so a null-rendering (finalised, empty) assistant turn collapses to zero height instead of leaving a pb-6 blank slot. - Per-role row-height estimate instead of a single blended constant, so the scrollbar drifts less as off-screen rows resolve. - Drop the scripts/perf harness from the PR. * fix(mothership): preserve user-row top spacing in virtualized layout Restoring pt-3/pt-2 on the user row keeps the exact inter-row rhythm from the old space-y-6 layout: assistant→user gaps stay 24+12px and user→assistant stay 24px, instead of becoming uniform when the gap moved to per-row pb. * fix(mothership): don't re-scroll when a pending chat persists its id The per-chat initial-scroll guard treated undefined→persisted-id as a chat switch and scrolled to the bottom again, yanking the viewport down if the user had scrolled up mid-stream. Treat that transition as the same conversation: adopt the id without re-scrolling. Genuine chat switches (id→different id) still re-scroll.
#5021) * fix(db-part-3): bound cross-request shared promises against pool wedge * address comments
…s malformed (#5023) * v0.6.29: login improvements, posthog telemetry (#4026) * feat(posthog): Add tracking on mothership abort (#4023) Co-authored-by: Theodore Li <theo@sim.ai> * fix(login): fix captcha headers for manual login (#4025) * fix(signup): fix turnstile key loading * fix(login): fix captcha header passing * Catch user already exists, remove login form captcha * fix(db): correct misleading error message when DATABASE_REPLICA_URL is malformed The error message said reads fall back to the primary when unset, but the code throws a fatal error instead. The misleading parenthetical contradicted actual behavior and could waste time during incident response when an operator sees this message and expects graceful degradation. --------- Co-authored-by: Waleed <walif6@gmail.com> Co-authored-by: Theodore Li <theodoreqili@gmail.com> Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com> Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com> Co-authored-by: Theodore Li <theo@sim.ai>
…ebhook coherence, resume migration (#5024) * improvement(react-query): codebase-wide audit — server-state hooks, webhook coherence, resume migration * chore(react-query): add static pattern linter + address review feedback - add scripts/check-react-query-patterns.ts (staleTime/signal/key-factory/inline-key enforcement) wired into CI as check:react-query; strict zone hooks/queries/**, ratchet elsewhere - fix(resume): use same-origin relative path for resume POST (getBaseUrl could cross origin on whitelabel/preview hosts and drop session cookies) — Cursor Bugbot - remove explanatory inline comments in favor of TSDoc per repo convention
…ly Quartr icon (#5026) * fix(skills): reuse shared upload field in skill import modal; logo-only Quartr icon - Replace the hand-rolled drop zone in the skill import modal with the shared ChipModalField type='file' control (same component the Knowledge Base and Help & Support modals use), so the upload zone is visually consistent. - Migrate the GitHub-URL and paste-content rows to ChipModalField so every field shares the canonical px-4 gutter and error rendering, and align the 'or' dividers to match. - Drop the monospace font on the paste textarea so its text matches the rest of the modal. - Quartr icon now renders the logo mark only (no wordmark) as a black mark on a white rounded tile. * fix(emcn): restore upload spinner via loading prop on ChipModalField file control Addresses review feedback — the shared file drop zone now accepts an optional loading prop that renders an animated spinner and blocks further picks while an async import is in flight, restoring the feedback the skill import modal lost when it migrated off its bespoke drop zone.
…5030) * fix(mothership): keep isAnimating latched so completed messages don't flash The streamed-text reveal latches `mode` and `animated` via `keepStreamingTree` to avoid the streaming→static handoff flash, but `isAnimating` was still wired to `isRevealing`, which flips false the instant the reveal catches up. Streamdown treats `isAnimating: false` as "streaming over" and rebuilds the whole message without the per-word animation spans — that DOM rebuild is a visible flash when a message finishes. Wire `isAnimating` to the same `keepStreamingTree` latch so all three Streamdown props stay constant across completion. Content is stable once revealed, so a permanently-true `isAnimating` has no new tokens to fade and never re-animates. * feat(tavily): official brand icon and white block background Replace the Tavily block icon with the official brand mark and set the block bgColor to white. Ran generate-docs so the docs app icon, the Tavily docs page, and the integrations catalog pick up the new icon/color.
* feat(blocks): add external-service url to block metadata - Add optional `url` field to BlockMeta for the integration's own homepage (e.g. exa.ai, salesforce.com), distinct from docsLink which points at Sim's docs - Populate `url` on all 209 integration blocks with verified homepages (protocol/internal blocks without a single vendor site are left without one) - Document the field in the add-block skill and command, with rule + checklist entries - Backfill missing docsLink on dspy and add a few accurate tag entries * fix(blocks): drop tag backfills to avoid catalog drift Revert the agiloft/exa/tailscale tag additions; integrations.json (used by landing/SEO) is not regenerated here, so the expanded tags would diverge from getAllBlockMeta(). Keep the url additions, which are not projected into integrations.json. * fix(blocks): correct inaccurate tool URLs flagged in review - spotify: open.spotify.com (web player) -> www.spotify.com brand homepage - sts: point to the STS API reference (aws.amazon.com/sts 404s; STS has no standalone product page) instead of the shared IAM page - microsoft: drop locale-specific /en-us/ segments across Excel, Teams, OneDrive, SharePoint, Outlook, Dataverse, Planner, Entra ID, and shorten OneDrive/Outlook to stable product roots
) * improvement(resource): simplify table shell, toasts, and loading breadcrumbs - Resource.Table: remove internal sorting (defaultSort/sortValues) and the emptyMessage state — rows render in the order given, chrome always paints - Resource: root is now the positioning context for overlays; consumers (files, tables, knowledge, document) wrap detail views in <Resource> instead of hand-rolled divs - ResourceHeader: root titles no longer truncate during initial layout; LocationFocusVeil gates the portal on mount to fix a hydration mismatch - Toasts: drop the StackDismiss ring and stack countdown — each toast runs its own timer; remove the Mod+E clear-notifications command; align toast typography and icons with chip chrome - Breadcrumbs: use the canonical '…' placeholder while names load - incident.io: fix display name and catalog slug (with redirect) - Add dev:capped / dev:full:capped scripts with a 4GB heap cap * feat(scheduled-tasks): calendar views; rename Mothership to Sim/Chat Add month/time calendar views for scheduled tasks with toolbar, event chips, and a create-task modal, backed by calendar-grid and schedule-events utils (with tests) and a use-calendar hook. Replace the old schedule-modal/context-menu flow. Rename the "Mothership" agent to "Sim" and the chat surface to "Chat" across landing copy, constitution, block metadata, API error messages, and copilot/data-drain internals. Drop unused workspace route layouts. * fix(emcn): force dropdown menus modal inside dialogs so they scroll A non-modal DropdownMenu portals outside an open dialog's react-remove-scroll subtree, so its content cannot be wheel-scrolled (e.g. the time picker in the scheduled-task create modal). ModalContent now marks its subtree via an InsideModal context, and the emcn DropdownMenu root upgrades itself to modal inside dialogs so it mounts its own scroll lock and focus scope; page-level menus keep their consumer-chosen modality. Also stretch the create-task modal's date/time chip controls to full width and drop the dead EDGE_GUTTER constant left behind by the equal-tracks calendar layout. * fix(scheduled-tasks): address review — midnight rollover, stub feedback, smooth Today scroll - useCalendar: today was frozen at mount, so after midnight the isToday column highlight and the current-time indicator stayed on the previous day. today is now state refreshed by a sleep-resilient minute poll that only re-renders when the calendar day actually changes - CreateTaskModal: the stub submit closed silently, reading as false success; it now shows an info toast that the task was not created - ScheduleCalendar: Today presses scroll smoothly as an orientation cue; mount and scope switches keep instant positioning * feat(emcn): view-only field primitives + scheduled-task modals - ChipCopyInput (canonical view-only copy field), ChipTimePicker, ChipModalField type='copy', ChipTextarea viewOnly; new border chip variant and shared chipPrimaryFillTokens - migrate ~40 consumers off disabled inputs and the deleted CopyableValueField; ChipConfirmModal description->text and secondaryActions[] API sweep - scheduled-tasks: rename create-task-modal to task-modal, add task-details-modal + task-context-menu, useScheduledTasks hook - home: extract prompt-editor (usePromptEditor) out of user-input * feat(scheduled-tasks): persist + run tasks via the job-schedule backend Wire the calendar UI to the existing sourceType='job' workflow_schedule backend instead of local component state, so tasks persist and actually run as Sim agent invocations. - schema: add contexts (@-mentions resolved into the run), excludedDates (per-occurrence deletes), and endsAt (recurrence end) to workflow_schedule (migration 0235) - contracts/schedules: expose one-time `time`, contexts, endsAt on create; add the exclude_occurrence action; nullable cron in the create response - orchestration: persist the new fields, honor exclusions + end boundary via a shared computeNextRunAt, add performExcludeOccurrence - execution: forward contexts to /api/mothership/execute and recompute the next run through computeNextRunAt - mothership/execute: accept + resolve contexts like the interactive chat path - frontend: replace the local hook with React Query (create/update/delete + exclude-occurrence), expand recurrences into calendar occurrences, add the recurrence control (frequency + end) and the recurring this/all delete dialog * chore(scheduled-tasks): satisfy biome line-width on user-input imports * fix(scheduled-tasks): log agent-context resolution failures in execute route * fix(scheduled-tasks): keep context double-cast adjacent to its boundary annotation * fix(scheduled-tasks): preserve @-mention contexts on edit; sync editor valueRef on input * fix(scheduled-tasks): footer-wrap modal controls; audit fixes; cleanup - chip-modal: footer secondary cluster now wraps (min-w-0 flex-wrap) with a non-shrinking action cluster, so scheduling controls can never clip Cancel/ primary; recurrence labels compacted so the common case stays one row - schedule-execution: failure path now completes a recurring job when maxRuns/ endsAt/exclusions are exhausted (and a one-time/maxRuns job), mirroring the success path instead of leaving it active with a stale nextRunAt - prompt-editor: commitValue keeps valueRef in lockstep with state on the mention-hook setter paths, completing the stale-ref fix - task-modal: preserve @-mention contexts on edit (seed editor.setContexts); single emptiness source of truth - recurrence-control: preserve prior count when toggling end type; drop a needless useMemo - contracts: reuse scheduleContextSchema for the execute contexts shape * feat(scheduled-tasks): valid future default launch time; test scheduleToTasks mapping * chore(scheduled-tasks): convert added inline comments to TSDoc * simplify(scheduled-tasks): reuse date-fns for launch/end math; drop dead 'running' status + single-use helper --------- Co-authored-by: waleed <walif6@gmail.com>
) * feat(hubspot): add notes, emails, properties & associations tools - Add 11 tools: get_properties (read property/enum options), notes (create/get/list/search), email engagements (create/get/list/search), and v4 associations (list/create) - Add scopes: crm.objects.notes.read/write, crm.objects.emails.read/write, sales-email-read (required for email engagement content) - Wire new operations into the HubSpot block (subBlocks, conditions, tool mapping, outputs, BlockMeta templates/skills) - Fix pre-existing bugs found in validation: list_marketing_events list URL (/marketing/marketing-events/v3), appointment property names (hs_appointment_*), get_users output shape (CRM envelope), create_list response unwrap, and stringified-associations parsing in create tools - Regenerate integration docs * fix(hubspot): drop non-grantable notes/emails scopes; remove inline comments - crm.objects.notes.*/crm.objects.emails.* are not grantable HubSpot scopes (would break the OAuth authorize flow). Notes/emails engagement and association endpoints are authorized by crm.objects.contacts.*; sales-email-read remains for email-engagement content - Remove non-TSDoc inline comments from new tool files * fix(hubspot): address review comments - Forward the properties param for the Get Users operation (block param mapping + Properties-to-Return field now include get_users) - Use Record<string, unknown> for create_note/create_email request bodies - Only send Content-Type on create_association when a body is sent (default-association PUT has no body) - Remove stray duplicate JSDoc opener in types.ts
…vel (#5035) * improvement(permissions): permission groups scoped to organization level * chore(db): drop 0235 permission-groups migration to regenerate after staging merge * merge latest staging
#5036) * feat(billing): gate programmatic workflow execution behind a paid plan Block free-plan accounts (hosted only) from running workflows programmatically: API-key/public execute, MCP server, A2A agent server, generic webhooks, and cross-origin chat embeds. Returns 402 (403 for chat embeds) with an upgrade message; provider webhooks, session/browser runs, internal-JWT executor traffic, and self-hosted are unaffected. - Add isApiExecutionEntitled / isWorkspaceApiExecutionEntitled gate helpers - Gate the execute, mcp/serve, a2a/serve, webhooks/trigger, and chat routes - Deploy modal: show an upgrade prompt on the API/MCP/A2A tabs for free users - Upgrade page: rename Pro feature to 'Deploy workflows as APIs'; API endpoint rate-limit row shows 0 for Free * test(billing): mock api-execution gate in webhook + execute route tests These pre-existing tests exercise gated routes with API-key/generic-webhook paths; on hosted (CI) the gate now returns 402. Mock the gate as entitled by default and add a generic-webhook 402 coverage case. * fix(billing): address review findings on the paid-plan gate - execute route: gate on the workflow's workspace billed account (like MCP/A2A/ webhooks/chat) instead of the caller/creator's personal plan, so a paid workspace is never 402'd because an individual is on free - webhooks: all-generic-all-free fan-out now returns 402, not a 500 'No webhooks processed' fallback - deploy modal: hold the gate closed until the subscription query resolves (isFree(undefined) is true) to avoid flashing the upgrade wall at paid users * fix(billing): gate on isBillingEnabled, not isHosted The paywall should follow billing enforcement, not the hostname. Keying off isHosted would still 402 free users on a hosted deployment with BILLING_ENABLED unset. Switch the server gate (api-access, chat embed) to isBillingEnabled and the deploy-modal UI to the client NEXT_PUBLIC_BILLING_ENABLED flag (matching the Inbox paywall), so a billing-disabled deployment skips the gate entirely. * feat(billing): add FREE_API_DEPLOYMENT_GATE_ENABLED kill-switch Gate the programmatic-execution paywall behind a dedicated backend env flag (combined with BILLING_ENABLED), off by default, so it can ship dark and be enabled per-deployment after a backend sanity check. Backend only — the deploy modal UI is unchanged.
…harden CRUD/analytics (#5040) * improvement(salesforce): align tools + block with Salesforce API and harden CRUD/analytics - Migrate all 4 Account tools to shared getInstanceUrl + extractErrorMessage helpers (drop ~40 lines of inlined idToken decode per file) - Use extractErrorMessage consistently across every CRUD tool; add loggers to the opportunity tools - Trim ID path params on all update/delete/single-get tools; URL-encode single-record field lists - Fix dashboard tools: read name/metadata from dashboardMetadata.attributes (was always null); refresh now returns status/statusUrl defensively; drop no-op list_dashboards folderName filter - Expose update_case origin/contact/account and update_task who/what fields end-to-end - Block: mark create-required (Name, LastName, Company, StageName, Subject) and update/delete IDs conditionally required; add account billing*/revenue/employees and contact mailing*/department subBlocks; convert includeDetails to a Yes/No dropdown; move optional fields to advanced mode - Trim over-declared dashboard output types; make Task.Status optional - Regenerate integration docs * fix(salesforce): correct list/refresh response shapes per live API docs - list_dashboards: GET /analytics/dashboards returns a bare top-level array, not a {dashboards} wrapper (fixes always-empty result) - refresh_dashboard: statusUrl is returned at the top level of the PUT response, read it there first - list_reports: the list resource only returns report name/id/url/describeUrl/instancesUrl, so drop the no-op folderName filter and match searchTerm on name only Validated tool-by-tool against the live Salesforce REST/Analytics/Object-Reference docs (API v67.0). * fix(salesforce): address Greptile/Cursor review - Add shared requireId() guard so whitespace-only IDs fail fast instead of producing malformed /sobjects/Object/ empty-path requests (all update/delete/single-get tools) - URL-encode the fields query value in get_opportunities and get_tasks single-record GETs (matching the other get_* tools) - Reflect the Salesforce API success flag consistently across all create tools (success/created use data.success === true) * fix(salesforce): trim echoed output IDs and relation reference IDs - update/delete tools now return output.id from the trimmed ID so chained workflows never receive whitespace-padded IDs - trim relation reference IDs (AccountId, ContactId, WhoId, WhatId) in create/update bodies to avoid Salesforce reference errors from copy-pasted whitespace * fix(salesforce): trim accountId in update_contact body Last remaining untrimmed relation reference ID — update_contact now trims AccountId like the other create/update tools.
…rence (#5038) * feat(scheduled-tasks): position week/day chips at their exact minute Replace hour-bucketed event rendering with a per-day absolute overlay that places each task chip at timeToOffset(start), so a 5:38 task sits at 5:38 instead of the top of the 5:00 cell. Hour cells become click/gridline-only; the overlay is non-interactive so empty-space clicks still create. Removes the now-obsolete eventsByHour/hourKey/bucketEventsByHour path (month view already used eventsByDay). * feat(settings): user timezone preference for scheduled tasks Add a Timezone preference under Settings → General. Scheduled tasks now run in the user's chosen IANA zone instead of whatever device created them. - settings table gains a nullable `timezone` column (migration 0236); null means "use the browser-detected zone", so existing users are unchanged - contract: validated IANA `timezone` on the settings get/update shapes - useTimezone() resolves the saved zone or the browser fallback; the task modal captures it instead of recomputing the device zone - General settings adds a searchable timezone combobox defaulting to the detected zone - shared timezone util (getBrowserTimezone / getSupportedTimezones) * fix(scheduled-tasks): interpret launch/end times in the account timezone Address review: one-time runs and the end-of-day boundary were resolved in the browser zone, so a task could fire at the wrong instant when the account zone differed from the device. Resolve wall-clock launch/end through the account zone (DST-correct), and evaluate the past-launch guard and the default seed in that zone too — matching how the recurring cron is already evaluated. - timezone util: zonedWallClockToUtc (DST-correct, no library), wallClockNow; getSupportedTimezones falls back to a common set and always includes UTC - recurrenceToScheduleFields takes the timezone and resolves time/endsAt in it - settings timezone Label drops its dangling htmlFor - tests for the zone converter (UTC / +5:30 / DST) and the zoned mappings * feat(scheduled-tasks): Google-Calendar-style side-by-side overlap layout Tasks whose pills would collide now split the column into side-by-side lanes (like Google Calendar) instead of stacking on top of each other; tasks that don't overlap keep the full width. Adds a pure layoutColumn lane-assignment helper (interval clustering + greedy lane packing) with tests. * fix(scheduled-tasks): zone-consistent recurrence/edit + duplicate + loading Address review (zone consistency): - recurrenceToCron derives weekday/day-of-month from a UTC-parsed calendar date so the cron targets the right day regardless of device zone - cronToRecurrence + editSeedFor recover the launch date/time and ends-on date read back in the schedule's zone (zonedWallClock), so editing shows the right values when the account zone differs from the device - defaultLaunch no longer compares browser-local slot days against account-zone "today" Features: - right-click Duplicate: opens a pre-filled create modal from any task (TaskEditSeed now extends a shared TaskPrefill; modal gains a prefill prop) - loading.tsx paints only the header chrome (the page is a calendar, not a table) so it no longer pops table -> calendar; the empty calendar loads tasks in - task context menu drops "See details" (finished tasks open on click) * fix(scheduled-tasks): edit/duplicate use the task's own timezone, not the account one A task created in one zone but edited after the account zone changed (or duplicated) seeded its launch in the task's stored zone while validating and submitting in the current account zone, drifting unchanged run times. TaskPrefill now carries the task's timezone; the modal seeds AND submits in it for edit/duplicate, and only blank creates use the account zone. * fix(scheduled-tasks): duplicating a past one-time task seeds a future launch Audit follow-up: a duplicate of a one-time task whose launch already passed opened with Schedule disabled. It now falls through to the next-hour default so the new task is immediately schedulable. Also clarifies the DST spring-forward note on zonedWallClockToUtc and drops a stray test comment. * fix(scheduled-tasks): clear duplicate pre-fill when starting a fresh create The create modal had two open-sources (isCreateOpen + duplicatePrefill) that weren't coordinated. Starting a header/slot create now clears any duplicate pre-fill (and duplicating closes any open create), so a stale duplicate draft can never bleed into a blank or slot-seeded create. * fix(scheduled-tasks): make create/duplicate/edit modals mutually exclusive Opening any of the three modal flows (blank create, duplicate pre-fill, task edit/record) now closes the other two, so the create modal can never co-exist with the edit modal and no stale state survives a switch. * feat(scheduled-tasks): render calendar in the effective timezone Position each occurrence at its wall-clock time in the task's own timezone, and draw the now-line / today highlight in the viewer's effective zone, so the calendar always shows a task at the local time it was scheduled for — matching the modal. Adds zonedClockDate as the single zone boundary; the default case (account zone == browser zone) is unchanged. * fix(scheduled-tasks): re-sync calendar day frame when timezone resolves When useTimezone() resolves from the browser fallback to the saved account zone after mount, re-derive today (and the focused day, while it is still on today) so the grid frame, now-line, and fetched range stay in agreement. The focused day is preserved across the change once the user has navigated away. * fix(scheduled-tasks): pad view window for timezone slop; re-center scroll on zone change visibleRange now expands the rendered span by a day on each side so an occurrence whose own-zone display day is on screen is never filtered out by the account-zone frame; bucketEventsByDay still places each on its zoned day, dropping any off-screen. The week/day auto-scroll re-centers when the effective timezone resolves. * test(scheduled-tasks): pass timezone to cronToRecurrence ends-on case The second cronToRecurrence call omitted the required timezone, so the recovered end date depended on the test runner's system zone instead of the schedule zone. Pin it to UTC for determinism. * fix(scheduled-tasks): re-seed blank-create launch when timezone resolves useTimezone() starts on the browser fallback, so a blank create's next-top-of-the-hour default (and its past-launch guard) could be seeded in the wrong zone and submitted in the resolved account zone. Re-seed the default when the effective zone changes, unless the user has edited the fields; slot/edit/duplicate seeds are zone-stable and untouched. * fix(scheduled-tasks): DST fall-back resolve, today month-cell default, late-night pill bounds Audit follow-ups: - zonedWallClockToUtc resolves to the self-consistent instant, fixing one-time launches on the autumn fall-back day (were an hour early) while keeping the spring-forward gap rolling forward; adds DST regression tests. - defaultLaunch: today's whole-day (month-cell) click defaults to the next top of the hour like the header action, not a past 9am that disables Save. - DayEvents clips to the day bounds so a late-night pill never spills past the final hour row; now-line sits above event pills.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryHigh Risk Overview Enterprise access control moves permission groups from per-workspace to per-organization: CRUD and membership APIs live under Billing gates programmatic execution on a paid workspace billed account via Integrations & docs: HubSpot docs add notes, emails, properties, associations, and appointment field names; Salesforce docs align with API/tool behavior; block authoring requires Schedules & settings: job schedules gain Chat / agent execution: mothership execute accepts Quality & infra: CI adds Reviewed by Cursor Bugbot for commit 4ec26a0. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4ec26a0. Configure here.
| return createErrorResponse('Embedding this chat on external sites requires a paid plan', 403) | ||
| } | ||
|
|
||
| return null |
There was a problem hiding this comment.
Embed gate fails open
Medium Severity
Cross-origin chat embed gating calls isWorkspaceApiExecutionEntitled with an undefined workspace when the workflow row is missing (for example archived). That helper treats a missing workspace as entitled, so third-party embeds can skip the paid-plan check instead of being denied.
Reviewed by Cursor Bugbot for commit 4ec26a0. Configure here.

