@agent-native/core 0.154.5 → 0.157.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
  3. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
  4. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
  5. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
  6. package/corpus/templates/analytics/AGENTS.md +3 -0
  7. package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
  8. package/corpus/templates/analytics/actions/bigquery.ts +1 -1
  9. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
  10. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  11. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
  12. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
  13. package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
  14. package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
  15. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
  16. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
  17. package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
  18. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  19. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  20. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
  21. package/corpus/templates/calendar/AGENTS.md +2 -0
  22. package/corpus/templates/calendar/DEVELOPING.md +4 -4
  23. package/corpus/templates/calendar/actions/get-settings.ts +4 -11
  24. package/corpus/templates/calendar/actions/update-availability.ts +15 -4
  25. package/corpus/templates/calendar/actions/update-settings.ts +15 -4
  26. package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
  27. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
  28. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
  29. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
  30. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
  31. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
  32. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
  33. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
  34. package/corpus/templates/calendar/app/i18n-data.ts +30 -0
  35. package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
  36. package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
  37. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
  38. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
  39. package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
  40. package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
  41. package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
  42. package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
  43. package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
  44. package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
  45. package/corpus/templates/calendar/shared/api.ts +1 -0
  46. package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
  47. package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
  48. package/corpus/templates/calendar/shared/settings.ts +44 -0
  49. package/corpus/templates/clips/AGENTS.md +2 -0
  50. package/corpus/templates/clips/actions/add-comment.ts +5 -2
  51. package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
  52. package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
  53. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
  54. package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
  55. package/corpus/templates/clips/actions/update-comment.ts +9 -2
  56. package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
  57. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
  58. package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
  59. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
  60. package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
  61. package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
  62. package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
  63. package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
  64. package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
  65. package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
  66. package/corpus/templates/clips/app/root.tsx +2 -1
  67. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
  68. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
  69. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
  70. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +29 -8
  71. package/corpus/templates/clips/app/routes/share.$shareId.tsx +24 -6
  72. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
  73. package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
  74. package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
  75. package/corpus/templates/clips/desktop/src/app.tsx +116 -19
  76. package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
  77. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
  78. package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
  79. package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
  80. package/corpus/templates/clips/desktop/src/styles.css +117 -0
  81. package/corpus/templates/clips/server/db/index.ts +10 -0
  82. package/corpus/templates/clips/server/db/schema.ts +1 -0
  83. package/corpus/templates/clips/server/lib/recordings.ts +3 -1
  84. package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
  85. package/corpus/templates/clips/server/plugins/db.ts +5 -0
  86. package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
  87. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
  88. package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
  89. package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
  90. package/corpus/templates/content/actions/add-comment.ts +2 -1
  91. package/corpus/templates/content/actions/update-comment.ts +1 -1
  92. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
  93. package/corpus/templates/design/actions/generate-design.ts +10 -7
  94. package/corpus/templates/design/actions/present-design-variants.ts +35 -6
  95. package/corpus/templates/design/actions/run-design-audit.ts +33 -0
  96. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
  97. package/corpus/templates/design/shared/design-review.ts +1 -0
  98. package/corpus/templates/design/shared/html-integrity.ts +66 -0
  99. package/corpus/templates/dispatch/.env.example +5 -0
  100. package/corpus/templates/dispatch/AGENTS.md +24 -19
  101. package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
  102. package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
  103. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
  104. package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
  105. package/corpus/templates/factory/AGENTS.md +5 -1
  106. package/corpus/templates/factory/README.md +23 -0
  107. package/corpus/templates/factory/actions/view-screen.ts +17 -0
  108. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
  109. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
  110. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
  111. package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
  112. package/corpus/templates/factory/app/routes/factory.tsx +41 -0
  113. package/corpus/templates/factory/package.json +1 -0
  114. package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
  115. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
  116. package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
  117. package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
  118. package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
  119. package/corpus/templates/mail/vite.config.ts +7 -0
  120. package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
  121. package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
  122. package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
  123. package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
  124. package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
  125. package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
  126. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
  127. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  128. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  129. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  130. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  131. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  132. package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
  133. package/corpus/templates/slides/app/global.css +7 -40
  134. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  135. package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
  136. package/dist/agent/index.d.ts +1 -1
  137. package/dist/agent/production-agent.d.ts +30 -0
  138. package/dist/agent/production-agent.js +105 -9
  139. package/dist/agent/thread-data-builder.js +17 -1
  140. package/dist/agent/types.d.ts +9 -0
  141. package/dist/cli/skills-content/help.js +6 -4
  142. package/dist/cli/skills.js +17 -2
  143. package/dist/cli/workspacify.d.ts +5 -0
  144. package/dist/cli/workspacify.js +65 -8
  145. package/dist/client/AgentPanel.d.ts +5 -0
  146. package/dist/client/AgentPanel.js +32 -13
  147. package/dist/client/AssistantChat.d.ts +36 -1
  148. package/dist/client/AssistantChat.js +175 -110
  149. package/dist/client/MultiTabAssistantChat.js +44 -27
  150. package/dist/client/active-run-state.d.ts +14 -1
  151. package/dist/client/active-run-state.js +0 -0
  152. package/dist/client/agent-chat-adapter.js +107 -5
  153. package/dist/client/analytics.js +9 -2
  154. package/dist/client/chat/markdown-renderer.js +4 -2
  155. package/dist/client/chat/message-components.d.ts +2 -2
  156. package/dist/client/chat/message-components.js +69 -35
  157. package/dist/client/chat/run-recovery.d.ts +4 -1
  158. package/dist/client/chat/run-recovery.js +62 -30
  159. package/dist/client/chat/tool-call-display.d.ts +12 -3
  160. package/dist/client/chat/tool-call-display.js +79 -40
  161. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  162. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  163. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  164. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  165. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  166. package/dist/client/chat-first/apps-rail.js +9 -4
  167. package/dist/client/chat-first/primary-nav.d.ts +2 -1
  168. package/dist/client/chat-first/primary-nav.js +3 -3
  169. package/dist/client/chat-first/types.d.ts +5 -1
  170. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  171. package/dist/client/components/ui/message-scroller.js +7 -3
  172. package/dist/client/composer/runtime-adapters.js +3 -2
  173. package/dist/client/context-xray/ContextMeter.js +5 -1
  174. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  175. package/dist/client/error-format.d.ts +4 -0
  176. package/dist/client/error-format.js +93 -5
  177. package/dist/client/i18n.js +83 -17
  178. package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
  179. package/dist/client/markdown/InlineMarkdown.js +65 -0
  180. package/dist/client/markdown/index.d.ts +1 -0
  181. package/dist/client/markdown/index.js +1 -0
  182. package/dist/client/observability/ThumbsFeedback.js +10 -8
  183. package/dist/client/review/ReviewThreadPanel.js +2 -1
  184. package/dist/client/run-stream-ownership.d.ts +39 -0
  185. package/dist/client/run-stream-ownership.js +65 -0
  186. package/dist/client/sharing/ShareButton.js +160 -54
  187. package/dist/coding-tools/run-code.js +2 -1
  188. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  189. package/dist/coding-tools/sandbox/background.js +8 -1
  190. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  191. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  192. package/dist/collab/awareness.d.ts +2 -2
  193. package/dist/collab/routes.d.ts +1 -1
  194. package/dist/collab/struct-routes.d.ts +1 -1
  195. package/dist/feature-flags/index.d.ts +1 -1
  196. package/dist/feature-flags/index.js +1 -1
  197. package/dist/feature-flags/store.d.ts +10 -0
  198. package/dist/feature-flags/store.js +112 -1
  199. package/dist/guards/no-env-credentials.js +4 -0
  200. package/dist/index.d.ts +2 -2
  201. package/dist/index.js +1 -1
  202. package/dist/integrations/adapters/slack.js +5 -1
  203. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  204. package/dist/localization/core-messages/ar-SA.js +541 -0
  205. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  206. package/dist/localization/core-messages/de-DE.js +513 -0
  207. package/dist/localization/core-messages/en-US.d.ts +513 -0
  208. package/dist/localization/core-messages/en-US.js +513 -0
  209. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  210. package/dist/localization/core-messages/es-ES.js +520 -0
  211. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  212. package/dist/localization/core-messages/fr-FR.js +520 -0
  213. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  214. package/dist/localization/core-messages/hi-IN.js +513 -0
  215. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  216. package/dist/localization/core-messages/ja-JP.js +506 -0
  217. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  218. package/dist/localization/core-messages/ko-KR.js +506 -0
  219. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  220. package/dist/localization/core-messages/pt-BR.js +520 -0
  221. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  222. package/dist/localization/core-messages/zh-CN.js +506 -0
  223. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  224. package/dist/localization/core-messages/zh-TW.js +506 -0
  225. package/dist/localization/core-messages.d.ts +524 -0
  226. package/dist/localization/core-messages.js +124 -0
  227. package/dist/localization/default-messages.d.ts +3 -0
  228. package/dist/localization/default-messages.js +3 -0
  229. package/dist/notifications/routes.d.ts +1 -1
  230. package/dist/observability/routes.d.ts +2 -2
  231. package/dist/progress/routes.d.ts +1 -1
  232. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  233. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  234. package/dist/resources/agents.js +40 -5
  235. package/dist/resources/metadata.d.ts +10 -0
  236. package/dist/resources/metadata.js +7 -3
  237. package/dist/review/actions/create-review-comment.js +1 -1
  238. package/dist/review/actions/reply-review-comment.js +1 -1
  239. package/dist/review/actions/resolve-review-thread.js +1 -1
  240. package/dist/secrets/routes.d.ts +6 -6
  241. package/dist/server/action-routes.js +32 -6
  242. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  243. package/dist/server/agent-chat-plugin.d.ts +10 -0
  244. package/dist/server/agent-chat-plugin.js +144 -41
  245. package/dist/server/agent-discovery.js +14 -3
  246. package/dist/server/agent-run-context.d.ts +7 -1
  247. package/dist/server/agent-run-context.js +9 -2
  248. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  249. package/dist/server/agent-teams.js +38 -9
  250. package/dist/server/core-routes-plugin.js +1 -1
  251. package/dist/server/identity-sso-migrations.d.ts +7 -0
  252. package/dist/server/identity-sso-migrations.js +36 -0
  253. package/dist/server/identity-sso-store.d.ts +3 -2
  254. package/dist/server/identity-sso-store.js +8 -3
  255. package/dist/server/index.d.ts +1 -1
  256. package/dist/server/realtime-token.d.ts +1 -1
  257. package/dist/server/release-migrations.js +4 -0
  258. package/dist/server/request-context.d.ts +7 -0
  259. package/dist/server/request-context.js +15 -0
  260. package/dist/tracking/providers.js +13 -3
  261. package/dist/workspace-files/tool.js +1 -1
  262. package/docs/content/agent-surfaces.mdx +30 -0
  263. package/package.json +3 -2
