@agent-native/core 0.155.0 → 0.157.1

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 (192) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
  3. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
  4. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  5. package/corpus/templates/assets/app/global.css +4 -0
  6. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  7. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +18 -15
  8. package/corpus/templates/brain/app/routes/sources.tsx +1 -2
  9. package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
  10. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
  11. package/corpus/templates/calendar/server/handlers/bookings.ts +219 -93
  12. package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
  13. package/corpus/templates/clips/actions/create-folder.ts +7 -1
  14. package/corpus/templates/clips/actions/create-meeting.ts +6 -2
  15. package/corpus/templates/clips/actions/create-recording.ts +5 -3
  16. package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
  17. package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
  18. package/corpus/templates/clips/actions/request-recording-access.ts +272 -0
  19. package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
  20. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
  21. package/corpus/templates/clips/app/components/player/share-dialog.tsx +177 -134
  22. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
  23. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
  24. package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
  25. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
  26. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +24 -17
  27. package/corpus/templates/clips/app/routes/share.$shareId.tsx +151 -20
  28. package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
  29. package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
  30. package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
  31. package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
  32. package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
  33. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
  34. package/corpus/templates/clips/desktop/src/app.tsx +24 -0
  35. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
  36. package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
  37. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
  38. package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
  39. package/corpus/templates/clips/desktop/src/styles.css +24 -9
  40. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
  41. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
  42. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
  43. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
  44. package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
  45. package/corpus/templates/clips/server/db/index.ts +1 -0
  46. package/corpus/templates/clips/server/db/schema.ts +1 -0
  47. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
  48. package/corpus/templates/clips/server/lib/emails.ts +23 -0
  49. package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
  50. package/corpus/templates/clips/server/lib/recordings.ts +2 -1
  51. package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
  52. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
  53. package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
  54. package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
  55. package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
  56. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
  57. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
  58. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
  59. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +19 -0
  60. package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
  61. package/corpus/templates/clips/shared/recording-link.ts +4 -0
  62. package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
  63. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
  64. package/corpus/templates/factory/AGENTS.md +5 -1
  65. package/corpus/templates/factory/README.md +6 -2
  66. package/corpus/templates/factory/actions/view-screen.ts +15 -0
  67. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
  68. package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
  69. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  70. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
  71. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
  72. package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
  73. package/corpus/templates/factory/app/routes/agents.tsx +9 -0
  74. package/corpus/templates/mail/server/plugins/auth.ts +0 -2
  75. package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
  76. package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
  77. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  78. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  79. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  80. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  81. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
  82. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  83. package/corpus/templates/slides/app/context/DeckContext.tsx +108 -19
  84. package/corpus/templates/slides/app/global.css +7 -40
  85. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  86. package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
  87. package/corpus/templates/slides/app/pages/DeckEditor.tsx +143 -45
  88. package/dist/agent/index.d.ts +1 -1
  89. package/dist/agent/production-agent.d.ts +30 -0
  90. package/dist/agent/production-agent.js +100 -8
  91. package/dist/agent/types.d.ts +9 -0
  92. package/dist/client/AgentPanel.d.ts +13 -1
  93. package/dist/client/AgentPanel.js +22 -17
  94. package/dist/client/AssistantChat.d.ts +15 -1
  95. package/dist/client/AssistantChat.js +135 -48
  96. package/dist/client/HighlightedCodeBlock.js +12 -2
  97. package/dist/client/MultiTabAssistantChat.js +44 -27
  98. package/dist/client/agent-chat/index.d.ts +1 -0
  99. package/dist/client/agent-chat/index.js +1 -0
  100. package/dist/client/chat/markdown-renderer.js +20 -5
  101. package/dist/client/chat/message-components.d.ts +2 -2
  102. package/dist/client/chat/message-components.js +69 -35
  103. package/dist/client/chat/run-recovery.d.ts +4 -1
  104. package/dist/client/chat/run-recovery.js +62 -30
  105. package/dist/client/chat/tool-call-display.d.ts +12 -3
  106. package/dist/client/chat/tool-call-display.js +79 -40
  107. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  108. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  109. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  110. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  111. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  112. package/dist/client/chat-first/apps-rail.js +1 -1
  113. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  114. package/dist/client/components/ui/message-scroller.js +7 -3
  115. package/dist/client/composer/runtime-adapters.js +3 -2
  116. package/dist/client/context-xray/ContextMeter.js +5 -1
  117. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  118. package/dist/client/error-format.d.ts +4 -0
  119. package/dist/client/error-format.js +93 -5
  120. package/dist/client/i18n.js +83 -17
  121. package/dist/client/observability/ThumbsFeedback.js +10 -8
  122. package/dist/client/settings/AccountSettingsCard.js +48 -19
  123. package/dist/client/sharing/ShareButton.d.ts +1 -2
  124. package/dist/client/sharing/ShareButton.js +151 -81
  125. package/dist/client/sharing/ShareDialog.js +5 -4
  126. package/dist/client/use-chat-threads.js +10 -14
  127. package/dist/coding-tools/run-code.js +2 -1
  128. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  129. package/dist/coding-tools/sandbox/background.js +8 -1
  130. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  131. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  132. package/dist/collab/routes.d.ts +1 -1
  133. package/dist/collab/struct-routes.d.ts +1 -1
  134. package/dist/index.d.ts +1 -1
  135. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  136. package/dist/localization/core-messages/ar-SA.js +541 -0
  137. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  138. package/dist/localization/core-messages/de-DE.js +513 -0
  139. package/dist/localization/core-messages/en-US.d.ts +513 -0
  140. package/dist/localization/core-messages/en-US.js +513 -0
  141. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  142. package/dist/localization/core-messages/es-ES.js +520 -0
  143. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  144. package/dist/localization/core-messages/fr-FR.js +520 -0
  145. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  146. package/dist/localization/core-messages/hi-IN.js +513 -0
  147. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  148. package/dist/localization/core-messages/ja-JP.js +506 -0
  149. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  150. package/dist/localization/core-messages/ko-KR.js +506 -0
  151. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  152. package/dist/localization/core-messages/pt-BR.js +520 -0
  153. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  154. package/dist/localization/core-messages/zh-CN.js +506 -0
  155. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  156. package/dist/localization/core-messages/zh-TW.js +506 -0
  157. package/dist/localization/core-messages.d.ts +524 -0
  158. package/dist/localization/core-messages.js +124 -0
  159. package/dist/localization/default-messages.d.ts +3 -0
  160. package/dist/localization/default-messages.js +3 -0
  161. package/dist/notifications/routes.d.ts +1 -1
  162. package/dist/observability/routes.d.ts +5 -5
  163. package/dist/progress/routes.d.ts +1 -1
  164. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  165. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  166. package/dist/resources/handlers.d.ts +1 -1
  167. package/dist/secrets/routes.d.ts +9 -9
  168. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  169. package/dist/server/agent-chat-plugin.d.ts +10 -0
  170. package/dist/server/agent-chat-plugin.js +144 -41
  171. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  172. package/dist/server/agent-run-context.d.ts +7 -1
  173. package/dist/server/agent-run-context.js +9 -2
  174. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  175. package/dist/server/agent-teams.js +38 -9
  176. package/dist/server/auth-marketing.d.ts +1 -0
  177. package/dist/server/auth-marketing.js +0 -2
  178. package/dist/server/auth.d.ts +1 -0
  179. package/dist/server/auth.js +18 -0
  180. package/dist/server/index.d.ts +1 -1
  181. package/dist/server/onboarding-html.d.ts +1 -0
  182. package/dist/server/onboarding-html.js +2 -88
  183. package/dist/server/realtime-token.d.ts +1 -1
  184. package/dist/server/request-context.d.ts +7 -0
  185. package/dist/server/request-context.js +15 -0
  186. package/dist/shared/markdown-block-split.d.ts +16 -0
  187. package/dist/shared/markdown-block-split.js +81 -5
  188. package/dist/styles/agent-native.css +13 -0
  189. package/dist/voice/voice-cleanup-prompt.js +1 -0
  190. package/docs/content/agent-surfaces.mdx +30 -0
  191. package/package.json +2 -2
  192. package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 8236
