Skip to content

v0.7.6: calendar scheduled tasks, new hubspot tools, virtualized chat, db perf improvements#5042

Open
waleedlatif1 wants to merge 17 commits into
mainfrom
staging
Open

v0.7.6: calendar scheduled tasks, new hubspot tools, virtualized chat, db perf improvements#5042
waleedlatif1 wants to merge 17 commits into
mainfrom
staging

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

waleedlatif1 and others added 17 commits June 12, 2026 23:05
…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.
@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 14, 2026 3:53am

Request Review

@cursor

cursor Bot commented Jun 14, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Organization-scoped permission groups and default-group resolution change who is restricted across all workspaces; paid-plan gates on MCP, A2A, and embedded chat affect production API behavior and billing edge cases.

Overview
v0.7.6 bundles product, billing, and enterprise access-control changes with integration and reliability fixes.

Enterprise access control moves permission groups from per-workspace to per-organization: CRUD and membership APIs live under organizations/[id]/permission-groups, entitlement uses org Enterprise billing, and autoAddNewMembers is replaced by a single org isDefault group that applies when a user has no explicit assignment (including external workspace members). The user config endpoint resolves explicit membership first, then the default group, and returns organizationId / isOrgAdmin for UI gating. Docs for access control and permissions are updated to match.

Billing gates programmatic execution on a paid workspace billed account via isWorkspaceApiExecutionEntitled on A2A and MCP serve (402), and blocks third-party chat embeds on free plans with assertChatEmbedAllowed (403) while leaving first-party *.sim.ai origins alone.

Integrations & docs: HubSpot docs add notes, emails, properties, associations, and appointment field names; Salesforce docs align with API/tool behavior; block authoring requires url on BlockMeta for external services; Sim trigger docs rename execution events to Run Error/Success.

Schedules & settings: job schedules gain time, endsAt, contexts, and an exclude_occurrence action; user settings add a persisted timezone (calendar / minute-granular scheduling).

Chat / agent execution: mothership execute accepts contexts and runs processContextsServer so headless runs get the same agent mentions as the chat UI; user-facing copy shifts from Mothership/Copilot to Chat/Sim across README, landing, and constitution rules.

Quality & infra: CI adds bun run check:react-query; OAuth refresh coalescing catches coalesce failures; schedule advisory locks use hashtextextended; emcn rules document ChipCopyInput, ChipTimePicker, and modal copy fields.

Reviewed by Cursor Bugbot for commit 4ec26a0. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4ec26a0. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants