@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
@@ -0,0 +1,64 @@
1
+ import type { TimeSlot } from "./api.js";
2
+
3
+ function timeToMinutes(value: string): number | null {
4
+ const match = /^(\d{1,2}):(\d{2})$/.exec(value);
5
+ if (!match) return null;
6
+ const hours = Number(match[1]);
7
+ const minutes = Number(match[2]);
8
+ if (hours > 23 || minutes > 59) return null;
9
+ return hours * 60 + minutes;
10
+ }
11
+
12
+ /** Returns the saved windows in chronological order with adjacent overlaps merged. */
13
+ export function normalizeAvailabilitySlots(slots: TimeSlot[]): TimeSlot[] {
14
+ const valid = slots
15
+ .map((slot) => ({
16
+ slot,
17
+ start: timeToMinutes(slot.start),
18
+ end: timeToMinutes(slot.end),
19
+ }))
20
+ .filter(
21
+ (entry): entry is typeof entry & { start: number; end: number } =>
22
+ entry.start !== null && entry.end !== null && entry.start < entry.end,
23
+ )
24
+ .sort((a, b) => a.start - b.start || a.end - b.end);
25
+
26
+ const normalized: Array<{
27
+ slot: TimeSlot;
28
+ start: number;
29
+ end: number;
30
+ }> = [];
31
+ for (const entry of valid) {
32
+ const previous = normalized[normalized.length - 1];
33
+ if (previous && entry.start <= previous.end) {
34
+ const mergedEnd = Math.max(previous.end, entry.end);
35
+ previous.slot = {
36
+ ...previous.slot,
37
+ end: entry.end > previous.end ? entry.slot.end : previous.slot.end,
38
+ };
39
+ previous.end = mergedEnd;
40
+ continue;
41
+ }
42
+ normalized.push({ ...entry, slot: { ...entry.slot } });
43
+ }
44
+
45
+ return normalized.map(({ slot }) => slot);
46
+ }
47
+
48
+ export function availabilitySlotsOverlap(slots: TimeSlot[]): boolean {
49
+ const valid = slots
50
+ .map((slot) => ({
51
+ start: timeToMinutes(slot.start),
52
+ end: timeToMinutes(slot.end),
53
+ }))
54
+ .filter(
55
+ (entry): entry is { start: number; end: number } =>
56
+ entry.start !== null && entry.end !== null && entry.start < entry.end,
57
+ )
58
+ .sort((a, b) => a.start - b.start || a.end - b.end);
59
+
60
+ return valid.some((slot, index) => {
61
+ const previous = valid[index - 1];
62
+ return previous !== undefined && slot.start < previous.end;
63
+ });
64
+ }
@@ -0,0 +1,21 @@
1
+ export type CalendarWeekStart = "sunday" | "monday";
2
+
3
+ export type Weekday = 0 | 1 | 2 | 3 | 4 | 5 | 6;
4
+
5
+ export const DEFAULT_CALENDAR_WEEK_START: CalendarWeekStart = "sunday";
6
+
7
+ export function isCalendarWeekStart(
8
+ value: unknown,
9
+ ): value is CalendarWeekStart {
10
+ return value === "sunday" || value === "monday";
11
+ }
12
+
13
+ export function getWeekStartsOn(
14
+ weekStart: CalendarWeekStart | null | undefined,
15
+ ): 0 | 1 {
16
+ return weekStart === "monday" ? 1 : 0;
17
+ }
18
+
19
+ export function getWeekdayOrder(weekStartsOn: 0 | 1): Weekday[] {
20
+ return weekStartsOn === 1 ? [1, 2, 3, 4, 5, 6, 0] : [0, 1, 2, 3, 4, 5, 6];
21
+ }
@@ -0,0 +1,44 @@
1
+ import type { Settings } from "./api.js";
2
+ import {
3
+ DEFAULT_CALENDAR_WEEK_START,
4
+ isCalendarWeekStart,
5
+ } from "./calendar-week.js";
6
+
7
+ export const DEFAULT_SETTINGS: Settings = {
8
+ timezone: "America/New_York",
9
+ bookingPageTitle: "Book a Meeting",
10
+ bookingPageDescription: "Select a time that works for you.",
11
+ defaultEventDuration: 30,
12
+ weekStart: DEFAULT_CALENDAR_WEEK_START,
13
+ };
14
+
15
+ export function normalizeCalendarSettings(input: unknown): Settings {
16
+ const raw =
17
+ input && typeof input === "object"
18
+ ? (input as Partial<Settings>)
19
+ : ({} as Partial<Settings>);
20
+
21
+ return {
22
+ timezone:
23
+ typeof raw.timezone === "string"
24
+ ? raw.timezone
25
+ : DEFAULT_SETTINGS.timezone,
26
+ bookingPageTitle:
27
+ typeof raw.bookingPageTitle === "string"
28
+ ? raw.bookingPageTitle
29
+ : DEFAULT_SETTINGS.bookingPageTitle,
30
+ bookingPageDescription:
31
+ typeof raw.bookingPageDescription === "string"
32
+ ? raw.bookingPageDescription
33
+ : DEFAULT_SETTINGS.bookingPageDescription,
34
+ defaultEventDuration:
35
+ typeof raw.defaultEventDuration === "number" &&
36
+ Number.isFinite(raw.defaultEventDuration) &&
37
+ raw.defaultEventDuration > 0
38
+ ? raw.defaultEventDuration
39
+ : DEFAULT_SETTINGS.defaultEventDuration,
40
+ weekStart: isCalendarWeekStart(raw.weekStart)
41
+ ? raw.weekStart
42
+ : DEFAULT_SETTINGS.weekStart,
43
+ };
44
+ }
@@ -31,6 +31,8 @@ video sharing app. The agent and the UI share the same SQL data and actions.
31
31
  secrets/OAuth/runtime config and obvious placeholders.