34
+ - template files: 8241
@@ -265,7 +265,18 @@ export async function getFirstPartyAnalyticsBigQueryBackfillJob(
265
265
  async function getNextFirstPartyAnalyticsBigQueryBackfillJob(
266
266
  db: Executor,
267
267
  now: string,
268
+ scope?: FirstPartyAnalyticsScope,
268
269
  ): Promise<BigQueryBackfillJob | null> {
270
+ const scopeClauses: string[] = [];
271
+ const args: unknown[] = [now, now];
272
+ if (scope?.orgId) {
273
+ scopeClauses.push("AND org_id = ?");
274
+ args.push(scope.orgId);
275
+ }
276
+ if (scope?.userEmail) {
277
+ scopeClauses.push("AND owner_email = ?");
278
+ args.push(scope.userEmail);
279
+ }
269
280
  const result = await db.execute({
270
281
  sql: `SELECT id, org_id, owner_email, table_ref, batch_size,
271
282
  backfill_cursor, status, copied_count, lease_token,
@@ -278,9 +289,10 @@ async function getNextFirstPartyAnalyticsBigQueryBackfillJob(
278
289
  OR (status = 'running' AND lease_expires_at IS NOT NULL
279
290
  AND lease_expires_at <= ?)
280
291
  )
292
+ ${scopeClauses.join("\n ")}
281
293
  ORDER BY updated_at ASC
282
294
  LIMIT 1`,
283
- args: [now, now],
295
+ args,
284
296
  timeoutMs: 3_000,
285
297
  maxAttempts: 1,
286
298
  });
@@ -820,8 +832,8 @@ async function pressureSnapshot(
820
832
  sql: `SELECT
821
833
  COUNT(*) AS total_sessions,
822
834
  SUM(CASE WHEN state = 'active' THEN 1 ELSE 0 END) AS active_sessions,
823
- SUM(CASE WHEN wait_event_type IS NOT NULL THEN 1 ELSE 0 END) AS waiting_sessions,
824
- SUM(CASE WHEN wait_event_type = 'Lock' THEN 1 ELSE 0 END) AS lock_waiters
835
+ SUM(CASE WHEN state = 'active' AND wait_event_type IS NOT NULL THEN 1 ELSE 0 END) AS waiting_sessions,
836
+ SUM(CASE WHEN state = 'active' AND wait_event_type = 'Lock' THEN 1 ELSE 0 END) AS lock_waiters
825
837
  FROM pg_stat_activity
826
838
  WHERE pid <> pg_backend_pid()`,
827
839
  timeoutMs: 2_000,
@@ -870,7 +882,9 @@ async function recordPressurePause(
870
882
  });
871
883
  }
872
884
 
873
- export async function runFirstPartyAnalyticsBigQueryBackfillOnce(): Promise<BigQueryBackfillSweepResult> {
885
+ export async function runFirstPartyAnalyticsBigQueryBackfillOnce(
886
+ scope?: FirstPartyAnalyticsScope,
887
+ ): Promise<BigQueryBackfillSweepResult> {
874
888
  if (process.env.ANALYTICS_BIGQUERY_BACKFILL_JOBS?.trim() === "0") {
875
889
  return { status: "disabled", batches: 0, copied: 0, remaining: 0 };
876
890
  }
@@ -879,6 +893,7 @@ export async function runFirstPartyAnalyticsBigQueryBackfillOnce(): Promise<BigQ
879
893
  const coordinator = await getNextFirstPartyAnalyticsBigQueryBackfillJob(
880
894
  db,
881
895
  new Date().toISOString(),
896
+ scope,
882
897
  );
883
898
  if (!coordinator) {
884
899
  return { status: "idle", batches: 0, copied: 0, remaining: 0 };
@@ -913,7 +913,8 @@ function addPartitionPrunedEventDeduplication(
913
913
  }
914
914
  result +=
915
915
  sql.slice(cursor, predicateEnd) +
916
- " QUALIFY ROW_NUMBER() OVER (PARTITION BY id ORDER BY received_at DESC) = 1";
916
+ " QUALIFY ROW_NUMBER() OVER (PARTITION BY id ORDER BY received_at DESC) = 1" +
917
+ (predicateEnd < sql.length ? " " : "");
917
918
  cursor = predicateEnd;
918
919
  }
919
920
  return result;
@@ -1405,7 +1406,11 @@ export async function backfillFirstPartyAnalyticsBatch(
1405
1406
  const selectedRows = rows.slice(0, boundedLimit);
1406
1407
  if (!selectedRows.length) {
1407
1408
  return {
1408
- nextCursor: serializeBackfillCursor(parsedCursor),
1409
+ // An empty shard has no tuple cursor. Returning the sentinel empty
1410
+ // cursor makes the shard worker reject an otherwise successful drain.
1411
+ nextCursor: parsedCursor.receivedAt
1412
+ ? serializeBackfillCursor(parsedCursor)
1413
+ : null,
1409
1414
  copied: 0,
1410
1415
  complete: true,
1411
1416
  };
@@ -159,7 +159,9 @@ export function AssetPreviewDialog({
159
159
  onClick={() => setShowDetails((value) => !value)}
160
160
  >
161
161
  <IconInfoCircle className="h-4 w-4" />
162
- {t("library.viewDetails")}
162
+ {showDetails
163
+ ? t("library.hideDetails")
164
+ : t("library.viewDetails")}
163
165
  </Button>
164
166
  <DialogClose
165
167
  aria-label={t("library.closePreview")}
@@ -107,6 +107,10 @@
107
107
  text-align: center;
108
108
  }
109
109
 
110
+ .assets-create-chat-panel [data-agent-empty-state="centered"] {
111
+ justify-content: safe center;
112
+ }
113
+
110
114
  .assets-create-chat-intro h1 {
111
115
  margin: 0;
112
116
  color: hsl(var(--foreground));
@@ -844,10 +844,11 @@ const enUS = {
844
844
  checking: "Checking",
845
845
  checkingUpload: "Checking upload",
846
846
  clearAll: "Clear all",
847
+ clearAllWithCount: "Clear all candidates ({{count}})",
847
848
  clearCandidates: "Clear candidates",
848
849
  clearCandidatesTitle: "Clear {{count}} candidates?",
849
850
  clearCandidatesDescription:
850
- "Clears the live stage and deletes unsaved draft candidates.",
851
+ "Clears the live stage and deletes every unsaved draft candidate. Saved library assets stay untouched.",
851
852
  clearGeneratedCandidatesTitle: "Clear generated candidates?",
852
853
  clearGeneratedCandidatesDescription:
853
854
  "This removes every unsaved candidate from the thread and deletes the generated asset rows behind them. Saved library assets are not touched.",
@@ -986,6 +987,7 @@ const enUS = {
986
987
  open: "Open",
987
988
  openAssets: "Open Assets",
988
989
  openChat: "Open chat",
990
+ hideDetails: "Hide details",
989
991
  openDetails: "Open details",
990
992
  preparing: "Preparing...",
991
993
  previousImage: "Previous image",
@@ -3728,7 +3730,9 @@ export const messagesByLocale = {
3728
3730
  checkingUpload: "正在检查上传",
3729
3731
  clear: "清除",
3730
3732
  clearAll: "全部清除",
3733
+ clearAllWithCount: "清除所有候选项({{count}})",
3731
3734
  clearCandidates: "清除候选项",
3735
+ hideDetails: "隐藏详情",
3732
3736
  clearCandidatesDescription: "清除实时阶段并删除未保存的草稿候选项。",
3733
3737
  clearCandidatesTitle: "清除 {{count}} 个候选项?",
3734
3738
  clearGeneratedCandidatesDescription:
@@ -4552,7 +4556,9 @@ export const messagesByLocale = {
4552
4556
  checkingImageLibraries: "Comprobando bibliotecas de imágenes...",
4553
4557
  checkingUpload: "Comprobando carga",
4554
4558
  clearAll: "Borrar todo",
4559
+ clearAllWithCount: "Borrar todos los candidatos ({{count}})",
4555
4560
  clearCandidates: "Limpiar candidatos",
4561
+ hideDetails: "Ocultar detalles",
4556
4562
  clearCandidatesDescription:
4557
4563
  "Limpia la etapa en vivo y elimina los candidatos de borrador no guardados.",
4558
4564
  clearGeneratedCandidatesDescription:
@@ -5026,7 +5032,9 @@ export const messagesByLocale = {
5026
5032
  checkingImageLibraries: "Vérification des bibliothèques d'images...",
5027
5033
  checkingUpload: "Vérification du téléchargement",
5028
5034
  clearAll: "Tout effacer",
5035
+ clearAllWithCount: "Effacer tous les candidats ({{count}})",
5029
5036
  clearCandidates: "Effacer les candidats",
5037
+ hideDetails: "Masquer les détails",
5030
5038
  clearCandidatesDescription:
5031
5039
  "Efface l'étape en direct et supprime les candidats de brouillon non enregistrés.",
5032
5040
  clearGeneratedCandidatesDescription:
@@ -5479,7 +5487,9 @@ export const messagesByLocale = {
5479
5487
  checkingImageLibraries: "Bildbibliotheken werden geprüft...",
5480
5488
  checkingUpload: "Upload wird überprüft",
5481
5489
  clearAll: "Alles löschen",
5490
+ clearAllWithCount: "Alle Kandidaten löschen ({{count}})",
5482
5491
  clearCandidates: "Kandidaten löschen",
5492
+ hideDetails: "Details ausblenden",
5483
5493
  clearCandidatesDescription:
5484
5494
  "Löscht die Live-Phase und entfernt nicht gespeicherte Entwurfskandidaten.",
5485
5495
  clearGeneratedCandidatesDescription:
@@ -5837,7 +5847,9 @@ export const messagesByLocale = {
5837
5847
  checkingImageLibraries: "画像ライブラリを確認中...",
5838
5848
  checkingUpload: "アップロードを確認しています",
5839
5849
  clearAll: "すべてクリア",
5850
+ clearAllWithCount: "候補をすべてクリア({{count}})",
5840
5851
  clearCandidates: "候補をクリアする",
5852
+ hideDetails: "詳細を非表示",
5841
5853
  clearCandidatesDescription:
5842
5854
  "ライブ ステージをクリアし、保存されていないドラフト候補を削除します。",
5843
5855
  clearGeneratedCandidatesDescription:
@@ -6186,7 +6198,9 @@ export const messagesByLocale = {
6186
6198
  checkingImageLibraries: "이미지 라이브러리 확인 중...",
6187
6199
  checkingUpload: "업로드 확인 중",
6188
6200
  clearAll: "모두 지우기",
6201
+ clearAllWithCount: "후보 모두 지우기({{count}})",
6189
6202
  clearCandidates: "후보 제거",
6203
+ hideDetails: "세부정보 숨기기",
6190
6204
  clearCandidatesDescription:
6191
6205
  "라이브 단계를 지우고 저장되지 않은 초안 후보를 삭제합니다.",
6192
6206
  clearGeneratedCandidatesDescription:
@@ -6534,7 +6548,9 @@ export const messagesByLocale = {
6534
6548
  checkingImageLibraries: "Verificando bibliotecas de imagens...",
6535
6549
  checkingUpload: "Verificando upload",
6536
6550
  clearAll: "Limpar tudo",
6551
+ clearAllWithCount: "Limpar todos os candidatos ({{count}})",
6537
6552
  clearCandidates: "Limpar candidatos",
6553
+ hideDetails: "Ocultar detalhes",
6538
6554
  clearCandidatesDescription:
6539
6555
  "Limpa o estágio ao vivo e exclui candidatos de rascunho não salvos.",
6540
6556
  clearGeneratedCandidatesDescription:
@@ -6985,7 +7001,9 @@ export const messagesByLocale = {
6985
7001
  checkingImageLibraries: "Image libraries जांची जा रही हैं...",
6986
7002
  checkingUpload: "अपलोड की जाँच की जा रही है",
6987
7003
  clearAll: "सभी साफ करें",
7004
+ clearAllWithCount: "सभी उम्मीदवार साफ करें ({{count}})",
6988
7005
  clearCandidates: "उम्मीदवारों को साफ करें",
7006
+ hideDetails: "विवरण छिपाएं",
6989
7007
  clearCandidatesDescription:
6990
7008
  "लाइव चरण को साफ करता है और अ सहेजे गए ड्राफ्ट उम्मीदवारों को हटाता है।",
6991
7009
  clearGeneratedCandidatesDescription:
@@ -7331,7 +7349,9 @@ export const messagesByLocale = {
7331
7349
  checkingImageLibraries: "جارٍ فحص مكتبات الصور...",
7332
7350
  checkingUpload: "التحقق من التحميل",
7333
7351
  clearAll: "مسح الكل",
7352
+ clearAllWithCount: "مسح كل المرشحين ({{count}})",
7334
7353
  clearCandidates: "امسح المرشحين",
7354
+ hideDetails: "إخفاء التفاصيل",
7335
7355
  clearCandidatesDescription:
7336
7356
  "مسح مرحلة المرشح وحذف المرشحين الذين تم إنشاؤهم غير المحفوظة. أصول المكتبة المحفوظة تبقى دون تغيير.",
7337
7357
  clearGeneratedCandidatesDescription:
@@ -1217,12 +1217,11 @@ export function BrandKitDetailRoute({
1217
1217
  );
1218
1218
  const shareAction = (
1219
1219
  <ShareButton
1220
- trigger="label-icon"
1221
1220
  resourceType="asset-library"
1222
1221
  resourceId={library.id}
1223
1222
  allowedRoles={["viewer", "editor", "admin"]}
1224
1223
  resourceTitle={library.title}
1225
- triggerClassName="h-10 gap-2 px-4 border-input bg-background hover:bg-accent hover:text-accent-foreground"
1224
+ triggerClassName="h-10 px-4 border-input bg-background hover:bg-accent hover:text-accent-foreground"
1226
1225
  />
1227
1226
  );
1228
1227
  const headerActions = (
@@ -4533,7 +4532,9 @@ function LiveCandidatesActions({
4533
4532
  const hasFailed = failedCount > 0;
4534
4533
  const isClearing = dismissSlots.isPending || deleteAssets.isPending;
4535
4534
  const actionLabel =
4536
- pending === "failed" ? t("library.dismissFailed") : t("library.clearAll");
4535
+ pending === "failed"
4536
+ ? t("library.dismissFailed")
4537
+ : t("library.clearAllWithCount", { count: totalCount });
4537
4538
  const busyLabel =
4538
4539
  pending === "failed" ? t("library.dismissing") : t("library.clearing");
4539
4540
 
@@ -4645,26 +4646,28 @@ function LiveCandidatesActions({
4645
4646
  </Button>
4646
4647
  </DropdownMenuTrigger>
4647
4648
  <DropdownMenuContent align="end">
4648
- <DropdownMenuItem
4649
- disabled={!hasFailed || isClearing}
4650
- onSelect={(event) => {
4651
- event.preventDefault();
4652
- setPending("failed");
4653
- }}
4654
- >
4655
- <IconTrash className="mr-2 h-4 w-4 shrink-0" />
4656
- {t("library.dismissFailedWithCount", { count: failedCount })}
4657
- </DropdownMenuItem>
4649
+ {hasFailed ? (
4650
+ <DropdownMenuItem
4651
+ disabled={isClearing}
4652
+ onSelect={(event) => {
4653
+ event.preventDefault();
4654
+ setPending("failed");
4655
+ }}
4656
+ >
4657
+ <IconTrash className="mr-2 h-4 w-4 shrink-0" />
4658
+ {t("library.dismissFailedWithCount", { count: failedCount })}
4659
+ </DropdownMenuItem>
4660
+ ) : null}
4658
4661
  <DropdownMenuItem
4659
4662
  className="text-destructive focus:bg-destructive/10 focus:text-destructive"
4660
- disabled={isClearing}
4663
+ disabled={isClearing || totalCount === 0}
4661
4664
  onSelect={(event) => {
4662
4665
  event.preventDefault();
4663
4666
  setPending("all");
4664
4667
  }}
4665
4668
  >
4666
4669
  <IconTrash className="mr-2 h-4 w-4 shrink-0" />
4667
- {t("library.clearAll")}
4670
+ {t("library.clearAllWithCount", { count: totalCount })}
4668
4671
  </DropdownMenuItem>
4669
4672
  </DropdownMenuContent>
4670
4673
  </DropdownMenu>
@@ -1875,8 +1875,7 @@ function SourceListItem({
1875
1875
  resourceId={source.id}
1876
1876
  allowedRoles={["viewer", "editor", "admin"]}
1877
1877
  resourceTitle={sourceName(source)}
1878
- trigger="icon"
1879
- triggerClassName="size-8"
1878
+ triggerClassName="h-8 px-2"
1880
1879
  />
1881
1880
 
1882
1881
  <DropdownMenu>
@@ -1,5 +1,5 @@
1
1
  import { useActionQuery } from "@agent-native/core/client/hooks";
2
- import type { Booking } from "@shared/api";
2
+ import type { Booking, BookingHost } from "@shared/api";
3
3
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
4
4
 
5
5
  import { appApiPath } from "@/lib/api-path";
@@ -17,16 +17,26 @@ export function useBookings() {
17
17
  return useActionQuery<Booking[]>("list-bookings");
18
18
  }
19
19
 
20
+ export type BookingAvailabilityPreview = {
21
+ slug: string;
22
+ durations: number[];
23
+ hosts: BookingHost[];
24
+ };
25
+
20
26
  export function useAvailableSlots(
21
27
  date: string,
22
28
  duration: number,
23
29
  slug?: string,
30
+ draft?: BookingAvailabilityPreview,
24
31
  ) {
32
+ const draftParam = draft ? JSON.stringify(draft) : undefined;
33
+
25
34
  return useQuery<{ start: string; end: string }[]>({
26
- queryKey: ["available-slots", date, duration, slug],
35
+ queryKey: ["available-slots", date, duration, slug, draftParam],
27
36
  queryFn: async () => {
28
37
  const params = new URLSearchParams({ date, duration: String(duration) });
29
38
  if (slug) params.set("slug", slug);
39
+ if (draftParam) params.set("draft", draftParam);
30
40
  const res = await fetch(
31
41
  appApiPath(`/api/bookings/available-slots?${params}`),
32
42
  );
@@ -46,6 +46,7 @@ import {
46
46
  addMonths,
47
47
  subMonths,
48
48
  format,
49
+ parseISO,
49
50
  startOfDay,
50
51
  getDay,
51
52
  } from "date-fns";
@@ -113,6 +114,10 @@ import {
113
114
  useUpdateBookingLink,
114
115
  OPTIMISTIC_PREFIX,
115
116
  } from "@/hooks/use-booking-links";
117
+ import {
118
+ useAvailableSlots,
119
+ type BookingAvailabilityPreview,
120
+ } from "@/hooks/use-bookings";
116
121
  import { useGoogleAuthStatus } from "@/hooks/use-google-auth";
117
122
  import { useSettings } from "@/hooks/use-settings";
118
123
  import { useZoomStatus, useConnectZoom } from "@/hooks/use-zoom-auth";
@@ -826,6 +831,14 @@ export default function BookingLinksPage({
826
831
  !!selectedLink &&
827
832
  savedDraftSignature !== null &&
828
833
  draftSignature !== savedDraftSignature;
834
+ const availabilityPreview =
835
+ selectedLink && !selectedLink.id.startsWith(OPTIMISTIC_PREFIX)
836
+ ? ({
837
+ slug: slugify(draft.slug),
838
+ durations: draft.durations,
839
+ hosts: draft.hosts,
840
+ } satisfies BookingAvailabilityPreview)
841
+ : undefined;
829
842
 
830
843
  function handleCreate() {
831
844
  setCreateDialogOpen(true);
@@ -1535,6 +1548,13 @@ export default function BookingLinksPage({
1535
1548
  customFields={draft.customFields}
1536
1549
  isActive={draft.isActive}
1537
1550
  availability={availability ?? undefined}
1551
+ bookingSourceSlug={
1552
+ selectedLink.id?.startsWith(OPTIMISTIC_PREFIX) ||
1553
+ !availabilityPreview
1554
+ ? undefined
1555
+ : selectedLink.slug
1556
+ }
1557
+ availabilityPreview={availabilityPreview}
1538
1558
  bookingUrl={previewUrl}
1539
1559
  onCopy={() => void copyPreviewUrl(draft.slug)}
1540
1560
  openHref={bookingPreviewPath(draft.slug)}
@@ -2022,6 +2042,8 @@ function BookingPreview({
2022
2042
  customFields = [],
2023
2043
  isActive,
2024
2044
  availability,
2045
+ bookingSourceSlug,
2046
+ availabilityPreview,
2025
2047
  bookingUrl,
2026
2048
  onCopy,
2027
2049
  openHref,
@@ -2034,6 +2056,8 @@ function BookingPreview({
2034
2056
  customFields?: CustomField[];
2035
2057
  isActive: boolean;
2036
2058
  availability?: AvailabilityConfig;
2059
+ bookingSourceSlug?: string;
2060
+ availabilityPreview?: BookingAvailabilityPreview;
2037
2061
  bookingUrl?: string;
2038
2062
  onCopy?: () => void;
2039
2063
  openHref?: string;
@@ -2062,6 +2086,24 @@ function BookingPreview({
2062
2086
  fieldResponses: {},
2063
2087
  });
2064
2088
 
2089
+ const liveAvailabilityDate =
2090
+ bookingSourceSlug && selectedDate ? format(selectedDate, "yyyy-MM-dd") : "";
2091
+ const liveAvailabilityDuration =
2092
+ selectedDuration !== null && durations.includes(selectedDuration)
2093
+ ? selectedDuration
2094
+ : primaryDuration;
2095
+ const {
2096
+ data: liveSlots = [],
2097
+ isLoading: liveSlotsLoading,
2098
+ isError: liveSlotsError,
2099
+ } = useAvailableSlots(
2100
+ liveAvailabilityDate,
2101
+ liveAvailabilityDuration,
2102
+ bookingSourceSlug,
2103
+ availabilityPreview,
2104
+ );
2105
+ const hasLiveAvailability = Boolean(bookingSourceSlug && selectedDate);
2106
+
2065
2107
  // Reset selections when durations change
2066
2108
  useEffect(() => {
2067
2109
  setSelectedDuration(null);
@@ -2092,6 +2134,9 @@ function BookingPreview({
2092
2134
 
2093
2135
  // Generate realistic time slots based on availability
2094
2136
  const timeSlots = useMemo(() => {
2137
+ if (hasLiveAvailability) {
2138
+ return liveSlots.map((slot) => format(parseISO(slot.start), "h:mm a"));
2139
+ }
2095
2140
  if (!selectedDate || !availability) {
2096
2141
  return ["9:00 AM", "9:30 AM", "10:00 AM", "10:30 AM", "11:00 AM"];
2097
2142
  }
@@ -2122,7 +2167,14 @@ function BookingPreview({
2122
2167
  }
2123
2168
  }
2124
2169
  return slots;
2125
- }, [selectedDate, selectedDuration, primaryDuration, availability]);
2170
+ }, [
2171
+ selectedDate,
2172
+ selectedDuration,
2173
+ primaryDuration,
2174
+ availability,
2175
+ hasLiveAvailability,
2176
+ liveSlots,
2177
+ ]);
2126
2178
 
2127
2179
  // Determine which step to show
2128
2180
  const [forcedStep, setForcedStep] = useState<BookingPreviewStep | null>(null);
@@ -2458,7 +2510,17 @@ function BookingPreview({
2458
2510
  {t("bookingLinks.availableTimes")}
2459
2511
  </p>
2460
2512
  )}
2461
- {timeSlots.length > 0 ? (
2513
+ {liveSlotsLoading ? (
2514
+ <div className="grid grid-cols-3 gap-1.5">
2515
+ {Array.from({ length: 6 }).map((_, index) => (
2516
+ <Skeleton key={index} className="h-8 rounded-md" />
2517
+ ))}
2518
+ </div>
2519
+ ) : liveSlotsError ? (
2520
+ <p className="rounded-md border border-destructive/30 bg-destructive/[0.06] px-2.5 py-2 text-center text-xs text-destructive">
2521
+ {t("bookingLinks.availabilityUnavailable")}
2522
+ </p>
2523
+ ) : timeSlots.length > 0 ? (
2462
2524
  <div className="grid grid-cols-3 gap-1.5">
2463
2525
  {timeSlots.map((slot) => (
2464
2526
  <button