@agent-native/core 0.164.4 → 0.164.11

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 (214) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +36 -47
  3. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -0
  4. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +18 -1
  5. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +256 -38
  6. package/corpus/templates/calendar/app/hooks/use-google-auth.ts +43 -3
  7. package/corpus/templates/calendar/app/i18n-data.ts +120 -0
  8. package/corpus/templates/calendar/app/lib/event-form-utils.ts +113 -0
  9. package/corpus/templates/calendar/server/handlers/google-auth.ts +119 -3
  10. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  11. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  12. package/corpus/templates/calendar/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  13. package/corpus/templates/calendar/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  14. package/corpus/templates/clips/app/components/player/video-player.tsx +12 -0
  15. package/corpus/templates/clips/app/routes/_app.dictate.tsx +18 -4
  16. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +114 -20
  17. package/corpus/templates/clips/app/routes/share.$shareId.tsx +83 -78
  18. package/corpus/templates/clips/changelog/2026-08-19-clips-nightly-builds-are-available-for-trying-the-latest-upd.md +6 -0
  19. package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +1 -0
  20. package/corpus/templates/clips/desktop/README.md +19 -4
  21. package/corpus/templates/clips/desktop/src/app.tsx +78 -16
  22. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -2
  23. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -0
  24. package/corpus/templates/clips/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  25. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +41 -3
  26. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  27. package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +22 -1
  28. package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +41 -22
  29. package/corpus/templates/factory/AGENTS.md +1 -1
  30. package/corpus/templates/factory/actions/list-factory-automations.ts +4 -0
  31. package/corpus/templates/factory/actions/start-builder-for-item.ts +19 -17
  32. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +100 -36
  33. package/corpus/templates/factory/app/i18n/en-US.ts +4 -0
  34. package/corpus/templates/factory/app/routes/factory.tsx +209 -46
  35. package/corpus/templates/factory/server/connectors/slack.ts +44 -2
  36. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -7
  37. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +13 -0
  38. package/corpus/templates/factory/server/triage/slack-client.ts +5 -0
  39. package/corpus/templates/factory/server/triage/slack-poller.ts +72 -4
  40. package/corpus/templates/mail/actions/view-screen.ts +1 -1
  41. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -18
  42. package/corpus/templates/mail/server/handlers/google-auth.ts +125 -24
  43. package/corpus/templates/mail/server/lib/gmail-query.ts +5 -2
  44. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  45. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  46. package/corpus/templates/mail/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  47. package/corpus/templates/mail/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  48. package/corpus/templates/plan/app/pages/PlansPage.tsx +0 -12
  49. package/corpus/templates/plan/changelog/2026-08-19-plan-prompts-now-guide-you-to-connect-ai-before-sending-and-.md +6 -0
  50. package/corpus/templates/slides/actions/approve-deck-access-request.ts +234 -0
  51. package/corpus/templates/slides/actions/get-deck-access-status.ts +35 -2
  52. package/corpus/templates/slides/actions/request-deck-access.ts +263 -64
  53. package/corpus/templates/slides/app/components/editor/MissingDeckAccessPane.tsx +196 -0
  54. package/corpus/templates/slides/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
  55. package/corpus/templates/slides/app/hooks/use-deck-access.ts +7 -4
  56. package/corpus/templates/slides/app/i18n/en-US.ts +29 -6
  57. package/corpus/templates/slides/app/pages/DeckEditor.tsx +147 -169
  58. package/corpus/templates/slides/app/routes/access-request.approve.tsx +250 -0
  59. package/corpus/templates/slides/server/db/schema.ts +8 -0
  60. package/corpus/templates/slides/server/lib/access-request-email.ts +36 -0
  61. package/corpus/templates/slides/server/lib/emails.ts +26 -0
  62. package/corpus/templates/slides/server/plugins/agent-chat.ts +7 -0
  63. package/corpus/templates/slides/server/plugins/db.ts +50 -0
  64. package/corpus/templates/slides/shared/deck-access.ts +31 -0
  65. package/dist/a2a/task-store.d.ts +1 -0
  66. package/dist/a2a/task-store.js +1 -1
  67. package/dist/agent/observational-memory/store.d.ts +1 -0
  68. package/dist/agent/observational-memory/store.js +1 -1
  69. package/dist/agent/run-store.d.ts +1 -0
  70. package/dist/agent/run-store.js +1 -1
  71. package/dist/agent/tool-approval-store.d.ts +1 -0
  72. package/dist/agent/tool-approval-store.js +1 -1
  73. package/dist/application-state/store.d.ts +1 -0
  74. package/dist/application-state/store.js +1 -1
  75. package/dist/browser-sessions/store.d.ts +1 -0
  76. package/dist/browser-sessions/store.js +1 -1
  77. package/dist/checkpoints/store.d.ts +1 -0
  78. package/dist/checkpoints/store.js +1 -1
  79. package/dist/cli/code-agent-schedules.d.ts +11 -0
  80. package/dist/cli/code-agent-schedules.js +44 -5
  81. package/dist/client/AgentPanel.d.ts +5 -1
  82. package/dist/client/AgentPanel.js +2 -2
  83. package/dist/client/AssistantChat.d.ts +5 -1
  84. package/dist/client/AssistantChat.js +40 -6
  85. package/dist/client/EnvironmentBadge.d.ts +7 -13
  86. package/dist/client/EnvironmentBadge.js +43 -92
  87. package/dist/client/agent-chat/index.d.ts +1 -0
  88. package/dist/client/agent-chat/index.js +1 -0
  89. package/dist/client/agent-chat-adapter.js +76 -50
  90. package/dist/client/app-providers.js +4 -4
  91. package/dist/client/chat/message-components.d.ts +4 -0
  92. package/dist/client/chat/message-components.js +28 -1
  93. package/dist/client/chat/run-recovery.js +26 -0
  94. package/dist/client/chat-first/apps-rail.d.ts +1 -0
  95. package/dist/client/chat-first/apps-rail.js +28 -2
  96. package/dist/client/sse-event-processor.js +13 -8
  97. package/dist/client/use-active-agent-chat-run.d.ts +2 -0
  98. package/dist/client/use-active-agent-chat-run.js +22 -0
  99. package/dist/coding-tools/sandbox/executions-store.d.ts +1 -0
  100. package/dist/coding-tools/sandbox/executions-store.js +1 -1
  101. package/dist/collab/awareness-store.d.ts +1 -0
  102. package/dist/collab/awareness-store.js +1 -1
  103. package/dist/collab/awareness.d.ts +2 -2
  104. package/dist/collab/routes.d.ts +1 -1
  105. package/dist/collab/storage.d.ts +1 -0
  106. package/dist/collab/storage.js +1 -1
  107. package/dist/db/client.js +3 -3
  108. package/dist/db/ddl-guard.js +8 -0
  109. package/dist/db/migration-runtime.d.ts +23 -0
  110. package/dist/db/migration-runtime.js +41 -0
  111. package/dist/db/migrations.d.ts +1 -2
  112. package/dist/db/migrations.js +2 -24
  113. package/dist/email-catalog/log.d.ts +1 -0
  114. package/dist/email-catalog/log.js +1 -1
  115. package/dist/guards/index.d.ts +1 -0
  116. package/dist/guards/index.js +1 -0
  117. package/dist/guards/release-schema-complete.d.ts +22 -0
  118. package/dist/guards/release-schema-complete.js +144 -0
  119. package/dist/integrations/a2a-continuations-store.d.ts +1 -0
  120. package/dist/integrations/a2a-continuations-store.js +1 -1
  121. package/dist/integrations/awaiting-input-store.d.ts +1 -0
  122. package/dist/integrations/awaiting-input-store.js +1 -1
  123. package/dist/integrations/config-store.d.ts +1 -0
  124. package/dist/integrations/config-store.js +1 -1
  125. package/dist/integrations/controls-store.d.ts +1 -0
  126. package/dist/integrations/controls-store.js +1 -1
  127. package/dist/integrations/identity-links-store.d.ts +1 -0
  128. package/dist/integrations/identity-links-store.js +1 -1
  129. package/dist/integrations/installations-store.d.ts +1 -0
  130. package/dist/integrations/installations-store.js +1 -1
  131. package/dist/integrations/remote-commands-store.d.ts +1 -0
  132. package/dist/integrations/remote-commands-store.js +1 -1
  133. package/dist/integrations/remote-devices-store.d.ts +1 -0
  134. package/dist/integrations/remote-devices-store.js +1 -1
  135. package/dist/integrations/remote-push-store.d.ts +1 -0
  136. package/dist/integrations/remote-push-store.js +1 -1
  137. package/dist/integrations/remote-run-events-store.d.ts +1 -0
  138. package/dist/integrations/remote-run-events-store.js +1 -1
  139. package/dist/integrations/scope-store.d.ts +1 -0
  140. package/dist/integrations/scope-store.js +1 -1
  141. package/dist/integrations/thread-mapping-store.d.ts +1 -0
  142. package/dist/integrations/thread-mapping-store.js +1 -1
  143. package/dist/integrations/usage-budget-store.d.ts +1 -0
  144. package/dist/integrations/usage-budget-store.js +1 -1
  145. package/dist/jobs/background-automation-runner.js +111 -9
  146. package/dist/jobs/run-history.d.ts +1 -0
  147. package/dist/jobs/run-history.js +1 -1
  148. package/dist/jobs/scheduler-health.d.ts +1 -0
  149. package/dist/jobs/scheduler-health.js +1 -1
  150. package/dist/mcp/approval-store.d.ts +1 -0
  151. package/dist/mcp/approval-store.js +1 -1
  152. package/dist/mcp/connect-store.d.ts +1 -0
  153. package/dist/mcp/connect-store.js +1 -1
  154. package/dist/mcp/oauth-store.d.ts +1 -0
  155. package/dist/mcp/oauth-store.js +1 -1
  156. package/dist/notifications/routes.d.ts +3 -3
  157. package/dist/notifications/store.d.ts +1 -0
  158. package/dist/notifications/store.js +1 -1
  159. package/dist/oauth-tokens/store.d.ts +1 -0
  160. package/dist/oauth-tokens/store.js +1 -1
  161. package/dist/observability/routes.d.ts +3 -3
  162. package/dist/progress/store.d.ts +1 -0
  163. package/dist/progress/store.js +1 -1
  164. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  165. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  166. package/dist/provider-api/corpus-jobs-store.d.ts +1 -0
  167. package/dist/provider-api/corpus-jobs-store.js +1 -1
  168. package/dist/provider-api/custom-registry.d.ts +1 -0
  169. package/dist/provider-api/custom-registry.js +1 -1
  170. package/dist/provider-api/quota-governor.d.ts +1 -0
  171. package/dist/provider-api/quota-governor.js +1 -1
  172. package/dist/provider-api/staged-datasets-store.d.ts +1 -0
  173. package/dist/provider-api/staged-datasets-store.js +1 -1
  174. package/dist/resources/handlers.d.ts +3 -3
  175. package/dist/resources/store.d.ts +1 -0
  176. package/dist/resources/store.js +1 -1
  177. package/dist/scripts/dev/index.js +15 -1
  178. package/dist/search/index.js +3 -0
  179. package/dist/secrets/storage.d.ts +1 -0
  180. package/dist/secrets/storage.js +1 -1
  181. package/dist/server/agent-chat-plugin.js +38 -9
  182. package/dist/server/agent-teams-run-queue.d.ts +1 -0
  183. package/dist/server/agent-teams-run-queue.js +1 -1
  184. package/dist/server/auth-marketing.js +3 -1
  185. package/dist/server/auth.d.ts +18 -1
  186. package/dist/server/auth.js +370 -154
  187. package/dist/server/embed-session.d.ts +1 -0
  188. package/dist/server/embed-session.js +1 -1
  189. package/dist/server/google-auth-plugin.js +3 -1
  190. package/dist/server/google-oauth.d.ts +10 -0
  191. package/dist/server/google-oauth.js +25 -1
  192. package/dist/server/identity-sso-store.d.ts +1 -0
  193. package/dist/server/identity-sso-store.js +1 -1
  194. package/dist/server/index.d.ts +1 -1
  195. package/dist/server/index.js +1 -1
  196. package/dist/server/onboarding-html.js +200 -31
  197. package/dist/server/poll.d.ts +2 -0
  198. package/dist/server/poll.js +19 -1
  199. package/dist/server/release-migrations.js +5 -0
  200. package/dist/server/release-schema.d.ts +46 -0
  201. package/dist/server/release-schema.js +307 -0
  202. package/dist/settings/store.d.ts +1 -0
  203. package/dist/settings/store.js +1 -1
  204. package/dist/shared/environment-lanes.d.ts +28 -0
  205. package/dist/shared/environment-lanes.js +61 -0
  206. package/dist/sharing/actions/share-resource.js +22 -2
  207. package/dist/styles/agent-native.css +17 -15
  208. package/dist/triggers/routes.d.ts +11 -0
  209. package/dist/triggers/routes.js +10 -0
  210. package/dist/usage/alerts-store.d.ts +1 -0
  211. package/dist/usage/alerts-store.js +1 -1
  212. package/dist/usage/store.d.ts +1 -0
  213. package/dist/usage/store.js +1 -1
  214. package/package.json +2 -2