32
32
  - Use actions for recording metadata, transcripts, cleanup, summaries, chapters,
33
33
  comments, spaces/folders, meetings, and sharing. Never bypass access helpers.
34
+ - Comment text accepts inline Markdown for emphasis, inline code, links, and
35
+ line breaks; headings and other block structures stay flattened in comments.
34
36
  - Recording start/stop/pause are UI gestures — browser capture needs user
35
37
  activation; navigate to the recording view instead of a server action.
36
38
  - Native transcript first; cloud transcription is fallback-only. Never hide a
@@ -20,10 +20,13 @@ import { nanoid } from "../server/lib/recordings.js";
20
20
 
21
21
  export default defineAction({
22
22
  description:
23
- "Add a comment to a recording at a specific video timestamp. For new threads, omit threadId/parentId. For replies, pass both.",
23
+ "Add a comment to a recording at a specific video timestamp. Comment text supports inline Markdown such as bold, italic, inline code, and links; headings are flattened in comment surfaces. For new threads, omit threadId/parentId. For replies, pass both.",
24
24
  schema: z.object({
25
25
  recordingId: z.string().describe("Recording ID"),
26
- content: z.string().min(1).describe("Comment text"),
26
+ content: z
27
+ .string()
28
+ .min(1)
29
+ .describe("Comment text; inline Markdown is supported, without headings"),
27
30
  videoTimestampMs: z
28
31
  .number()
29
32
  .int()
@@ -50,6 +50,10 @@ import {
50
50
  } from "../server/lib/resumable-session.js";
51
51
  import { resolveResumableUploadProvider } from "../server/lib/resumable-upload-provider.js";
52
52
  import { fetchS3ObjectByUrl } from "../server/lib/s3-upload-provider.js";
53
+ import {
54
+ clearSeekableRepairPending,
55
+ markSeekableRepairPending,
56
+ } from "../server/lib/seekable-media-state.js";
53
57
  import { isStreamingUploadDisabled } from "../server/lib/streaming-upload-mode.js";
54
58
  import {
55
59
  probeHasAudioStream,
@@ -59,7 +63,10 @@ import {
59
63
  requiresConfiguredVideoStorage,
60
64
  STORAGE_SETUP_REQUIRED_REASON,
61
65
  } from "../server/lib/video-storage.js";
62
- import { markRecordingSeekable } from "./lib/ensure-seekable-video.js";
66
+ import {
67
+ isRemoteProviderUrl,
68
+ markRecordingSeekable,
69
+ } from "./lib/ensure-seekable-video.js";
63
70
 
64
71
  // Recordings up to this size get their seekable rewrite applied inline during
65
72
  // finalize (we already hold the assembled bytes). Larger recordings are handed
@@ -398,6 +405,7 @@ async function persistPendingMediaVerification(params: {
398
405
  videoUrl: media.videoUrl,
399
406
  videoFormat: media.videoFormat,
400
407
  videoSizeBytes: media.videoSizeBytes,
408
+ mediaUpdatedAt: now,
401
409
  durationMs: media.finalDurationMs,
402
410
  width: media.finalWidth,
403
411
  height: media.finalHeight,
@@ -576,6 +584,7 @@ async function markRecordingReady(params: {
576
584
  videoUrl,
577
585
  videoFormat,
578
586
  videoSizeBytes,
587
+ mediaUpdatedAt: now,
579
588
  durationMs: finalDurationMs,
580
589
  width: finalWidth,
581
590
  height: finalHeight,
@@ -667,6 +676,12 @@ async function markRecordingReady(params: {
667
676
  if (seekableApplied) {
668
677
  // Uploaded bytes are already start-playable and seekable — remember it so
669
678
  // later reprocess sweeps skip this clip.
679
+ await clearSeekableRepairPending(id).catch((err) => {
680
+ console.warn("[finalize] failed to clear seekable repair marker", {
681
+ id,
682
+ error: err instanceof Error ? err.message : String(err),
683
+ });
684
+ });
670
685
  await markRecordingSeekable(id, videoUrl).catch((err) => {
671
686
  console.warn("[finalize] failed to write seekable marker", {
672
687
  id,
@@ -679,6 +694,17 @@ async function markRecordingReady(params: {
679
694
  // without a Cues index buffers on load and re-buffers on every seek. A
680
695
  // fresh self-dispatched request owns the repair so serverless runtimes do
681
696
  // not freeze it when this finalize request returns.
697
+ if (isRemoteProviderUrl(videoUrl)) {
698
+ await markSeekableRepairPending({
699
+ recordingId: id,
700
+ videoUrl,
701
+ }).catch((err) => {
702
+ console.warn("[finalize] failed to mark seekable repair pending", {
703
+ id,
704
+ error: err instanceof Error ? err.message : String(err),
705
+ });
706
+ });
707
+ }
682
708
  await dispatchPostFinalizeJob({
683
709
  recordingId: id,
684
710
  kind: "seekable",
@@ -1283,6 +1309,7 @@ export default defineAction({
1283
1309
  .set({
1284
1310
  status: "processing",
1285
1311
  uploadProgress: 100,
1312
+ mediaUpdatedAt: new Date().toISOString(),
1286
1313
  updatedAt: new Date().toISOString(),
1287
1314
  })
1288
1315
  .where(eq(schema.recordings.id, id));
@@ -1303,6 +1330,7 @@ export default defineAction({
1303
1330
  .set({
1304
1331
  status: "failed",
1305
1332
  failureReason,
1333
+ mediaUpdatedAt: now,
1306
1334
  updatedAt: now,
1307
1335
  })
1308
1336
  .where(eq(schema.recordings.id, id));
@@ -1623,6 +1651,7 @@ export default defineAction({
1623
1651
  hasAudio: finalHasAudio,
1624
1652
  hasCamera: finalHasCamera,
1625
1653
  uploadProgress: 0,
1654
+ mediaUpdatedAt: now,
1626
1655
  updatedAt: now,
1627
1656
  })
1628
1657
  .where(eq(schema.recordings.id, id));
@@ -1657,6 +1686,7 @@ export default defineAction({
1657
1686
  hasAudio: finalHasAudio,
1658
1687
  hasCamera: finalHasCamera,
1659
1688
  uploadProgress: 100,
1689
+ mediaUpdatedAt: now,
1660
1690
  updatedAt: now,
1661
1691
  })
1662
1692
  .where(eq(schema.recordings.id, id));
@@ -40,6 +40,7 @@ import {
40
40
  countRecordingViews,
41
41
  parseSpaceIds,
42
42
  } from "../server/lib/recordings.js";
43
+ import { isSeekableRepairPending } from "../server/lib/seekable-media-state.js";
43
44
  import { parseBrowserDiagnosticsRow } from "../shared/browser-diagnostics.js";
44
45
  import {
45
46
  CLIPS_BUILDER_CREDITS_STATE_KEY,
@@ -161,6 +162,7 @@ export default defineAction({
161
162
  cleanupStateRaw,
162
163
  builderCreditsRaw,
163
164
  verificationPending,
165
+ seekableRepairPending,
164
166
  viewCount,
165
167
  agentViewCount,
166
168
  ] = await Promise.all([
@@ -173,6 +175,12 @@ export default defineAction({
173
175
  recordingId: args.recordingId,
174
176
  recordingStatus: rec.status,
175
177
  }),
178
+ isSeekableRepairPending({
179
+ ownerEmail: rec.ownerEmail,
180
+ recordingId: args.recordingId,
181
+ recordingStatus: rec.status,
182
+ videoUrl: rec.videoUrl,
183
+ }),
176
184
  countRecordingViews(args.recordingId).catch(() => 0),
177
185
  countRecordingAgentViews(args.recordingId).catch(() => 0),
178
186
  ]);
@@ -336,6 +344,7 @@ export default defineAction({
336
344
  hasCamera: Boolean(rec.hasCamera),
337
345
  status: rec.status,
338
346
  verificationPending,
347
+ seekableRepairPending,
339
348
  uploadProgress: rec.uploadProgress,
340
349
  failureReason: rec.failureReason,
341
350
  // Don't leak the password to clients (especially to MCP hosts that
@@ -31,6 +31,7 @@ import { getDb, schema } from "../../server/db/index.js";
31
31
  import { queueBuilderMediaCompression } from "../../server/lib/builder-media-compression.js";
32
32
  import { deleteRecordingMediaObjects } from "../../server/lib/recording-media-cleanup.js";
33
33
  import { ownerEmailMatches } from "../../server/lib/recordings.js";
34
+ import { clearSeekableRepairPending } from "../../server/lib/seekable-media-state.js";
34
35
  import {
35
36
  makeSeekable,
36
37
  normalizeTimelineToMp4,
@@ -144,7 +145,7 @@ export async function fetchProviderBytes(
144
145
  * Ensure a single recording's stored media is seekable. Owner-scoped: pass the
145
146
  * resolved owner email so the DB lookup can only touch that owner's rows.
146
147
  */
147
- export async function ensureRecordingSeekable(params: {
148
+ async function ensureRecordingSeekableInternal(params: {
148
149
  recordingId: string;
149
150
  ownerEmail: string;
150
151
  force?: boolean;
@@ -281,6 +282,7 @@ export async function ensureRecordingSeekable(params: {
281
282
  videoUrl: upload.url,
282
283
  videoFormat: outputFormat,
283
284
  videoSizeBytes: seekable.bytes.byteLength,
285
+ mediaUpdatedAt: new Date().toISOString(),
284
286
  updatedAt: new Date().toISOString(),
285
287
  })
286
288
  .where(
@@ -340,3 +342,21 @@ export async function ensureRecordingSeekable(params: {
340
342
  videoUrl: upload.url,
341
343
  };
342
344
  }
345
+
346
+ export async function ensureRecordingSeekable(params: {
347
+ recordingId: string;
348
+ ownerEmail: string;
349
+ force?: boolean;
350
+ normalizeTimeline?: boolean;
351
+ }): Promise<EnsureSeekableResult> {
352
+ const result = await ensureRecordingSeekableInternal(params);
353
+ if (result.changed || result.status === "already-optimized") {
354
+ await clearSeekableRepairPending(params.recordingId).catch((err) => {
355
+ console.warn("[ensure-seekable-video] failed to clear pending marker", {
356
+ recordingId: params.recordingId,
357
+ error: err instanceof Error ? err.message : String(err),
358
+ });
359
+ });
360
+ }
361
+ return result;
362
+ }
@@ -20,10 +20,13 @@ import { nanoid } from "../server/lib/recordings.js";
20
20
 
21
21
  export default defineAction({
22
22
  description:
23
- "Reply to an existing comment. Looks up the thread and parent and delegates to add-comment.",
23
+ "Reply to an existing comment with inline Markdown text (without headings). Looks up the thread and parent and delegates to add-comment.",
24
24
  schema: z.object({
25
25
  commentId: z.string().describe("Comment ID to reply to"),
26
- content: z.string().min(1).describe("Reply text"),
26
+ content: z
27
+ .string()
28
+ .min(1)
29
+ .describe("Reply text; inline Markdown is supported, without headings"),
27
30
  authorName: z.string().optional(),
28
31
  }),
29
32
  run: async (args) => {
@@ -16,10 +16,17 @@ import { getDb, schema } from "../server/db/index.js";
16
16
  import { sameOwnerEmail } from "../server/lib/recordings.js";
17
17
 
18
18
  export default defineAction({
19
- description: "Update a comment's text. Only the comment author can edit it.",
19
+ description:
20
+ "Update a comment's text. Inline Markdown is supported without headings. Only the comment author can edit it.",
20
21
  schema: z.object({
21
22
  id: z.string().describe("Comment ID"),
22
- content: z.string().trim().min(1).describe("Updated comment text"),
23
+ content: z
24
+ .string()
25
+ .trim()
26
+ .min(1)
27
+ .describe(
28
+ "Updated comment text; inline Markdown is supported, without headings",
29
+ ),
23
30
  }),
24
31
  run: async (args) => {
25
32
  const userEmail = getRequestUserEmail();
@@ -1,7 +1,7 @@
1
1
  import { useT } from "@agent-native/core/client/i18n";
2
2
  import { IconClock, IconSearch, IconX } from "@tabler/icons-react";
3
- import React, { useEffect, useRef, useState } from "react";
4
- import { useNavigate } from "react-router";
3
+ import React, { useCallback, useEffect, useRef, useState } from "react";
4
+ import { useNavigate, useSearchParams } from "react-router";
5
5
 
6
6
  import { msToClock } from "@/components/player/scrubber";
7
7
  import {
@@ -10,6 +10,10 @@ import {
10
10
  PopoverContent,
11
11
  } from "@/components/ui/popover";
12
12
  import { useRecordingSearch, type SearchHit } from "@/hooks/use-library";
13
+ import {
14
+ clearSearchFocusRequest,
15
+ hasSearchFocusRequest,
16
+ } from "@/lib/search-focus";
13
17
  import { cn, shortcutLabel } from "@/lib/utils";
14
18
 
15
19
  function highlight(
@@ -64,22 +68,33 @@ function matchLabel(hit: SearchHit, t: ReturnType<typeof useT>): string {
64
68
  export function SearchBar({ className, side = "right" }: SearchBarProps) {
65
69
  const t = useT();
66
70
  const navigate = useNavigate();
71
+ const [searchParams, setSearchParams] = useSearchParams();
67
72
  const [query, setQuery] = useState("");
68
73
  const [open, setOpen] = useState(false);
69
74
  const inputRef = useRef<HTMLInputElement>(null);
75
+ const routeRequestsFocus = hasSearchFocusRequest(searchParams);
70
76
 
71
77
  const { data, isFetching } = useRecordingSearch(query);
72
78
  const results: SearchHit[] = data?.results ?? [];
73
79
 
74
- // Cmd+K / Ctrl+K and "/" global shortcuts to focus search
80
+ const focusSearchInput = useCallback(() => {
81
+ inputRef.current?.focus();
82
+ inputRef.current?.select();
83
+ setOpen(true);
84
+ }, []);
85
+
86
+ useEffect(() => {
87
+ if (!routeRequestsFocus) return;
88
+ const frame = requestAnimationFrame(() => {
89
+ focusSearchInput();
90
+ setSearchParams(clearSearchFocusRequest, { replace: true });
91
+ });
92
+ return () => cancelAnimationFrame(frame);
93
+ }, [focusSearchInput, routeRequestsFocus, setSearchParams]);
94
+
95
+ // "/" is the inline search shortcut. Cmd+K belongs to the app command menu.
75
96
  useEffect(() => {
76
97
  const onKey = (e: KeyboardEvent) => {
77
- if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
78
- e.preventDefault();
79
- inputRef.current?.focus();
80
- inputRef.current?.select();
81
- setOpen(true);
82
- }
83
98
  if (
84
99
  e.key === "/" &&
85
100
  !e.metaKey &&
@@ -90,9 +105,7 @@ export function SearchBar({ className, side = "right" }: SearchBarProps) {
90
105
  !(e.target as HTMLElement)?.isContentEditable
91
106
  ) {
92
107
  e.preventDefault();
93
- inputRef.current?.focus();
94
- inputRef.current?.select();
95
- setOpen(true);
108
+ focusSearchInput();
96
109
  }
97
110
  if (e.key === "Escape") {
98
111
  setOpen(false);
@@ -101,7 +114,7 @@ export function SearchBar({ className, side = "right" }: SearchBarProps) {
101
114
  };
102
115
  window.addEventListener("keydown", onKey);
103
116
  return () => window.removeEventListener("keydown", onKey);
104
- }, []);
117
+ }, [focusSearchInput]);
105
118
 
106
119
  function pickResult(hit: SearchHit) {
107
120
  setOpen(false);
@@ -4,8 +4,15 @@ import {
4
4
  useActionQuery,
5
5
  } from "@agent-native/core/client/hooks";
6
6
  import { useT } from "@agent-native/core/client/i18n";
7
- import { IconLink, IconMail } from "@tabler/icons-react";
8
- import { useEffect, useMemo, useState, type ReactNode } from "react";
7
+ import { IconChevronDown, IconLink, IconMail } from "@tabler/icons-react";
8
+ import {
9
+ useCallback,
10
+ useEffect,
11
+ useMemo,
12
+ useRef,
13
+ useState,
14
+ type ReactNode,
15
+ } from "react";
9
16
  import { toast } from "sonner";
10
17
 
11
18
  import {
@@ -19,6 +26,12 @@ import {
19
26
  type SharesResponse,
20
27
  type Visibility,
21
28
  } from "@/components/sharing/share-ui";
29
+ import { Button } from "@/components/ui/button";
30
+ import {
31
+ Collapsible,
32
+ CollapsibleContent,
33
+ CollapsibleTrigger,
34
+ } from "@/components/ui/collapsible";
22
35
  import {
23
36
  Popover,
24
37
  PopoverContent,
@@ -157,6 +170,56 @@ function LinkTab({
157
170
  const visibility: Visibility =
158
171
  (data?.visibility as Visibility | null) ?? "private";
159
172
  const isPublic = visibility === "public";
173
+ const sharesLoaded = data?.visibility != null;
174
+ const createAgentLink = useActionMutation("create-agent-resource-link");
175
+ const createAgentLinkAsyncRef = useRef(createAgentLink.mutateAsync);
176
+ const agentLinkRequestIdRef = useRef(0);
177
+ const [agentLink, setAgentLink] = useState("");
178
+ const [agentLinkError, setAgentLinkError] = useState(false);
179
+ const [agentDetailsOpen, setAgentDetailsOpen] = useState(false);
180
+
181
+ useEffect(() => {
182
+ createAgentLinkAsyncRef.current = createAgentLink.mutateAsync;
183
+ });
184
+
185
+ const loadAgentLink = useCallback(async () => {
186
+ const requestId = agentLinkRequestIdRef.current + 1;
187
+ agentLinkRequestIdRef.current = requestId;
188
+ setAgentLink("");
189
+ setAgentLinkError(false);
190
+
191
+ try {
192
+ const result = (await createAgentLinkAsyncRef.current({
193
+ resourceType: "meeting",
194
+ resourceId: meetingId,
195
+ })) as { contextUrl?: string };
196
+ if (agentLinkRequestIdRef.current !== requestId) return;
197
+ if (result?.contextUrl) setAgentLink(result.contextUrl);
198
+ else setAgentLinkError(true);
199
+ } catch {
200
+ if (agentLinkRequestIdRef.current === requestId) setAgentLinkError(true);
201
+ }
202
+ }, [meetingId]);
203
+
204
+ useEffect(() => {
205
+ setAgentLink("");
206
+ setAgentLinkError(false);
207
+ if (!sharesLoaded || isPublic) return;
208
+ void loadAgentLink();
209
+
210
+ return () => {
211
+ agentLinkRequestIdRef.current += 1;
212
+ };
213
+ }, [isPublic, loadAgentLink, meetingId, sharesLoaded, visibility]);
214
+
215
+ useEffect(() => {
216
+ if (isPublic) setAgentDetailsOpen(false);
217
+ }, [isPublic]);
218
+
219
+ const agentShareDisabled =
220
+ sharesQuery.isLoading ||
221
+ !sharesLoaded ||
222
+ (!isPublic && (createAgentLink.isPending || !agentLink));
160
223
 
161
224
  useEffect(() => {
162
225
  setIncludeTranscript(shareTranscript);
@@ -217,7 +280,15 @@ function LinkTab({
217
280
  />
218
281
  </div>
219
282
 
220
- <CopyField label={t("clipsFinalRaw.shareLink")} value={shareUrl} />
283
+ <CopyField
284
+ label={
285
+ isPublic
286
+ ? t("clipsFinalRaw.shareLink")
287
+ : t("shareDialog.shareWithHumans")
288
+ }
289
+ value={shareUrl}
290
+ disabled={!sharesLoaded}
291
+ />
221
292
 
222
293
  {!isPublic && canManage ? (
223
294
  <MakePublicCard
@@ -229,6 +300,67 @@ function LinkTab({
229
300
  }
230
301
  />
231
302
  ) : null}
303
+
304
+ {!isPublic ? (
305
+ <Collapsible
306
+ open={agentDetailsOpen}
307
+ onOpenChange={setAgentDetailsOpen}
308
+ className="overflow-hidden rounded-md border border-border"
309
+ >
310
+ <CollapsibleTrigger asChild>
311
+ <button
312
+ type="button"
313
+ className="flex min-h-10 w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm font-medium transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
314
+ >
315
+ <span className="flex min-w-0 items-center gap-2 truncate">
316
+ <IconLink
317
+ aria-hidden="true"
318
+ className="size-3.5 shrink-0 text-muted-foreground"
319
+ strokeWidth={1.8}
320
+ />
321
+ {t("shareDialog.shareWithAgents")}
322
+ </span>
323
+ <IconChevronDown
324
+ aria-hidden="true"
325
+ className={`size-4 shrink-0 text-muted-foreground transition-transform ${agentDetailsOpen ? "rotate-180" : ""}`}
326
+ />
327
+ </button>
328
+ </CollapsibleTrigger>
329
+ <CollapsibleContent className="clips-collapsible-content border-t border-border px-3 py-3">
330
+ <div className="space-y-2">
331
+ <CopyField
332
+ label={t("shareDialog.shareWithAgents")}
333
+ value={agentLink}
334
+ disabled={agentShareDisabled}
335
+ />
336
+ {sharesLoaded ? (
337
+ <>
338
+ <p className="text-xs text-muted-foreground">
339
+ {t("shareMeeting.agentLinkDescription")}
340
+ </p>
341
+ {agentLinkError ? (
342
+ <div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
343
+ <p className="text-xs text-muted-foreground">
344
+ {t("shareDialog.agentLinkUnavailable")}
345
+ </p>
346
+ <Button
347
+ type="button"
348
+ variant="ghost"
349
+ size="sm"
350
+ className="h-7"
351
+ onClick={() => void loadAgentLink()}
352
+ disabled={createAgentLink.isPending}
353
+ >
354
+ {t("shareDialog.retryAgentLink")}
355
+ </Button>
356
+ </div>
357
+ ) : null}
358
+ </>
359
+ ) : null}
360
+ </div>
361
+ </CollapsibleContent>
362
+ </Collapsible>
363
+ ) : null}
232
364
  </div>
233
365
  );
234
366
  }