@@ -14,6 +14,7 @@ import type {
14
14
  CustomField,
15
15
  DaySchedule,
16
16
  } from "@shared/api";
17
+ import { getWeekdayOrder, getWeekStartsOn } from "@shared/calendar-week";
17
18
  import {
18
19
  IconBrandGoogle,
19
20
  IconBrandZoom,
@@ -113,7 +114,16 @@ import {
113
114
  OPTIMISTIC_PREFIX,
114
115
  } from "@/hooks/use-booking-links";
115
116
  import { useGoogleAuthStatus } from "@/hooks/use-google-auth";
117
+ import { useSettings } from "@/hooks/use-settings";
116
118
  import { useZoomStatus, useConnectZoom } from "@/hooks/use-zoom-auth";
119
+ import {
120
+ DEFAULT_TIME_SLOT,
121
+ addTimeSlot,
122
+ getEditableTimeSlots,
123
+ removeTimeSlot,
124
+ setDayEnabled,
125
+ updateTimeSlot,
126
+ } from "@/lib/availability-schedule";
117
127
  import { copyTextToClipboard } from "@/lib/clipboard";
118
128
  import { cn } from "@/lib/utils";
119
129
 
@@ -175,7 +185,7 @@ const DAYS: { key: DayName }[] = [
175
185
 
176
186
  const DEFAULT_SCHEDULE: DaySchedule = {
177
187
  enabled: false,
178
- slots: [{ start: "09:00", end: "17:00" }],
188
+ slots: [{ ...DEFAULT_TIME_SLOT }],
179
189
  };
180
190
 
181
191
  type Tab = "links" | "availability" | "bookings";
@@ -290,11 +300,12 @@ function formatAvailabilitySummary(
290
300
  dayLabel = enabledDays.map((d) => shortNames[d]).join(", ");
291
301
  }
292
302
 
293
- // Find common time range
294
- const slot = ws[enabledDays[0]].slots[0];
295
- if (!slot) return dayLabel;
303
+ const slots = ws[enabledDays[0]].slots;
304
+ if (slots.length === 0) return dayLabel;
296
305
 
297
- return `${dayLabel}, ${formatTime12(slot.start)} - ${formatTime12(slot.end)}`;
306
+ return `${dayLabel}, ${slots
307
+ .map((slot) => `${formatTime12(slot.start)} - ${formatTime12(slot.end)}`)
308
+ .join(", ")}`;
298
309
  }
299
310
 
300
311
  function BookingLinksListSkeleton() {
@@ -697,17 +708,36 @@ export default function BookingLinksPage({
697
708
  function updateDay(day: DayName, updates: Partial<DaySchedule>) {
698
709
  setSchedule((prev) => ({
699
710
  ...prev,
700
- [day]: { ...prev[day], ...updates },
711
+ [day]:
712
+ typeof updates.enabled === "boolean"
713
+ ? setDayEnabled(prev[day], updates.enabled)
714
+ : { ...prev[day], ...updates },
701
715
  }));
702
716
  }
703
717
 
704
- function updateDaySlot(day: DayName, field: "start" | "end", value: string) {
718
+ function updateDaySlot(
719
+ day: DayName,
720
+ slotIndex: number,
721
+ field: "start" | "end",
722
+ value: string,
723
+ ) {
705
724
  setSchedule((prev) => ({
706
725
  ...prev,
707
- [day]: {
708
- ...prev[day],
709
- slots: [{ ...prev[day].slots[0], [field]: value }],
710
- },
726
+ [day]: updateTimeSlot(prev[day], slotIndex, field, value),
727
+ }));
728
+ }
729
+
730
+ function addDaySlot(day: DayName) {
731
+ setSchedule((prev) => ({
732
+ ...prev,
733
+ [day]: addTimeSlot(prev[day]),
734
+ }));
735
+ }
736
+
737
+ function removeDaySlot(day: DayName, slotIndex: number) {
738
+ setSchedule((prev) => ({
739
+ ...prev,
740
+ [day]: removeTimeSlot(prev[day], slotIndex),
711
741
  }));
712
742
  }
713
743
 
@@ -1756,7 +1786,7 @@ export default function BookingLinksPage({
1756
1786
  </div>
1757
1787
  {DAYS.map(({ key }) => {
1758
1788
  const day = schedule[key];
1759
- const slot = day.slots[0] ?? { start: "09:00", end: "17:00" };
1789
+ const slots = getEditableTimeSlots(day);
1760
1790
  const label = t(`bookingLinks.days.${key}`);
1761
1791
  const short = t(`bookingLinks.days.${key}Short`);
1762
1792
  return (
@@ -1778,26 +1808,65 @@ export default function BookingLinksPage({
1778
1808
  </div>
1779
1809
 
1780
1810
  {day.enabled ? (
1781
- <div className="flex items-center gap-2">
1782
- <Input
1783
- type="time"
1784
- value={slot.start}
1785
- onChange={(e) =>
1786
- updateDaySlot(key, "start", e.target.value)
1787
- }
1788
- className="w-28 sm:w-32"
1789
- />
1790
- <span className="text-muted-foreground">
1791
- {t("bookingLinks.to")}
1792
- </span>
1793
- <Input
1794
- type="time"
1795
- value={slot.end}
1796
- onChange={(e) =>
1797
- updateDaySlot(key, "end", e.target.value)
1798
- }
1799
- className="w-28 sm:w-32"
1800
- />
1811
+ <div className="min-w-0 flex-1 space-y-2">
1812
+ {slots.map((slot, slotIndex) => (
1813
+ <div
1814
+ key={`${key}-${slotIndex}`}
1815
+ className="flex flex-wrap items-center gap-2"
1816
+ >
1817
+ <Input
1818
+ type="time"
1819
+ value={slot.start}
1820
+ onChange={(e) =>
1821
+ updateDaySlot(
1822
+ key,
1823
+ slotIndex,
1824
+ "start",
1825
+ e.target.value,
1826
+ )
1827
+ }
1828
+ className="w-28 sm:w-32"
1829
+ />
1830
+ <span className="text-muted-foreground">
1831
+ {t("bookingLinks.to")}
1832
+ </span>
1833
+ <Input
1834
+ type="time"
1835
+ value={slot.end}
1836
+ onChange={(e) =>
1837
+ updateDaySlot(
1838
+ key,
1839
+ slotIndex,
1840
+ "end",
1841
+ e.target.value,
1842
+ )
1843
+ }
1844
+ className="w-28 sm:w-32"
1845
+ />
1846
+ {slots.length > 1 && (
1847
+ <Button
1848
+ type="button"
1849
+ variant="ghost"
1850
+ size="sm"
1851
+ className="h-8 px-2 text-muted-foreground hover:text-destructive"
1852
+ onClick={() => removeDaySlot(key, slotIndex)}
1853
+ >
1854
+ <IconTrash className="mr-1.5 h-3.5 w-3.5" />
1855
+ {t("eventForm.delete")}
1856
+ </Button>
1857
+ )}
1858
+ </div>
1859
+ ))}
1860
+ <Button
1861
+ type="button"
1862
+ variant="ghost"
1863
+ size="sm"
1864
+ className="h-8 px-2"
1865
+ onClick={() => addDaySlot(key)}
1866
+ >
1867
+ <IconPlus className="mr-1.5 h-3.5 w-3.5" />
1868
+ {t("bookingLinks.add")}
1869
+ </Button>
1801
1870
  </div>
1802
1871
  ) : (
1803
1872
  <span className="text-sm text-muted-foreground">
@@ -1971,6 +2040,8 @@ function BookingPreview({
1971
2040
  onCollapse?: () => void;
1972
2041
  }) {
1973
2042
  const t = useT();
2043
+ const { data: settings } = useSettings();
2044
+ const weekStartsOn = getWeekStartsOn(settings?.weekStart);
1974
2045
  const displayTitle = title.trim() || t("bookingLinks.untitledMeeting");
1975
2046
  const hasDurationChoice = durations.length > 1;
1976
2047
  const primaryDuration = durations[0] ?? 30;
@@ -2005,8 +2076,8 @@ function BookingPreview({
2005
2076
  // Calendar data for viewed month
2006
2077
  const monthStart = startOfMonth(viewMonth);
2007
2078
  const monthEnd = endOfMonth(viewMonth);
2008
- const calStart = startOfWeek(monthStart);
2009
- const calEnd = endOfWeek(monthEnd);
2079
+ const calStart = startOfWeek(monthStart, { weekStartsOn });
2080
+ const calEnd = endOfWeek(monthEnd, { weekStartsOn });
2010
2081
  const calDays = eachDayOfInterval({ start: calStart, end: calEnd });
2011
2082
 
2012
2083
  function isDayDisabled(day: Date) {
@@ -2027,28 +2098,28 @@ function BookingPreview({
2027
2098
  const dayName = DAY_MAP[getDay(selectedDate)];
2028
2099
  const daySchedule = availability.weeklySchedule[dayName];
2029
2100
  if (!daySchedule?.enabled) return [];
2030
- const slot = daySchedule.slots[0];
2031
- if (!slot) return [];
2032
2101
 
2033
2102
  const dur = selectedDuration ?? primaryDuration;
2034
- const [startH, startM] = slot.start.split(":").map(Number);
2035
- const [endH, endM] = slot.end.split(":").map(Number);
2036
- const startMin = startH * 60 + startM;
2037
- const endMin = endH * 60 + endM;
2038
2103
  const slots: string[] = [];
2039
- const firstStart =
2040
- Math.ceil(startMin / BOOKING_SLOT_STEP_MINUTES) *
2041
- BOOKING_SLOT_STEP_MINUTES;
2042
- for (
2043
- let m = firstStart;
2044
- m + dur <= endMin;
2045
- m += BOOKING_SLOT_STEP_MINUTES
2046
- ) {
2047
- const h = Math.floor(m / 60);
2048
- const mm = m % 60;
2049
- const ampm = h >= 12 ? "PM" : "AM";
2050
- const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
2051
- slots.push(`${h12}:${mm.toString().padStart(2, "0")} ${ampm}`);
2104
+ for (const slot of daySchedule.slots) {
2105
+ const [startH, startM] = slot.start.split(":").map(Number);
2106
+ const [endH, endM] = slot.end.split(":").map(Number);
2107
+ const startMin = startH * 60 + startM;
2108
+ const endMin = endH * 60 + endM;
2109
+ const firstStart =
2110
+ Math.ceil(startMin / BOOKING_SLOT_STEP_MINUTES) *
2111
+ BOOKING_SLOT_STEP_MINUTES;
2112
+ for (
2113
+ let m = firstStart;
2114
+ m + dur <= endMin;
2115
+ m += BOOKING_SLOT_STEP_MINUTES
2116
+ ) {
2117
+ const h = Math.floor(m / 60);
2118
+ const mm = m % 60;
2119
+ const ampm = h >= 12 ? "PM" : "AM";
2120
+ const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
2121
+ slots.push(`${h12}:${mm.toString().padStart(2, "0")} ${ampm}`);
2122
+ }
2052
2123
  }
2053
2124
  return slots;
2054
2125
  }, [selectedDate, selectedDuration, primaryDuration, availability]);
@@ -2307,14 +2378,17 @@ function BookingPreview({
2307
2378
 
2308
2379
  {/* Weekday headers */}
2309
2380
  <div className="grid grid-cols-7 mb-0.5">
2310
- {WEEKDAY_HEADER_KEYS.map((dayKey) => (
2311
- <div
2312
- key={dayKey}
2313
- className="py-0.5 text-center text-[10px] font-medium text-muted-foreground/60"
2314
- >
2315
- {t(`bookingLinks.days.${dayKey}`)}
2316
- </div>
2317
- ))}
2381
+ {getWeekdayOrder(weekStartsOn).map((day) => {
2382
+ const dayKey = WEEKDAY_HEADER_KEYS[day];
2383
+ return (
2384
+ <div
2385
+ key={dayKey}
2386
+ className="py-0.5 text-center text-[10px] font-medium text-muted-foreground/60"
2387
+ >
2388
+ {t(`bookingLinks.days.${dayKey}`)}
2389
+ </div>
2390
+ );
2391
+ })}
2318
2392
  </div>
2319
2393
 
2320
2394
  {/* Days grid */}
@@ -5,6 +5,7 @@ import {
5
5
  StarfieldBackground,
6
6
  } from "@agent-native/core/client/ui";
7
7
  import type { Booking } from "@shared/api";
8
+ import { getWeekStartsOn } from "@shared/calendar-week";
8
9
  import { IconAlertTriangle, IconCalendar } from "@tabler/icons-react";
9
10
  import {
10
11
  addMinutes,
@@ -432,6 +433,7 @@ export default function BookingPage() {
432
433
  availabilityLoading={availableDatesLoading}
433
434
  viewMonth={viewMonth}
434
435
  onViewMonthChange={setViewMonth}
436
+ weekStartsOn={getWeekStartsOn(settings?.weekStart)}
435
437
  />
436
438
  </div>
437
439
  )}
@@ -6,6 +6,7 @@ import type {
6
6
  CalendarEventDraft,
7
7
  UpdateEventScope,
8
8
  } from "@shared/api";
9
+ import { getWeekStartsOn } from "@shared/calendar-week";
9
10
  import {
10
11
  IconCheck,
11
12
  IconChevronLeft,
@@ -89,8 +90,8 @@ import {
89
90
  buildEventTitleUpdate,
90
91
  dateTimeInTimezoneToIso,
91
92
  getEditableEventTitle,
92
- getLocalTimezone,
93
93
  UNNAMED_EVENT_TITLE,
94
+ resolveEventTimezone,
94
95
  } from "@/lib/event-form-utils";
95
96
  import { buildDeleteEventMutationInput } from "@/lib/event-mutation-inputs";
96
97
  import { getLocationSuggestions } from "@/lib/location-suggestions";
@@ -373,6 +374,7 @@ export default function CalendarView() {
373
374
  const defaultAccountEmail = googleStatus.data?.accounts?.[0]?.email;
374
375
  const settingsQuery = useSettings();
375
376
  const { data: settings } = settingsQuery;
377
+ const weekStartsOn = getWeekStartsOn(settings?.weekStart);
376
378
  const { data: rawOverlayPeople } = useOverlayPeople();
377
379
  const overlayPeople = Array.isArray(rawOverlayPeople) ? rawOverlayPeople : [];
378
380
  const overlayEmails = useMemo(
@@ -398,14 +400,14 @@ export default function CalendarView() {
398
400
  const ms = startOfMonth(selectedDate);
399
401
  const me = endOfMonth(selectedDate);
400
402
  return {
401
- from: startOfWeek(ms).toISOString(),
402
- to: endOfWeek(me).toISOString(),
403
+ from: startOfWeek(ms, { weekStartsOn }).toISOString(),
404
+ to: endOfWeek(me, { weekStartsOn }).toISOString(),
403
405
  };
404
406
  }
405
407
  case "week": {
406
408
  return {
407
- from: startOfWeek(selectedDate).toISOString(),
408
- to: endOfWeek(selectedDate).toISOString(),
409
+ from: startOfWeek(selectedDate, { weekStartsOn }).toISOString(),
410
+ to: endOfWeek(selectedDate, { weekStartsOn }).toISOString(),
409
411
  };
410
412
  }
411
413
  case "day": {
@@ -416,7 +418,7 @@ export default function CalendarView() {
416
418
  return { from: dayStart.toISOString(), to: dayEnd.toISOString() };
417
419
  }
418
420
  }
419
- }, [viewMode, selectedDate]);
421
+ }, [viewMode, selectedDate, weekStartsOn]);
420
422
 
421
423
  const {
422
424
  data: rawEventsData,
@@ -470,8 +472,8 @@ export default function CalendarView() {
470
472
  const next = addMonths(selectedDate, 1);
471
473
  const prev = subMonths(selectedDate, 1);
472
474
  return [next, prev].map((d) => ({
473
- from: startOfWeek(startOfMonth(d)).toISOString(),
474
- to: endOfWeek(endOfMonth(d)).toISOString(),
475
+ from: startOfWeek(startOfMonth(d), { weekStartsOn }).toISOString(),
476
+ to: endOfWeek(endOfMonth(d), { weekStartsOn }).toISOString(),
475
477
  }));
476
478
  }
477
479
  case "week": {
@@ -480,8 +482,8 @@ export default function CalendarView() {
480
482
  const next2 = addWeeks(selectedDate, 2);
481
483
  const prev = subWeeks(selectedDate, 1);
482
484
  return [next, next2, prev].map((d) => ({
483
- from: startOfWeek(d).toISOString(),
484
- to: endOfWeek(d).toISOString(),
485
+ from: startOfWeek(d, { weekStartsOn }).toISOString(),
486
+ to: endOfWeek(d, { weekStartsOn }).toISOString(),
485
487
  }));
486
488
  }
487
489
  case "day": {
@@ -500,7 +502,14 @@ export default function CalendarView() {
500
502
  for (const range of ranges) {
501
503
  void prefetchEvents(queryClient, range.from, range.to, overlayEmails);
502
504
  }
503
- }, [isLoading, viewMode, selectedDate, overlayEmails, queryClient]);
505
+ }, [
506
+ isLoading,
507
+ viewMode,
508
+ selectedDate,
509
+ overlayEmails,
510
+ queryClient,
511
+ weekStartsOn,
512
+ ]);
504
513
 
505
514
  // Show the skeleton only when there is genuinely nothing to show for the
506
515
  // current date range — the first load, or navigating to a range we have not
@@ -675,7 +684,9 @@ export default function CalendarView() {
675
684
  }
676
685
 
677
686
  const location = draft.location ?? draft.workingLocationLabel ?? "";
678
- const timezone = draft.startTimeZone ?? getLocalTimezone();
687
+ const timezone = resolveEventTimezone(
688
+ draft.startTimeZone ?? draft.endTimeZone,
689
+ );
679
690
  const semanticFullDay =
680
691
  eventType === "outOfOffice" &&
681
692
  draft.fullDay === true &&
@@ -1143,7 +1154,9 @@ export default function CalendarView() {
1143
1154
  if (newEnd.getTime() <= newStart.getTime()) return;
1144
1155
 
1145
1156
  if (calendarDraftIdFromEventId(eventId)) {
1146
- const timezone = settings?.timezone || getLocalTimezone();
1157
+ const timezone = resolveEventTimezone(
1158
+ event.startTimeZone ?? event.endTimeZone,
1159
+ );
1147
1160
  updateDraftEvent(eventId, {
1148
1161
  start: newStart.toISOString(),
1149
1162
  end: newEnd.toISOString(),
@@ -1592,8 +1605,8 @@ export default function CalendarView() {
1592
1605
  ? format(selectedDate, "MMM yyyy")
1593
1606
  : format(selectedDate, "MMMM yyyy");
1594
1607
  case "week": {
1595
- const ws = startOfWeek(selectedDate);
1596
- const we = endOfWeek(selectedDate);
1608
+ const ws = startOfWeek(selectedDate, { weekStartsOn });
1609
+ const we = endOfWeek(selectedDate, { weekStartsOn });
1597
1610
  return isMobile
1598
1611
  ? `${format(ws, "MMM d")} – ${format(we, "d")}`
1599
1612
  : `${format(ws, "MMM d")} – ${format(we, "d, yyyy")}`;
@@ -1801,6 +1814,7 @@ export default function CalendarView() {
1801
1814
  onDraftCreate={createDraftEvent}
1802
1815
  onDraftDiscard={discardDraftEvent}
1803
1816
  isLoading={eventsLoading}
1817
+ weekStartsOn={weekStartsOn}
1804
1818
  />
1805
1819
  )}
1806
1820
  {viewMode === "week" && (
@@ -1819,6 +1833,7 @@ export default function CalendarView() {
1819
1833
  onDraftCreate={createDraftEvent}
1820
1834
  onDraftDiscard={discardDraftEvent}
1821
1835
  isLoading={eventsLoading}
1836
+ weekStartsOn={weekStartsOn}
1822
1837
  />
1823
1838
  )}
1824
1839
  {viewMode === "day" && (
@@ -14,6 +14,8 @@ import {
14
14
  AppearancePicker,
15
15
  type AppearancePresetId,
16
16
  } from "@agent-native/core/client/ui";
17
+ import type { CalendarWeekStart } from "@shared/calendar-week";
18
+ import { isCalendarWeekStart } from "@shared/calendar-week";
17
19
  import {
18
20
  IconBrandZoom,
19
21
  IconExternalLink,
@@ -38,6 +40,13 @@ import {
38
40
  } from "@/components/ui/card";
39
41
  import { Input } from "@/components/ui/input";
40
42
  import { Label } from "@/components/ui/label";
43
+ import {
44
+ Select,
45
+ SelectContent,
46
+ SelectItem,
47
+ SelectTrigger,
48
+ SelectValue,
49
+ } from "@/components/ui/select";
41
50
  import { Separator } from "@/components/ui/separator";
42
51
  import { Textarea } from "@/components/ui/textarea";
43
52
  import {
@@ -56,6 +65,8 @@ import { shouldOfferGoogleOAuthSetup } from "@/lib/google-oauth-setup";
56
65
 
57
66
  import changelog from "../../CHANGELOG.md?raw";
58
67
 
68
+ const AVAILABILITY_SETTINGS_PATH = "/booking-links?tab=availability";
69
+
59
70
  export default function Settings() {
60
71
  const t = useT();
61
72
  const agentSettingsTabs = useAgentSettingsTabs();
@@ -83,6 +94,7 @@ export default function Settings() {
83
94
  const [bookingTitle, setBookingTitle] = useState("");
84
95
  const [bookingDescription, setBookingDescription] = useState("");
85
96
  const [defaultDuration, setDefaultDuration] = useState(30);
97
+ const [weekStart, setWeekStart] = useState<CalendarWeekStart>("sunday");
86
98
 
87
99
  useEffect(() => {
88
100
  if (settings) {
@@ -90,6 +102,9 @@ export default function Settings() {
90
102
  setBookingTitle(settings.bookingPageTitle);
91
103
  setBookingDescription(settings.bookingPageDescription);
92
104
  setDefaultDuration(settings.defaultEventDuration);
105
+ setWeekStart(
106
+ isCalendarWeekStart(settings.weekStart) ? settings.weekStart : "sunday",
107
+ );
93
108
  }
94
109
  }, [settings]);
95
110
 
@@ -100,6 +115,7 @@ export default function Settings() {
100
115
  bookingPageTitle: bookingTitle,
101
116
  bookingPageDescription: bookingDescription,
102
117
  defaultEventDuration: defaultDuration,
118
+ weekStart,
103
119
  },
104
120
  {
105
121
  onSuccess: () => toast.success(t("settings.saved")),
@@ -185,9 +201,16 @@ export default function Settings() {
185
201
  {
186
202
  id: "calendar-general",
187
203
  label: t("settings.general"),
188
- keywords: "timezone booking duration defaults general",
204
+ keywords:
205
+ "timezone week start sunday monday booking duration defaults general",
189
206
  hash: "general-settings",
190
207
  },
208
+ {
209
+ id: "calendar-availability",
210
+ label: t("bookingLinks.availability"),
211
+ keywords: "availability available hours booking schedule working hours",
212
+ hash: "availability",
213
+ },
191
214
  {
192
215
  id: "calendar-appearance",
193
216
  label: t("settings.appearance"),
@@ -240,6 +263,18 @@ export default function Settings() {
240
263
  }}
241
264
  />
242
265
  </SettingsRow>
266
+ <SettingsRow
267
+ id="availability"
268
+ label={t("bookingLinks.availability")}
269
+ description={t("bookingLinks.availabilityDescription")}
270
+ control={
271
+ <Button variant="outline" size="sm" asChild>
272
+ <Link to={AVAILABILITY_SETTINGS_PATH}>
273
+ {t("bookingLinks.availability")}
274
+ </Link>
275
+ </Button>
276
+ }
277
+ />
243
278
  </SettingsGroup>
244
279
 
245
280
  {/* Google Calendar Connection */}
@@ -412,6 +447,30 @@ export default function Settings() {
412
447
  <TimezoneCombobox value={timezone} onChange={setTimezone} />
413
448
  </div>
414
449
 
450
+ <div className="space-y-2">
451
+ <Label htmlFor="week-start">
452
+ {t("settings.weekStartLabel")}
453
+ </Label>
454
+ <Select
455
+ value={weekStart}
456
+ onValueChange={(value) => {
457
+ if (isCalendarWeekStart(value)) setWeekStart(value);
458
+ }}
459
+ >
460
+ <SelectTrigger id="week-start" className="w-full sm:w-56">
461
+ <SelectValue />
462
+ </SelectTrigger>
463
+ <SelectContent>
464
+ <SelectItem value="sunday">
465
+ {t("settings.weekStartSunday")}
466
+ </SelectItem>
467
+ <SelectItem value="monday">
468
+ {t("settings.weekStartMonday")}
469
+ </SelectItem>
470
+ </SelectContent>
471
+ </Select>
472
+ </div>
473
+
415
474
  <div className="space-y-2">
416
475
  <Label htmlFor="booking-title">
417
476
  {t("settings.bookingTitleLabel")}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-13
4
+ ---
5
+
6
+ The Find a time window now closes from a clear header action.
@@ -28,6 +28,7 @@ import type {
28
28
  CustomField,
29
29
  TimeSlot,
30
30
  } from "../../shared/api.js";
31
+ import { normalizeAvailabilitySlots } from "../../shared/availability-schedule.js";
31
32
  import { getDb, schema } from "../db/index.js";
32
33
  import {
33
34
  parseBookingLinkDurations,
@@ -626,7 +627,7 @@ export function generateAvailableSlotsForDate({
626
627
  timezone,
627
628
  );
628
629
 
629
- for (const scheduleSlot of daySchedule.slots) {
630
+ for (const scheduleSlot of normalizeAvailabilitySlots(daySchedule.slots)) {
630
631
  const [startHour, startMin] = scheduleSlot.start.split(":").map(Number);
631
632
  const [endHour, endMin] = scheduleSlot.end.split(":").map(Number);
632
633
  if (
@@ -7,14 +7,7 @@ import {
7
7
  } from "@agent-native/core/settings";
8
8
  import { defineEventHandler, setResponseStatus, type H3Event } from "h3";
9
9
 
10
- import type { Settings } from "../../shared/api.js";
11
-
12
- const DEFAULT_SETTINGS: Settings = {
13
- timezone: "America/New_York",
14
- bookingPageTitle: "Book a Meeting",
15
- bookingPageDescription: "Select a time that works for you.",
16
- defaultEventDuration: 30,
17
- };
10
+ import { normalizeCalendarSettings } from "../../shared/settings.js";
18
11
 
19
12
  async function uEmail(event: H3Event): Promise<string> {
20
13
  const session = await getSession(event);
@@ -28,9 +21,9 @@ async function uEmail(event: H3Event): Promise<string> {
28
21
  export const getSettings = defineEventHandler(async (event: H3Event) => {
29
22
  try {
30
23
  const email = await uEmail(event);
31
- const settings =
32
- (await getUserSetting(email, "calendar-settings")) || DEFAULT_SETTINGS;
33
- return settings;
24
+ return normalizeCalendarSettings(
25
+ await getUserSetting(email, "calendar-settings"),
26
+ );
34
27
  } catch (error: any) {
35
28
  setResponseStatus(event, 500);
36
29
  return { error: error.message };
@@ -38,16 +31,19 @@ export const getSettings = defineEventHandler(async (event: H3Event) => {
38
31
  });
39
32
 
40
33
  export const getPublicSettings = defineEventHandler(async (_event: H3Event) => {
41
- const settings =
42
- ((await getSetting("calendar-settings")) as unknown as Settings | null) ||
43
- DEFAULT_SETTINGS;
44
- return settings;
34
+ return normalizeCalendarSettings(await getSetting("calendar-settings"));
45
35
  });
46
36
 
47
37
  export const updateSettings = defineEventHandler(async (event: H3Event) => {
48
38
  try {
49
39
  const email = await uEmail(event);
50
- const settings: Settings = await readBody(event);
40
+ const body = await readBody<unknown>(event);
41
+ const settings = normalizeCalendarSettings({
42
+ ...normalizeCalendarSettings(
43
+ await getUserSetting(email, "calendar-settings"),
44
+ ),
45
+ ...(body && typeof body === "object" ? body : {}),
46
+ });
51
47
  const settingsRecord = settings as unknown as Record<string, unknown>;
52
48
  await putUserSetting(email, "calendar-settings", settingsRecord);
53
49
  // Also write to global key so the public booking/settings page can read it
@@ -331,6 +331,7 @@ export interface Settings {
331
331
  bookingPageTitle: string;
332
332
  bookingPageDescription: string;
333
333
  defaultEventDuration: number; // minutes
334
+ weekStart: import("./calendar-week.js").CalendarWeekStart;
334
335
  }
335
336
 
336
337
  export type ApolloPersonResult = {