@@ -748,8 +748,10 @@ const enUS = {
748
748
  customEllipsis: "Custom...",
749
749
  customRepeatGoogleCalendar:
750
750
  "Custom repeat schedules must be edited in Google Calendar.",
751
+ customRecurrenceTitle: "Custom recurrence",
751
752
  customSchedule: "Custom schedule",
752
753
  daily: "Daily",
754
+ day: "day",
753
755
  default: "Default",
754
756
  delete: "Delete",
755
757
  description: "Description",
@@ -800,6 +802,7 @@ const enUS = {
800
802
  location: "Location",
801
803
  meet: "Meet",
802
804
  monthly: "Monthly",
805
+ month: "month",
803
806
  newEvent: "New Event",
804
807
  newEventLower: "New event",
805
808
  noGuests: "No guests",
@@ -832,6 +835,13 @@ const enUS = {
832
835
  recurringResponseScope: "Recurring response scope",
833
836
  repeats: "Repeats",
834
837
  repeat: "Repeat",
838
+ repeatEvery: "Repeat every",
839
+ repeatOn: "Repeat on",
840
+ ends: "Ends",
841
+ never: "Never",
842
+ on: "On",
843
+ after: "After",
844
+ occurrences: "occurrences",
835
845
  reviewInvite: "Review Invite",
836
846
  responseAwaitingCount: "{{count}} awaiting",
837
847
  responseMaybeCount: "{{count}} maybe",
@@ -888,12 +898,14 @@ const enUS = {
888
898
  onMonthDay: "on the {{day}}",
889
899
  weekdaysShort: "Mon–Fri",
890
900
  weekly: "Weekly",
901
+ week: "week",
891
902
  workingFrom: "Working from",
892
903
  workingLocation: "Working location",
893
904
  workingLocationDesk: "Desk {{desk}}",
894
905
  workingLocationFloor: "Floor {{floor}}",
895
906
  workingLocationTitle: "Working location: {{location}}",
896
907
  yearly: "Yearly",
908
+ year: "year",
897
909
  zoom: "Zoom",
898
910
  zoomAdded: "Zoom added",
899
911
  zoomAddFailed: "Failed to add Zoom",
@@ -6141,6 +6153,18 @@ const translatedCalendarInlineEditorLabels = {
6141
6153
  "zh-CN": {
6142
6154
  eventForm: {
6143
6155
  addGuest: "添加客人",
6156
+ customRecurrenceTitle: "自定义重复",
6157
+ day: "天",
6158
+ month: "月",
6159
+ repeatEvery: "每隔",
6160
+ repeatOn: "重复于",
6161
+ ends: "结束",
6162
+ never: "从不",
6163
+ on: "于",
6164
+ after: "之后",
6165
+ occurrences: "次",
6166
+ week: "周",
6167
+ year: "年",
6144
6168
  everyTwoWeeks: "每两周",
6145
6169
  repeat: "重复",
6146
6170
  onDate: "于 {{date}}",
@@ -6152,6 +6176,18 @@ const translatedCalendarInlineEditorLabels = {
6152
6176
  "es-ES": {
6153
6177
  eventForm: {
6154
6178
  addGuest: "Añadir invitado",
6179
+ customRecurrenceTitle: "Repetición personalizada",
6180
+ day: "día",
6181
+ month: "mes",
6182
+ repeatEvery: "Repetir cada",
6183
+ repeatOn: "Repetir el",
6184
+ ends: "Termina",
6185
+ never: "Nunca",
6186
+ on: "El",
6187
+ after: "Después de",
6188
+ occurrences: "repeticiones",
6189
+ week: "semana",
6190
+ year: "año",
6155
6191
  everyTwoWeeks: "Cada 2 semanas",
6156
6192
  repeat: "Repetir",
6157
6193
  onDate: "el {{date}}",
@@ -6163,6 +6199,18 @@ const translatedCalendarInlineEditorLabels = {
6163
6199
  "fr-FR": {
6164
6200
  eventForm: {
6165
6201
  addGuest: "Ajouter un invité",
6202
+ customRecurrenceTitle: "Répétition personnalisée",
6203
+ day: "jour",
6204
+ month: "mois",
6205
+ repeatEvery: "Répéter tous les",
6206
+ repeatOn: "Répéter le",
6207
+ ends: "Se termine",
6208
+ never: "Jamais",
6209
+ on: "Le",
6210
+ after: "Après",
6211
+ occurrences: "occurrences",
6212
+ week: "semaine",
6213
+ year: "an",
6166
6214
  everyTwoWeeks: "Toutes les 2 semaines",
6167
6215
  repeat: "Répéter",
6168
6216
  onDate: "le {{date}}",
@@ -6174,6 +6222,18 @@ const translatedCalendarInlineEditorLabels = {
6174
6222
  "de-DE": {
6175
6223
  eventForm: {
6176
6224
  addGuest: "Gast hinzufügen",
6225
+ customRecurrenceTitle: "Benutzerdefinierte Wiederholung",
6226
+ day: "Tag",
6227
+ month: "Monat",
6228
+ repeatEvery: "Wiederholen alle",
6229
+ repeatOn: "Wiederholen am",
6230
+ ends: "Endet",
6231
+ never: "Nie",
6232
+ on: "Am",
6233
+ after: "Nach",
6234
+ occurrences: "Vorkommen",
6235
+ week: "Woche",
6236
+ year: "Jahr",
6177
6237
  everyTwoWeeks: "Alle 2 Wochen",
6178
6238
  repeat: "Wiederholen",
6179
6239
  onDate: "am {{date}}",
@@ -6185,6 +6245,18 @@ const translatedCalendarInlineEditorLabels = {
6185
6245
  "ja-JP": {
6186
6246
  eventForm: {
6187
6247
  addGuest: "ゲストを追加",
6248
+ customRecurrenceTitle: "カスタム繰り返し",
6249
+ day: "日",
6250
+ month: "月",
6251
+ repeatEvery: "繰り返す間隔",
6252
+ repeatOn: "繰り返す曜日",
6253
+ ends: "終了",
6254
+ never: "なし",
6255
+ on: "指定日",
6256
+ after: "次の",
6257
+ occurrences: "回",
6258
+ week: "週",
6259
+ year: "年",
6188
6260
  everyTwoWeeks: "2週間ごと",
6189
6261
  repeat: "繰り返し",
6190
6262
  onDate: "{{date}}",
@@ -6196,6 +6268,18 @@ const translatedCalendarInlineEditorLabels = {
6196
6268
  "ko-KR": {
6197
6269
  eventForm: {
6198
6270
  addGuest: "게스트 추가",
6271
+ customRecurrenceTitle: "맞춤 반복",
6272
+ day: "일",
6273
+ month: "월",
6274
+ repeatEvery: "반복 간격",
6275
+ repeatOn: "반복 요일",
6276
+ ends: "종료",
6277
+ never: "안 함",
6278
+ on: "날짜",
6279
+ after: "후",
6280
+ occurrences: "회",
6281
+ week: "주",
6282
+ year: "년",
6199
6283
  everyTwoWeeks: "2주마다",
6200
6284
  repeat: "반복",
6201
6285
  onDate: "{{date}}",
@@ -6207,6 +6291,18 @@ const translatedCalendarInlineEditorLabels = {
6207
6291
  "pt-BR": {
6208
6292
  eventForm: {
6209
6293
  addGuest: "Adicionar convidado",
6294
+ customRecurrenceTitle: "Repetição personalizada",
6295
+ day: "dia",
6296
+ month: "mês",
6297
+ repeatEvery: "Repetir a cada",
6298
+ repeatOn: "Repetir em",
6299
+ ends: "Termina",
6300
+ never: "Nunca",
6301
+ on: "Em",
6302
+ after: "Após",
6303
+ occurrences: "ocorrências",
6304
+ week: "semana",
6305
+ year: "ano",
6210
6306
  everyTwoWeeks: "A cada 2 semanas",
6211
6307
  repeat: "Repetir",
6212
6308
  onDate: "em {{date}}",
@@ -6218,6 +6314,18 @@ const translatedCalendarInlineEditorLabels = {
6218
6314
  "hi-IN": {
6219
6315
  eventForm: {
6220
6316
  addGuest: "अतिथि जोड़ें",
6317
+ customRecurrenceTitle: "कस्टम दोहराव",
6318
+ day: "दिन",
6319
+ month: "महीना",
6320
+ repeatEvery: "हर",
6321
+ repeatOn: "इन दिनों दोहराएं",
6322
+ ends: "समाप्त",
6323
+ never: "कभी नहीं",
6324
+ on: "इस दिन",
6325
+ after: "के बाद",
6326
+ occurrences: "बार",
6327
+ week: "सप्ताह",
6328
+ year: "वर्ष",
6221
6329
  everyTwoWeeks: "हर 2 सप्ताह",
6222
6330
  repeat: "दोहराएं",
6223
6331
  onDate: "{{date}} को",
@@ -6229,6 +6337,18 @@ const translatedCalendarInlineEditorLabels = {
6229
6337
  "ar-SA": {
6230
6338
  eventForm: {
6231
6339
  addGuest: "إضافة ضيف",
6340
+ customRecurrenceTitle: "تكرار مخصص",
6341
+ day: "يوم",
6342
+ month: "شهر",
6343
+ repeatEvery: "تكرار كل",
6344
+ repeatOn: "تكرار في",
6345
+ ends: "ينتهي",
6346
+ never: "أبدًا",
6347
+ on: "في",
6348
+ after: "بعد",
6349
+ occurrences: "مرات",
6350
+ week: "أسبوع",
6351
+ year: "سنة",
6232
6352
  everyTwoWeeks: "كل أسبوعين",
6233
6353
  repeat: "تكرار",
6234
6354
  onDate: "في {{date}}",
@@ -12,6 +12,18 @@ export type RecurrencePreset =
12
12
  | "yearly"
13
13
  | "custom";
14
14
 
15
+ export type RecurrenceUnit = "day" | "week" | "month" | "year";
16
+ export type RecurrenceEndMode = "never" | "date" | "count";
17
+
18
+ export interface CustomRecurrenceDraft {
19
+ interval: number;
20
+ unit: RecurrenceUnit;
21
+ days: string[];
22
+ endMode: RecurrenceEndMode;
23
+ endDate: string;
24
+ count: number;
25
+ }
26
+
15
27
  export interface ReminderDraft {
16
28
  id: string;
17
29
  method: ReminderMethod;
@@ -387,6 +399,107 @@ export function getRecurrencePreset(recurrence?: string[]): RecurrencePreset {
387
399
  return "custom";
388
400
  }
389
401
 
402
+ function recurrenceFieldValue(rule: string, key: string): string | undefined {
403
+ return rule.match(new RegExp(`${key}=([^;]+)`, "i"))?.[1];
404
+ }
405
+
406
+ function recurrenceWeekday(startIso: string, timeZone?: string) {
407
+ return eventWeekdayCode(startIso, timeZone);
408
+ }
409
+
410
+ export function createCustomRecurrenceDraft(
411
+ startIso: string,
412
+ timeZone?: string,
413
+ ): CustomRecurrenceDraft {
414
+ return {
415
+ interval: 1,
416
+ unit: "week",
417
+ days: [recurrenceWeekday(startIso, timeZone)],
418
+ endMode: "never",
419
+ endDate: "",
420
+ count: 13,
421
+ };
422
+ }
423
+
424
+ export function parseCustomRecurrence(
425
+ recurrence: string[] | undefined,
426
+ startIso: string,
427
+ timeZone?: string,
428
+ ): CustomRecurrenceDraft {
429
+ const draft = createCustomRecurrenceDraft(startIso, timeZone);
430
+ const rule = recurrenceRule(recurrence);
431
+ if (!rule) return draft;
432
+
433
+ const freq = recurrenceFieldValue(rule, "FREQ")?.toLowerCase();
434
+ const unit: RecurrenceUnit =
435
+ freq === "daily"
436
+ ? "day"
437
+ : freq === "weekly"
438
+ ? "week"
439
+ : freq === "monthly"
440
+ ? "month"
441
+ : freq === "yearly"
442
+ ? "year"
443
+ : draft.unit;
444
+ const interval = Number.parseInt(
445
+ recurrenceFieldValue(rule, "INTERVAL") || "1",
446
+ 10,
447
+ );
448
+ const count = Number.parseInt(
449
+ recurrenceFieldValue(rule, "COUNT") || String(draft.count),
450
+ 10,
451
+ );
452
+ const until = recurrenceFieldValue(rule, "UNTIL");
453
+ const byDay = recurrenceFieldValue(rule, "BYDAY")
454
+ ?.split(",")
455
+ .map((day) => day.replace(/^[+-]?\d+/, "").toUpperCase())
456
+ .filter(Boolean);
457
+
458
+ return {
459
+ interval: Number.isFinite(interval) ? Math.max(1, interval) : 1,
460
+ unit,
461
+ days: byDay && byDay.length > 0 ? byDay : unit === "week" ? draft.days : [],
462
+ endMode: until
463
+ ? "date"
464
+ : recurrenceFieldValue(rule, "COUNT")
465
+ ? "count"
466
+ : "never",
467
+ endDate:
468
+ until && /^\d{8}/.test(until)
469
+ ? `${until.slice(0, 4)}-${until.slice(4, 6)}-${until.slice(6, 8)}`
470
+ : "",
471
+ count: Number.isFinite(count) ? Math.max(1, count) : draft.count,
472
+ };
473
+ }
474
+
475
+ export function buildCustomRecurrenceRules(
476
+ draft: CustomRecurrenceDraft,
477
+ ): string[] {
478
+ const interval = Math.max(1, Math.round(draft.interval));
479
+ const frequency = {
480
+ day: "DAILY",
481
+ week: "WEEKLY",
482
+ month: "MONTHLY",
483
+ year: "YEARLY",
484
+ }[draft.unit];
485
+ const parts = [
486
+ `FREQ=${frequency}`,
487
+ ...(interval > 1 ? [`INTERVAL=${interval}`] : []),
488
+ ];
489
+ if (draft.unit === "week" && draft.days.length > 0) {
490
+ parts.push(`BYDAY=${draft.days.join(",")}`);
491
+ }
492
+ if (draft.unit === "month" && draft.days.length > 0) {
493
+ parts.push(`BYDAY=${draft.days.join(",")}`);
494
+ }
495
+ if (draft.endMode === "date" && /^\d{4}-\d{2}-\d{2}$/.test(draft.endDate)) {
496
+ parts.push(`UNTIL=${draft.endDate.replace(/-/g, "")}T235959Z`);
497
+ } else if (draft.endMode === "count") {
498
+ parts.push(`COUNT=${Math.max(1, Math.round(draft.count))}`);
499
+ }
500
+ return [`RRULE:${parts.join(";")}`];
501
+ }
502
+
390
503
  export function buildRecurrenceRules(
391
504
  preset: RecurrencePreset,
392
505
  startIso: string,
@@ -16,6 +16,9 @@ import {
16
16
  oauthCallbackResponse,
17
17
  oauthDesktopExchangePage,
18
18
  oauthErrorPage,
19
+ registerDesktopExchange,
20
+ prepareDesktopOAuthBrowserBinding,
21
+ matchesDesktopOAuthBrowserBinding,
19
22
  setDesktopExchange,
20
23
  setDesktopExchangeError,
21
24
  safeReturnPath,
@@ -23,6 +26,8 @@ import {
23
26
  } from "@agent-native/core/server";
24
27
  import {
25
28
  defineEventHandler,
29
+ getHeader,
30
+ getMethod,
26
31
  getQuery,
27
32
  setResponseStatus,
28
33
  type H3Event,
@@ -55,6 +60,8 @@ type CalendarOAuthStateOptions = {
55
60
  app?: string;
56
61
  returnUrl?: string;
57
62
  flowId?: string;
63
+ desktopVerifierHash?: string;
64
+ desktopBrowserBindingHash?: string;
58
65
  };
59
66
 
60
67
  function encodeCalendarOAuthState(options: CalendarOAuthStateOptions): string {
@@ -226,6 +233,7 @@ function missingCredentialsResponse(
226
233
  export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
227
234
  try {
228
235
  const q = getQuery(event);
236
+ const method = getMethod(event);
229
237
  const redirectUri = resolveOAuthRedirectUri(event);
230
238
  if (!redirectUri) {
231
239
  setResponseStatus(event, 400);
@@ -241,6 +249,10 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
241
249
  isElectron(event) || q.desktop === "1" || q.desktop === "true";
242
250
  const mobile = q.mobile === "1" || q.mobile === "true";
243
251
  const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
252
+ if (method === "POST" && (!desktop || !flowId)) {
253
+ setResponseStatus(event, 400);
254
+ return { error: "Invalid desktop exchange challenge." };
255
+ }
244
256
  const calendarConnect = isCalendarConnectRequest(q, owner);
245
257
  const credentials = calendarConnect
246
258
  ? await resolveCalendarOAuthCredentials(event)
@@ -264,6 +276,31 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
264
276
  };
265
277
  }
266
278
 
279
+ let desktopVerifierHash: string | undefined;
280
+ let desktopBrowserBindingHash: string | undefined;
281
+ if (flowId) {
282
+ if (method !== "POST" || q.redirect !== undefined) {
283
+ setResponseStatus(event, 400);
284
+ return { error: "Invalid desktop exchange challenge." };
285
+ }
286
+ const verifier = getHeader(event, "x-agent-native-desktop-verifier");
287
+ if (!verifier || q.verifier !== undefined) {
288
+ setResponseStatus(event, 400);
289
+ return { error: "Invalid desktop exchange challenge." };
290
+ }
291
+ try {
292
+ desktopBrowserBindingHash = prepareDesktopOAuthBrowserBinding(event);
293
+ desktopVerifierHash = await registerDesktopExchange(
294
+ flowId,
295
+ verifier,
296
+ desktopBrowserBindingHash,
297
+ );
298
+ } catch {
299
+ setResponseStatus(event, 400);
300
+ return { error: "Invalid desktop exchange challenge." };
301
+ }
302
+ }
303
+
267
304
  const requestedReturn =
268
305
  typeof q.return === "string" ? safeReturnPath(q.return) : "/";
269
306
  const returnUrl = requestedReturn !== "/" ? requestedReturn : undefined;
@@ -279,6 +316,8 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
279
316
  app: OAUTH_STATE_APP_ID,
280
317
  returnUrl,
281
318
  flowId,
319
+ desktopVerifierHash,
320
+ desktopBrowserBindingHash,
282
321
  });
283
322
 
284
323
  const url = calendarConnect
@@ -316,6 +355,17 @@ export const handleGoogleCallback = defineEventHandler(
316
355
  desktop = state.desktop ?? false;
317
356
  mobile = state.mobile ?? false;
318
357
  flowId = state.flowId;
358
+ if (
359
+ flowId &&
360
+ (!state.desktopVerifierHash ||
361
+ !state.desktopBrowserBindingHash ||
362
+ !matchesDesktopOAuthBrowserBinding(
363
+ event,
364
+ state.desktopBrowserBindingHash,
365
+ ))
366
+ ) {
367
+ throw new Error("Desktop OAuth browser binding is invalid.");
368
+ }
319
369
 
320
370
  const googleError = query.error as string | undefined;
321
371
  if (googleError) {
@@ -366,7 +416,15 @@ export const handleGoogleCallback = defineEventHandler(
366
416
  );
367
417
 
368
418
  if (flowId && sessionToken) {
369
- setDesktopExchange(flowId, sessionToken, identity.email);
419
+ if (!state.desktopVerifierHash) {
420
+ throw new Error("Missing desktop exchange challenge.");
421
+ }
422
+ await setDesktopExchange(
423
+ flowId,
424
+ sessionToken,
425
+ identity.email,
426
+ state.desktopVerifierHash,
427
+ );
370
428
  }
371
429
 
372
430
  return oauthCallbackResponse(event, identity.email, {
@@ -410,7 +468,15 @@ export const handleGoogleCallback = defineEventHandler(
410
468
  : { sessionToken: undefined };
411
469
 
412
470
  if (flowId && sessionToken) {
413
- setDesktopExchange(flowId, sessionToken, sessionOwner);
471
+ if (!state.desktopVerifierHash) {
472
+ throw new Error("Missing desktop exchange challenge.");
473
+ }
474
+ await setDesktopExchange(
475
+ flowId,
476
+ sessionToken,
477
+ sessionOwner,
478
+ state.desktopVerifierHash,
479
+ );
414
480
  }
415
481
 
416
482
  // 4. Return platform-appropriate response
@@ -436,10 +502,15 @@ export const getGoogleAddAccountUrl = defineEventHandler(
436
502
  return { error: "Must be logged in to add an account" };
437
503
  }
438
504
  const q = getQuery(event);
505
+ const method = getMethod(event);
439
506
  const desktop =
440
507
  isElectron(event) || q.desktop === "1" || q.desktop === "true";
441
508
  const mobile = q.mobile === "1" || q.mobile === "true";
442
509
  const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
510
+ if (method === "POST" && (!desktop || !flowId)) {
511
+ setResponseStatus(event, 400);
512
+ return { error: "Invalid desktop exchange challenge." };
513
+ }
443
514
  if (!(await resolveCalendarOAuthCredentials(event))) {
444
515
  return missingCredentialsResponse(
445
516
  event,
@@ -456,6 +527,30 @@ export const getGoogleAddAccountUrl = defineEventHandler(
456
527
  message: "redirect_uri must stay on this app's _agent-native routes.",
457
528
  };
458
529
  }
530
+ let desktopVerifierHash: string | undefined;
531
+ let desktopBrowserBindingHash: string | undefined;
532
+ if (flowId) {
533
+ if (method !== "POST" || q.redirect !== undefined) {
534
+ setResponseStatus(event, 400);
535
+ return { error: "Invalid desktop exchange challenge." };
536
+ }
537
+ const verifier = getHeader(event, "x-agent-native-desktop-verifier");
538
+ if (!verifier || q.verifier !== undefined) {
539
+ setResponseStatus(event, 400);
540
+ return { error: "Invalid desktop exchange challenge." };
541
+ }
542
+ try {
543
+ desktopBrowserBindingHash = prepareDesktopOAuthBrowserBinding(event);
544
+ desktopVerifierHash = await registerDesktopExchange(
545
+ flowId,
546
+ verifier,
547
+ desktopBrowserBindingHash,
548
+ );
549
+ } catch {
550
+ setResponseStatus(event, 400);
551
+ return { error: "Invalid desktop exchange challenge." };
552
+ }
553
+ }
459
554
  const state = encodeCalendarOAuthState({
460
555
  redirectUri,
461
556
  owner: session.email,
@@ -465,6 +560,8 @@ export const getGoogleAddAccountUrl = defineEventHandler(
465
560
  addAccount: true,
466
561
  app: OAUTH_STATE_APP_ID,
467
562
  flowId,
563
+ desktopVerifierHash,
564
+ desktopBrowserBindingHash,
468
565
  });
469
566
  const url = await getAuthUrl(
470
567
  undefined,
@@ -499,6 +596,17 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
499
596
  desktop = state.desktop ?? false;
500
597
  mobile = state.mobile ?? false;
501
598
  flowId = state.flowId;
599
+ if (
600
+ flowId &&
601
+ (!state.desktopVerifierHash ||
602
+ !state.desktopBrowserBindingHash ||
603
+ !matchesDesktopOAuthBrowserBinding(
604
+ event,
605
+ state.desktopBrowserBindingHash,
606
+ ))
607
+ ) {
608
+ throw new Error("Desktop OAuth browser binding is invalid.");
609
+ }
502
610
 
503
611
  const googleError = query.error as string | undefined;
504
612
  if (googleError) {
@@ -547,7 +655,15 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
547
655
  : { sessionToken: undefined };
548
656
 
549
657
  if (flowId && sessionToken) {
550
- setDesktopExchange(flowId, sessionToken, ownerEmail);
658
+ if (!state.desktopVerifierHash) {
659
+ throw new Error("Missing desktop exchange challenge.");
660
+ }
661
+ await setDesktopExchange(
662
+ flowId,
663
+ sessionToken,
664
+ ownerEmail,
665
+ state.desktopVerifierHash,
666
+ );
551
667
  }
552
668
 
553
669
  return oauthCallbackResponse(event, addedEmail, {
@@ -0,0 +1 @@
1
+ export { default } from "../../../../_agent-native/google/add-account/auth-url.post.js";
@@ -0,0 +1 @@
1
+ export { default } from "../../../_agent-native/google/auth-url.post.js";
@@ -0,0 +1 @@
1
+ export { getGoogleAddAccountUrl as default } from "../../../../handlers/google-auth";
@@ -0,0 +1 @@
1
+ export { getGoogleAuthUrl as default } from "../../../handlers/google-auth";
@@ -168,6 +168,7 @@ export interface VideoPlayerHandle {
168
168
  play: () => Promise<void> | void;
169
169
  pause: () => void;
170
170
  seek: (ms: number) => void;
171
+ getCurrentOriginalMs: () => number;
171
172
  setSpeed: (rate: number) => void;
172
173
  toggleMute: () => void;
173
174
  toggleCaptions: () => void;
@@ -1159,6 +1160,17 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
1159
1160
  play: requestPlay,
1160
1161
  pause: pauseVideo,
1161
1162
  seek: seekToVisibleMs,
1163
+ getCurrentOriginalMs: () => {
1164
+ const v = videoRef.current;
1165
+ const liveOriginalMs =
1166
+ v &&
1167
+ Number.isFinite(v.currentTime) &&
1168
+ v.currentTime >= 0 &&
1169
+ v.currentTime < 1e7
1170
+ ? Math.floor(v.currentTime * 1000)
1171
+ : currentMsRef.current;
1172
+ return liveOriginalMs;
1173
+ },
1162
1174
  setSpeed: applySpeed,
1163
1175
  toggleMute: () => {
1164
1176
  if (videoRef.current) {
@@ -159,6 +159,10 @@ function dayBucket(iso: string): string {
159
159
  }
160
160
  }
161
161
 
162
+ export function dictationsRefetchInterval(isActive: boolean): number | false {
163
+ return isActive ? 2_000 : false;
164
+ }
165
+
162
166
  function sourceMeta(
163
167
  source: string | undefined,
164
168
  t: ReturnType<typeof useT>,
@@ -694,10 +698,6 @@ function DownloadDesktopAppCard() {
694
698
 
695
699
  export default function DictateRoute() {
696
700
  const t = useT();
697
- const { data, isLoading, isError } = useActionQuery<
698
- { dictations: Dictation[] } | Dictation[] | undefined
699
- >("list-dictations", {}, { retry: false });
700
-
701
701
  const { isDesktopApp } = useDesktopPromo();
702
702
  const [filter, setFilter] = useState<SourceFilter>("all");
703
703
  const [listening, setListening] = useState(false);
@@ -706,6 +706,20 @@ export default function DictateRoute() {
706
706
  const [speechSupported, setSpeechSupported] = useState(false);
707
707
  const qc = useQueryClient();
708
708
  const createDictation = useActionMutation("create-dictation");
709
+ const { data, isLoading, isError } = useActionQuery<
710
+ { dictations: Dictation[] } | Dictation[] | undefined
711
+ >(
712
+ "list-dictations",
713
+ {},
714
+ {
715
+ retry: false,
716
+ // Action-backed mutations invalidate this query when a desktop-created
717
+ // dictation lands. Poll only while browser work is active or saving,
718
+ // rather than running a permanent interval over idle history.
719
+ refetchInterval: () =>
720
+ dictationsRefetchInterval(listening || createDictation.isPending),
721
+ },
722
+ );
709
723
 
710
724
  const recognitionRef = useRef<SpeechRecognitionLike | null>(null);
711
725
  const transcriptRef = useRef("");