@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
@@ -1,15 +1,16 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ShareCopyRow, ShareTrigger } from "@agent-native/toolkit/sharing";
2
3
  import * as Select from "@radix-ui/react-select";
3
- import { IconLock, IconWorld, IconTrash, IconCheck, IconChevronDown, IconLoader2, IconLink, IconSearch, IconSearchOff, IconShare3, IconUsersGroup, } from "@tabler/icons-react";
4
+ import { IconLock, IconWorld, IconTrash, IconCheck, IconChevronDown, IconLoader2, IconSearch, IconSearchOff, IconUsersGroup, } from "@tabler/icons-react";
4
5
  import { useEffect, useId, useRef, useState, } from "react";
5
6
  import { writeClipboardText } from "../clipboard.js";
6
7
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
8
+ import { useT } from "../i18n.js";
7
9
  import { cn } from "../utils.js";
8
10
  import { useShareButtonController, } from "./useShareButtonController.js";
9
11
  // Match shadcn's <Button size="sm" variant="outline"> sizing so the trigger
10
12
  // sits flush next to other controls while staying transparent at rest.
11
13
  const BUTTON_BASE = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
12
- const BUTTON_OUTLINE_SM = cn(BUTTON_BASE, "h-9 px-3 border border-[hsl(var(--sidebar-border,var(--input)))] bg-transparent text-foreground hover:bg-[hsl(var(--sidebar-accent,var(--accent)))] hover:text-[hsl(var(--sidebar-accent-foreground,var(--accent-foreground)))]");
13
14
  const BUTTON_PRIMARY_SM = cn(BUTTON_BASE, "h-9 px-4 bg-primary text-primary-foreground hover:bg-primary/90");
14
15
  const BUTTON_GHOST_ICON = cn(BUTTON_BASE, "h-7 w-7 p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground");
15
16
  const SHARE_POPOVER_SURFACE = "border border-border bg-popover text-popover-foreground";
@@ -17,46 +18,77 @@ const SHARE_NESTED_OVERLAY_ATTR = "data-agent-native-share-overlay";
17
18
  const SHARE_NESTED_OVERLAY_Z = "z-[100020]";
18
19
  const VIS_META = {
19
20
  private: {
20
- label: "Private",
21
- description: "Only people with access can view",
21
+ labelKey: "agentChat.share.private",
22
+ descriptionKey: "agentChat.share.privateDescription",
23
+ defaultLabel: "Private",
24
+ defaultDescription: "Only people with access can view",
22
25
  Icon: IconLock,
23
26
  },
24
27
  org: {
25
- label: "Organization",
26
- description: "Anyone in your organization can view",
28
+ labelKey: "agentChat.share.organization",
29
+ descriptionKey: "agentChat.share.organizationDescription",
30
+ defaultLabel: "Organization",
31
+ defaultDescription: "Anyone in your organization can view",
27
32
  Icon: IconUsersGroup,
28
33
  },
29
34
  public: {
30
- label: "Public",
31
- description: "Anyone with the link can view",
35
+ labelKey: "agentChat.share.public",
36
+ descriptionKey: "agentChat.share.publicDescription",
37
+ defaultLabel: "Public",
38
+ defaultDescription: "Anyone with the link can view",
32
39
  Icon: IconWorld,
33
40
  },
34
41
  };
35
- function visibilityMeta(visibility, copy) {
42
+ function visibilityMeta(visibility, t, copy) {
36
43
  const base = VIS_META[visibility];
37
44
  const override = copy?.[visibility];
38
45
  return {
39
- ...base,
40
- label: override?.label ?? base.label,
41
- description: override?.description ?? base.description,
46
+ Icon: base.Icon,
47
+ label: override?.label ??
48
+ t(base.labelKey, {
49
+ defaultValue: base.defaultLabel,
50
+ }),
51
+ description: override?.description ??
52
+ t(base.descriptionKey, {
53
+ defaultValue: base.defaultDescription,
54
+ }),
42
55
  };
43
56
  }
44
- const ROLE_OPTIONS = [
45
- { value: "viewer", label: "Viewer", description: "Can view" },
46
- {
47
- value: "commenter",
48
- label: "Commenter",
49
- description: "Can view and add comments",
50
- },
51
- { value: "editor", label: "Editor", description: "Can edit" },
52
- {
53
- value: "admin",
54
- label: "Admin",
55
- description: "Can edit and manage access",
56
- },
57
- ];
58
- function roleMeta(role, copy) {
59
- const base = ROLE_OPTIONS.find((option) => option.value === role) ?? ROLE_OPTIONS[0];
57
+ function roleOptions(t) {
58
+ return [
59
+ {
60
+ value: "viewer",
61
+ label: t("agentChat.share.viewer", { defaultValue: "Viewer" }),
62
+ description: t("agentChat.share.viewerDescription", {
63
+ defaultValue: "Can view",
64
+ }),
65
+ },
66
+ {
67
+ value: "commenter",
68
+ label: t("agentChat.share.commenter", { defaultValue: "Commenter" }),
69
+ description: t("agentChat.share.commenterDescription", {
70
+ defaultValue: "Can view and add comments",
71
+ }),
72
+ },
73
+ {
74
+ value: "editor",
75
+ label: t("agentChat.share.editor", { defaultValue: "Editor" }),
76
+ description: t("agentChat.share.editorDescription", {
77
+ defaultValue: "Can edit",
78
+ }),
79
+ },
80
+ {
81
+ value: "admin",
82
+ label: t("agentChat.share.admin", { defaultValue: "Admin" }),
83
+ description: t("agentChat.share.adminDescription", {
84
+ defaultValue: "Can edit and manage access",
85
+ }),
86
+ },
87
+ ];
88
+ }
89
+ function roleMeta(role, t, copy) {
90
+ const options = roleOptions(t);
91
+ const base = options.find((option) => option.value === role) ?? options[0];
60
92
  const override = copy?.[role];
61
93
  return {
62
94
  ...base,
@@ -81,6 +113,7 @@ function handleSharePopoverInteractOutside(event) {
81
113
  * dark mode in any shadcn template.
82
114
  */
83
115
  export function ShareButton(props) {
116
+ const t = useT();
84
117
  const controller = useShareButtonController({
85
118
  resourceType: props.resourceType,
86
119
  resourceId: props.resourceId,
@@ -91,24 +124,22 @@ export function ShareButton(props) {
91
124
  allowedRoles: props.allowedRoles,
92
125
  hideInSearchControl: props.hideInSearchControl,
93
126
  });
94
- const triggerVisibility = controller.triggerVisibility;
95
- const triggerMeta = triggerVisibility
96
- ? visibilityMeta(triggerVisibility, props.visibilityCopy)
97
- : null;
98
- const TriggerIcon = triggerMeta?.Icon ?? IconShare3;
99
- const iconOnly = props.trigger === "icon";
100
- const showTriggerIcon = iconOnly || props.trigger === "label-icon";
101
- const triggerLabel = iconOnly && triggerMeta ? `Share (${triggerMeta.label})` : "Share";
102
- return (_jsxs(Popover, { open: controller.open, onOpenChange: controller.handleOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn(iconOnly ? BUTTON_GHOST_ICON : BUTTON_OUTLINE_SM, props.triggerClassName), "aria-label": triggerLabel, title: triggerLabel, children: [showTriggerIcon && _jsx(TriggerIcon, { size: 16, strokeWidth: 1.75 }), !iconOnly && _jsx("span", { children: "Share" })] }) }), _jsx(PopoverContent, { align: "end", sideOffset: 6, "data-agent-native-share-overlay": "", className: cn("z-[2000] w-[min(460px,92vw)] rounded-lg p-4 shadow-lg", SHARE_POPOVER_SURFACE, props.popoverClassName), onOpenAutoFocus: (e) => e.preventDefault(), onInteractOutside: handleSharePopoverInteractOutside, children: _jsx(SharePanel, { ...props, controller: controller }) })] }));
127
+ const triggerLabel = t("agentChat.share.share", { defaultValue: "Share" });
128
+ return (_jsxs(Popover, { open: controller.open, onOpenChange: controller.handleOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(ShareTrigger, { label: triggerLabel, className: props.triggerClassName, "aria-label": triggerLabel, title: triggerLabel }) }), _jsx(PopoverContent, { align: "end", sideOffset: 6, "data-agent-native-share-overlay": "", className: cn("z-[2000] w-[min(460px,92vw)] rounded-lg p-4 shadow-lg", SHARE_POPOVER_SURFACE, props.popoverClassName), onOpenAutoFocus: (e) => e.preventDefault(), onInteractOutside: handleSharePopoverInteractOutside, children: _jsx(SharePanel, { ...props, controller: controller }) })] }));
103
129
  }
104
130
  function SharePanel(props) {
131
+ const t = useT();
105
132
  const { controller } = props;
106
133
  const { inviteEmail, setInviteEmail: onInviteEmailChange, activeShareTab, handleShareTabChange, data, policy, visibility, canManage, role, setRole, notifyPeople, setNotifyPeople, shareMessage, setShareMessage, messageOpen, setMessageOpen, shareError, setShareError, suggestionsOpen, setSuggestionsOpen, inFlight, memberSearch, memberSuggestions, knownMembers, shares, handleVisibility, handleHideInSearch, handleAdd, handleChangeRole, handleRemove, } = controller;
107
134
  const hasInviteEmail = inviteEmail.trim().length > 0;
108
135
  const isLoading = data === undefined;
109
- const meta = visibilityMeta(visibility, props.visibilityCopy);
110
- const peopleAccessLabel = props.peopleAccessLabel ?? "People with access";
111
- const generalAccessLabel = props.generalAccessLabel ?? "General access";
136
+ const meta = visibilityMeta(visibility, t, props.visibilityCopy);
137
+ const peopleAccessLabel = props.peopleAccessLabel ??
138
+ t("agentChat.share.peopleWithAccess", {
139
+ defaultValue: "People with access",
140
+ });
141
+ const generalAccessLabel = props.generalAccessLabel ??
142
+ t("agentChat.share.generalAccess", { defaultValue: "General access" });
112
143
  const shareLinks = (_jsxs(_Fragment, { children: [props.shareUrl ? (_jsx(CopyLinkField, { value: props.shareUrl, label: props.shareUrlLabel, description: props.shareUrlDescription })) : props.shareUrlPlaceholder ? (_jsxs("div", { className: "mb-4 rounded-md border border-dashed border-border bg-muted/20 px-3 py-2.5 text-xs text-muted-foreground", children: [props.shareUrlLabel ? (_jsx("div", { className: "mb-0.5 font-medium text-foreground", children: props.shareUrlLabel })) : null, props.shareUrlPlaceholder] })) : null, props.secondaryShareUrl ? (_jsx(CopyLinkField, { value: props.secondaryShareUrl, label: props.secondaryShareUrlLabel, description: props.secondaryShareUrlDescription })) : null] }));
113
144
  const showShareLinks = (props.showShareLinks ?? true) &&
114
145
  (Boolean(props.shareUrl) ||
@@ -117,7 +148,8 @@ function SharePanel(props) {
117
148
  const shareUrlPlacement = props.shareUrlPlacement ?? "top";
118
149
  const extraTabs = props.shareTabs?.tabs.filter((tab) => tab.value !== "context") ?? [];
119
150
  const hasTabs = extraTabs.length > 0;
120
- const shareTabLabel = props.shareTabs?.shareLabel ?? "Share link";
151
+ const shareTabLabel = props.shareTabs?.shareLabel ??
152
+ t("agentChat.share.shareLink", { defaultValue: "Share link" });
121
153
  const sharePanel = isLoading ? (_jsxs("div", { children: [showShareLinks ? (_jsx("div", { className: "mb-4 h-9 rounded-md bg-muted animate-pulse" })) : null, _jsx("div", { className: "mb-2 text-sm font-semibold", children: generalAccessLabel }), _jsx("div", { className: "mb-4 h-9 rounded-md bg-muted animate-pulse" }), _jsx("div", { className: "mb-2 text-sm font-semibold", children: peopleAccessLabel }), _jsx("div", { className: "mb-4 h-7 rounded-md bg-muted animate-pulse" })] })) : (_jsxs("div", { children: [showShareLinks && shareUrlPlacement === "top" ? shareLinks : null, _jsxs("div", { className: "mb-4", children: [_jsx("div", { className: "mb-2 text-sm font-semibold", children: generalAccessLabel }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { "aria-hidden": true, className: "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(meta.Icon, { size: 16, strokeWidth: 1.75 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(VisibilitySelect, { value: visibility, onChange: handleVisibility, disabled: !canManage, visibilityCopy: props.visibilityCopy, allowPublic: policy.allowPublic }), _jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground", children: [_jsx("span", { className: "sr-only", children: meta.description }), visibility === "org" && props.hideInSearchControl ? (_jsx(AdvancedAccessPopover, { control: props.hideInSearchControl, canManage: canManage, onToggle: handleHideInSearch })) : null] })] })] })] }), _jsxs("div", { className: "mb-4 space-y-4", children: [_jsx("div", { className: "text-sm font-semibold", children: peopleAccessLabel }), canManage ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-stretch gap-2", children: [_jsx(MemberAutocomplete, { value: inviteEmail, open: suggestionsOpen, onOpenChange: setSuggestionsOpen, onValueChange: (next) => {
122
154
  onInviteEmailChange(next);
123
155
  if (shareError)
@@ -128,8 +160,28 @@ function SharePanel(props) {
128
160
  if (shareError)
129
161
  setShareError(null);
130
162
  }, onSubmit: handleAdd, placeholder: policy.requireOrgMemberForUserShares
131
- ? "Add people from your organization"
132
- : "Add people by email", suggestions: memberSuggestions, search: memberSearch }), _jsx(RoleSelect, { value: role, onChange: setRole, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !hasInviteEmail, className: BUTTON_PRIMARY_SM, children: "Add" })] }), shareError ? (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, hasInviteEmail ? (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs text-muted-foreground", children: [_jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: notifyPeople, onChange: (e) => setNotifyPeople(e.target.checked), className: "h-4 w-4 rounded border-input accent-primary" }), "Notify people"] }), notifyPeople ? (_jsx("button", { type: "button", "aria-expanded": messageOpen, onClick: () => setMessageOpen(!messageOpen), className: "rounded-sm px-1 py-0.5 font-medium text-foreground underline decoration-border underline-offset-2 transition-colors hover:bg-accent hover:text-accent-foreground", children: messageOpen ? "Hide message" : "Add a message" })) : null] })) : null, hasInviteEmail && notifyPeople && messageOpen ? (_jsx("div", { className: "rounded-md border border-border/70 bg-muted/20 p-2.5", children: _jsx("textarea", { "aria-label": "Message", placeholder: "Add a short note (optional)", value: shareMessage, onChange: (event) => setShareMessage(event.target.value), maxLength: 500, rows: 3, className: "w-full resize-y rounded-md border border-input bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }) })) : null] })) : null, _jsxs("ul", { className: "flex list-none flex-col gap-1 p-0 m-0", children: [data?.ownerEmail ? (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(Avatar, { label: displayName(data.ownerEmail, knownMembers) }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: displayName(data.ownerEmail, knownMembers) }), _jsx("span", { className: "text-xs text-muted-foreground", children: "Owner" })] })) : null, shares.map((s) => (_jsxs("li", { className: cn("flex items-center gap-3 px-1 py-1.5 text-sm", inFlight.has(keyOf(s)) && "opacity-60"), children: [_jsx(Avatar, { label: principalLabel(s, knownMembers), org: s.principalType === "org" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: principalLabel(s, knownMembers) }), canManage ? (_jsx(RoleSelect, { value: s.role, onChange: (r) => handleChangeRole(s, r), disabled: inFlight.has(keyOf(s)), plain: true, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: roleMeta(s.role, props.roleCopy).label })), canManage ? (_jsx("button", { type: "button", "aria-label": "Remove", onClick: () => handleRemove(s), disabled: inFlight.has(keyOf(s)), className: BUTTON_GHOST_ICON, children: _jsx(IconTrash, { size: 14 }) })) : null] }, keyOf(s)))), !shares.length && !data?.ownerEmail ? (_jsx("li", { className: "px-1 py-1.5 text-sm text-muted-foreground", children: "No one has access yet." })) : null] })] }), shareError && !canManage ? (_jsx("div", { role: "alert", className: "mb-4 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, props.accessNote ? (_jsx("div", { className: "mb-4 rounded-md border border-border bg-muted/35 p-3 text-xs text-muted-foreground", children: props.accessNote })) : null, showShareLinks && shareUrlPlacement === "bottom" ? shareLinks : null, props.shareFooterContent] }));
163
+ ? t("agentChat.share.addPeopleOrganization", {
164
+ defaultValue: "Add people from your organization",
165
+ })
166
+ : t("agentChat.share.addPeopleEmail", {
167
+ defaultValue: "Add people by email",
168
+ }), suggestions: memberSuggestions, search: memberSearch }), _jsx(RoleSelect, { value: role, onChange: setRole, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !hasInviteEmail, className: BUTTON_PRIMARY_SM, children: t("agentChat.share.add", { defaultValue: "Add" }) })] }), shareError ? (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, hasInviteEmail ? (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs text-muted-foreground", children: [_jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: notifyPeople, onChange: (e) => setNotifyPeople(e.target.checked), className: "h-4 w-4 rounded border-input accent-primary" }), t("agentChat.share.notifyPeople", {
169
+ defaultValue: "Notify people",
170
+ })] }), notifyPeople ? (_jsx("button", { type: "button", "aria-expanded": messageOpen, onClick: () => setMessageOpen(!messageOpen), className: "rounded-sm px-1 py-0.5 font-medium text-foreground underline decoration-border underline-offset-2 transition-colors hover:bg-accent hover:text-accent-foreground", children: messageOpen
171
+ ? t("agentChat.share.hideMessage", {
172
+ defaultValue: "Hide message",
173
+ })
174
+ : t("agentChat.share.addMessage", {
175
+ defaultValue: "Add a message",
176
+ }) })) : null] })) : null, hasInviteEmail && notifyPeople && messageOpen ? (_jsx("div", { className: "rounded-md border border-border/70 bg-muted/20 p-2.5", children: _jsx("textarea", { "aria-label": t("agentChat.share.message", {
177
+ defaultValue: "Message",
178
+ }), placeholder: t("agentChat.share.messagePlaceholder", {
179
+ defaultValue: "Add a short note (optional)",
180
+ }), value: shareMessage, onChange: (event) => setShareMessage(event.target.value), maxLength: 500, rows: 3, className: "w-full resize-y rounded-md border border-input bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }) })) : null] })) : null, _jsxs("ul", { className: "flex list-none flex-col gap-1 p-0 m-0", children: [data?.ownerEmail ? (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(Avatar, { label: displayName(data.ownerEmail, knownMembers, t) }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: displayName(data.ownerEmail, knownMembers, t) }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("agentChat.share.owner", { defaultValue: "Owner" }) })] })) : null, shares.map((s) => (_jsxs("li", { className: cn("flex items-center gap-3 px-1 py-1.5 text-sm", inFlight.has(keyOf(s)) && "opacity-60"), children: [_jsx(Avatar, { label: principalLabel(s, knownMembers, t), org: s.principalType === "org" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: principalLabel(s, knownMembers, t) }), canManage ? (_jsx(RoleSelect, { value: s.role, onChange: (r) => handleChangeRole(s, r), disabled: inFlight.has(keyOf(s)), plain: true, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: roleMeta(s.role, t, props.roleCopy).label })), canManage ? (_jsx("button", { type: "button", "aria-label": t("agentChat.share.remove", {
181
+ defaultValue: "Remove",
182
+ }), onClick: () => handleRemove(s), disabled: inFlight.has(keyOf(s)), className: BUTTON_GHOST_ICON, children: _jsx(IconTrash, { size: 14 }) })) : null] }, keyOf(s)))), !shares.length && !data?.ownerEmail ? (_jsx("li", { className: "px-1 py-1.5 text-sm text-muted-foreground", children: t("agentChat.share.noAccess", {
183
+ defaultValue: "No one has access yet.",
184
+ }) })) : null] })] }), shareError && !canManage ? (_jsx("div", { role: "alert", className: "mb-4 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, props.accessNote ? (_jsx("div", { className: "mb-4 rounded-md border border-border bg-muted/35 p-3 text-xs text-muted-foreground", children: props.accessNote })) : null, showShareLinks && shareUrlPlacement === "bottom" ? shareLinks : null, props.shareFooterContent] }));
133
185
  if (!hasTabs)
134
186
  return sharePanel;
135
187
  const tabs = [
@@ -144,17 +196,30 @@ function SharePanel(props) {
144
196
  const activeTab = tabs.some((tab) => tab.value === activeShareTab)
145
197
  ? activeShareTab
146
198
  : "share";
147
- return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("div", { role: "tablist", "aria-label": "Share options", className: "flex gap-1 rounded-xl bg-muted/70 p-1", children: tabs.map((tab) => {
199
+ return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("div", { role: "tablist", "aria-label": t("agentChat.share.shareOptions", {
200
+ defaultValue: "Share options",
201
+ }), className: "flex gap-1 rounded-xl bg-muted/70 p-1", children: tabs.map((tab) => {
148
202
  const active = tab.value === activeTab;
149
203
  return (_jsx("button", { type: "button", role: "tab", "aria-selected": active, disabled: tab.disabled, onClick: () => handleShareTabChange(tab.value), className: cn("h-11 min-w-0 flex-1 rounded-lg px-3 text-sm font-medium text-muted-foreground transition-colors hover:bg-background/70 hover:text-foreground disabled:pointer-events-none disabled:opacity-50", active &&
150
204
  "bg-background text-foreground shadow-sm ring-2 ring-primary"), children: _jsx("span", { className: "block truncate", children: tab.label }) }, tab.value));
151
205
  }) }), _jsx("div", { role: "tabpanel", children: tabs.find((tab) => tab.value === activeTab)?.content })] }));
152
206
  }
153
207
  function AdvancedAccessPopover({ control, canManage, onToggle, }) {
154
- return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: !canManage, className: "inline-flex items-center gap-1 rounded-sm px-1 py-0.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50", children: ["Advanced", _jsx(IconChevronDown, { size: 12, strokeWidth: 1.8 })] }) }), _jsx(PopoverContent, { align: "start", sideOffset: 6, "data-agent-native-share-overlay": "", onOpenAutoFocus: (event) => event.preventDefault(), className: cn(SHARE_NESTED_OVERLAY_Z, "w-72 p-3 shadow-lg", SHARE_POPOVER_SURFACE), children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: "Advanced access" }), _jsx("div", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: "Control how organization access appears in search." })] }), _jsxs("button", { type: "button", role: "switch", "aria-checked": control.checked, disabled: !canManage || control.pending, onClick: onToggle, className: cn("flex w-full items-start gap-3 rounded-md border border-border/70 bg-card px-3 py-2.5 text-start transition-colors hover:bg-accent/45 disabled:cursor-not-allowed disabled:opacity-60", control.checked && "border-border bg-accent/35 text-foreground"), children: [_jsx("span", { "aria-hidden": true, className: cn("relative mt-0.5 inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border bg-muted-foreground/25 transition-colors", control.checked && "border-primary/70 bg-primary"), children: _jsx("span", { className: cn("ml-0.5 size-4 rounded-full bg-popover shadow-sm transition-transform", control.checked && "translate-x-4") }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-sm font-medium text-foreground", children: [_jsx(IconSearchOff, { size: 14, strokeWidth: 1.8 }), control.label ?? "Hide in search"] }), _jsx("span", { className: "mt-0.5 block text-xs leading-5 text-muted-foreground", children: control.description ??
155
- "People with the link can still open this." })] })] })] }) })] }));
208
+ const t = useT();
209
+ return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: !canManage, className: "inline-flex items-center gap-1 rounded-sm px-1 py-0.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50", children: [t("agentChat.share.advanced", { defaultValue: "Advanced" }), _jsx(IconChevronDown, { size: 12, strokeWidth: 1.8 })] }) }), _jsx(PopoverContent, { align: "start", sideOffset: 6, "data-agent-native-share-overlay": "", onOpenAutoFocus: (event) => event.preventDefault(), className: cn(SHARE_NESTED_OVERLAY_Z, "w-72 p-3 shadow-lg", SHARE_POPOVER_SURFACE), children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: t("agentChat.share.advancedAccess", {
210
+ defaultValue: "Advanced access",
211
+ }) }), _jsx("div", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: t("agentChat.share.advancedDescription", {
212
+ defaultValue: "Control how organization access appears in search.",
213
+ }) })] }), _jsxs("button", { type: "button", role: "switch", "aria-checked": control.checked, disabled: !canManage || control.pending, onClick: onToggle, className: cn("flex w-full items-start gap-3 rounded-md border border-border/70 bg-card px-3 py-2.5 text-start transition-colors hover:bg-accent/45 disabled:cursor-not-allowed disabled:opacity-60", control.checked && "border-border bg-accent/35 text-foreground"), children: [_jsx("span", { "aria-hidden": true, className: cn("relative mt-0.5 inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border bg-muted-foreground/25 transition-colors", control.checked && "border-primary/70 bg-primary"), children: _jsx("span", { className: cn("ml-0.5 size-4 rounded-full bg-popover shadow-sm transition-transform", control.checked && "translate-x-4") }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-sm font-medium text-foreground", children: [_jsx(IconSearchOff, { size: 14, strokeWidth: 1.8 }), control.label ??
214
+ t("agentChat.share.hideInSearch", {
215
+ defaultValue: "Hide in search",
216
+ })] }), _jsx("span", { className: "mt-0.5 block text-xs leading-5 text-muted-foreground", children: control.description ??
217
+ t("agentChat.share.linkCanStillOpen", {
218
+ defaultValue: "People with the link can still open this.",
219
+ }) })] })] })] }) })] }));
156
220
  }
157
221
  function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelectMember, onSubmit, placeholder, suggestions, search, }) {
222
+ const t = useT();
158
223
  const rawListboxId = useId();
159
224
  const listboxId = rawListboxId.replace(/:/g, "");
160
225
  const inputRef = useRef(null);
@@ -247,32 +312,24 @@ function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelect
247
312
  return (_jsxs("div", { id: optionId(listboxId, index), role: "option", "aria-selected": active, onMouseDown: (event) => event.preventDefault(), onMouseEnter: () => setActiveIndex(index), onClick: () => chooseMember(member), className: cn("flex cursor-pointer select-none flex-col rounded-sm px-3 py-2 text-sm outline-none", active
248
313
  ? "bg-accent text-accent-foreground"
249
314
  : "text-foreground hover:bg-accent hover:text-accent-foreground"), children: [_jsx("span", { className: "truncate font-medium", children: member.name?.trim() || member.email }), member.name?.trim() ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: member.email })) : null] }, member.email));
250
- }), search.isLoading && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: "Searching..." })) : null, search.error ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: "Could not load people." })) : null, !search.isLoading && !search.error && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: value.trim() ? "No matches." : "No people found." })) : null, search.isLoadingMore ? (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground", children: [_jsx(IconLoader2, { "aria-hidden": true, size: 14, strokeWidth: 1.8, className: "animate-spin" }), "Loading..."] })) : null, search.hasMore && !search.isLoadingMore ? (_jsx("button", { type: "button", onMouseDown: (event) => event.preventDefault(), onClick: search.loadMore, className: "mt-1 flex w-full items-center justify-center rounded-sm px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-accent-foreground", children: "Load more" })) : null] }) })] }));
315
+ }), search.isLoading && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: t("agentChat.share.searching", {
316
+ defaultValue: "Searching...",
317
+ }) })) : null, search.error ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: t("agentChat.share.loadPeopleFailed", {
318
+ defaultValue: "Could not load people.",
319
+ }) })) : null, !search.isLoading && !search.error && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: value.trim()
320
+ ? t("agentChat.share.noMatches", {
321
+ defaultValue: "No matches.",
322
+ })
323
+ : t("agentChat.share.noPeopleFound", {
324
+ defaultValue: "No people found.",
325
+ }) })) : null, search.isLoadingMore ? (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground", children: [_jsx(IconLoader2, { "aria-hidden": true, size: 14, strokeWidth: 1.8, className: "animate-spin" }), t("agentChat.share.loading", { defaultValue: "Loading..." })] })) : null, search.hasMore && !search.isLoadingMore ? (_jsx("button", { type: "button", onMouseDown: (event) => event.preventDefault(), onClick: search.loadMore, className: "mt-1 flex w-full items-center justify-center rounded-sm px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-accent-foreground", children: t("agentChat.share.loadMore", { defaultValue: "Load more" }) })) : null] }) })] }));
251
326
  }
252
327
  function optionId(baseId, index) {
253
328
  return `${baseId}-option-${index}`;
254
329
  }
255
- function CopyLinkField({ value, label = "Share link", description, }) {
256
- const [copied, setCopied] = useState(false);
257
- const resetRef = useRef(undefined);
258
- useEffect(() => {
259
- return () => {
260
- if (resetRef.current)
261
- clearTimeout(resetRef.current);
262
- };
263
- }, []);
264
- const handleCopy = async () => {
265
- if (await writeClipboardText(value)) {
266
- setCopied(true);
267
- if (resetRef.current)
268
- clearTimeout(resetRef.current);
269
- resetRef.current = setTimeout(() => setCopied(false), 1400);
270
- }
271
- else {
272
- setCopied(false);
273
- }
274
- };
275
- return (_jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-sm font-semibold", children: label }), description ? (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: description })) : null] }), _jsxs("button", { type: "button", onClick: handleCopy, "aria-label": copied ? "Link copied" : "Copy", className: "inline-flex h-9 shrink-0 items-center gap-2 whitespace-nowrap rounded-md border border-input bg-card px-3 text-sm font-medium text-foreground hover:bg-accent", children: [copied ? _jsx(IconCheck, { size: 15 }) : _jsx(IconLink, { size: 15 }), copied ? "Copied" : "Copy"] })] }));
330
+ function CopyLinkField({ value, label, description, }) {
331
+ const t = useT();
332
+ return (_jsx(ShareCopyRow, { value: value, label: label ?? t("agentChat.share.shareLink", { defaultValue: "Share link" }), description: description, copyLabel: t("agentChat.share.copy", { defaultValue: "Copy" }), copiedLabel: t("agentChat.share.copied", { defaultValue: "Copied" }), onCopy: writeClipboardText, className: "mb-4" }));
276
333
  }
277
334
  // ---------------------------------------------------------------------------
278
335
  // Radix Select wrappers styled like shadcn Select (no native <select> anywhere)
@@ -283,17 +340,22 @@ function SelectItems({ items }) {
283
340
  return (_jsx(_Fragment, { children: items.map((it) => (_jsxs(Select.Item, { value: it.value, className: selectItemClass, children: [_jsx("span", { className: "absolute start-2 top-2 flex h-4 w-4 items-center justify-center", children: _jsx(Select.ItemIndicator, { children: _jsx(IconCheck, { size: 14 }) }) }), _jsxs("span", { className: "flex flex-col", children: [_jsx(Select.ItemText, { children: it.label }), it.description ? (_jsx("span", { className: "text-xs text-muted-foreground", children: it.description })) : null] })] }, it.value))) }));
284
341
  }
285
342
  function RoleSelect(props) {
286
- const current = roleMeta(props.value, props.roleCopy);
287
- const allowedRoles = props.allowedRoles ?? ROLE_OPTIONS.map((item) => item.value);
288
- const options = ROLE_OPTIONS.filter((option) => option.value === props.value || allowedRoles.includes(option.value)).map((option) => roleMeta(option.value, props.roleCopy));
343
+ const t = useT();
344
+ const allOptions = roleOptions(t);
345
+ const current = roleMeta(props.value, t, props.roleCopy);
346
+ const allowedRoles = props.allowedRoles ?? allOptions.map((item) => item.value);
347
+ const options = allOptions
348
+ .filter((option) => option.value === props.value || allowedRoles.includes(option.value))
349
+ .map((option) => roleMeta(option.value, t, props.roleCopy));
289
350
  return (_jsxs(Select.Root, { value: props.value, onValueChange: (v) => props.onChange(v), disabled: props.disabled, children: [_jsxs(Select.Trigger, { className: props.plain
290
351
  ? cn(BUTTON_BASE, "h-7 px-2 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground")
291
- : cn(BUTTON_BASE, "h-9 px-3 border border-input bg-card hover:bg-accent hover:text-accent-foreground"), "aria-label": "Role", children: [_jsx(Select.Value, { children: current.label }), _jsx(Select.Icon, { children: _jsx(IconChevronDown, { size: 14 }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { "data-agent-native-share-overlay": "", className: selectContentClass, position: "popper", sideOffset: 4, children: _jsx(Select.Viewport, { children: _jsx(SelectItems, { items: options }) }) }) })] }));
352
+ : cn(BUTTON_BASE, "h-9 px-3 border border-input bg-card hover:bg-accent hover:text-accent-foreground"), "aria-label": t("agentChat.share.role", { defaultValue: "Role" }), children: [_jsx(Select.Value, { children: current.label }), _jsx(Select.Icon, { children: _jsx(IconChevronDown, { size: 14 }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { "data-agent-native-share-overlay": "", className: selectContentClass, position: "popper", sideOffset: 4, children: _jsx(Select.Viewport, { children: _jsx(SelectItems, { items: options }) }) }) })] }));
292
353
  }
293
354
  function VisibilitySelect(props) {
355
+ const t = useT();
294
356
  const allowPrivate = props.allowPrivate !== false;
295
357
  const allowPublic = props.allowPublic !== false;
296
- const current = visibilityMeta(props.value, props.visibilityCopy);
358
+ const current = visibilityMeta(props.value, t, props.visibilityCopy);
297
359
  const options = Object.keys(VIS_META).filter((k) => {
298
360
  if (k === props.value)
299
361
  return true;
@@ -303,10 +365,12 @@ function VisibilitySelect(props) {
303
365
  return false;
304
366
  return true;
305
367
  });
306
- return (_jsxs(Select.Root, { value: props.value, onValueChange: (v) => props.onChange(v), disabled: props.disabled, children: [_jsxs(Select.Trigger, { className: cn(BUTTON_BASE, "h-7 px-1 -ms-1 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground"), "aria-label": "General access", children: [_jsx(Select.Value, { children: current.label }), _jsx(Select.Icon, { children: _jsx(IconChevronDown, { size: 14 }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { "data-agent-native-share-overlay": "", className: selectContentClass, position: "popper", sideOffset: 4, children: _jsx(Select.Viewport, { children: _jsx(SelectItems, { items: options.map((k) => ({
368
+ return (_jsxs(Select.Root, { value: props.value, onValueChange: (v) => props.onChange(v), disabled: props.disabled, children: [_jsxs(Select.Trigger, { className: cn(BUTTON_BASE, "h-7 px-1 -ms-1 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground"), "aria-label": t("agentChat.share.generalAccess", {
369
+ defaultValue: "General access",
370
+ }), children: [_jsx(Select.Value, { children: current.label }), _jsx(Select.Icon, { children: _jsx(IconChevronDown, { size: 14 }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { "data-agent-native-share-overlay": "", className: selectContentClass, position: "popper", sideOffset: 4, children: _jsx(Select.Viewport, { children: _jsx(SelectItems, { items: options.map((k) => ({
307
371
  value: k,
308
- label: visibilityMeta(k, props.visibilityCopy).label,
309
- description: visibilityMeta(k, props.visibilityCopy)
372
+ label: visibilityMeta(k, t, props.visibilityCopy).label,
373
+ description: visibilityMeta(k, t, props.visibilityCopy)
310
374
  .description,
311
375
  })) }) }) }) })] }));
312
376
  }
@@ -320,18 +384,24 @@ function initials(s) {
320
384
  const name = s.split("@")[0] ?? s;
321
385
  return (name[0] ?? "?").toUpperCase();
322
386
  }
323
- function principalLabel(share, members) {
387
+ function principalLabel(share, members, t) {
324
388
  const serverLabel = share.displayName?.trim();
325
389
  if (serverLabel)
326
390
  return serverLabel;
327
391
  if (share.principalType === "org")
328
- return "Organization";
329
- return displayName(share.principalId, members);
392
+ return t("agentChat.share.organization", {
393
+ defaultValue: "Organization",
394
+ });
395
+ return displayName(share.principalId, members, t);
330
396
  }
331
- function displayName(emailOrId, members) {
397
+ function displayName(emailOrId, members, t) {
332
398
  const normalized = emailOrId.trim().toLowerCase();
333
399
  const match = members.find((m) => m.email.toLowerCase() === normalized);
334
400
  if (match?.name && match.name.trim())
335
401
  return match.name;
336
- return normalized.includes("@") ? emailOrId : "Unknown person";
402
+ return normalized.includes("@")
403
+ ? emailOrId
404
+ : t("agentChat.share.unknownPerson", {
405
+ defaultValue: "Unknown person",
406
+ });
337
407
  }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ActionButton, Avatar as DesignSystemAvatar, Dialog as DesignSystemDialog, IconButton, Picker, Status, TextArea, TextField, } from "@agent-native/toolkit/design-system";
2
+ import { ActionButton, Avatar as DesignSystemAvatar, Dialog as DesignSystemDialog, IconButton, Picker, Status, TextArea, } from "@agent-native/toolkit/design-system";
3
+ import { ShareCopyRow } from "@agent-native/toolkit/sharing";
3
4
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@agent-native/toolkit/ui";
4
5
  import { IconCheck, IconCode, IconCopy, IconChevronDown, IconLink, IconLock, IconMail, IconTrash, IconUsersGroup, IconWorld, } from "@tabler/icons-react";
5
6
  import { useState } from "react";
@@ -53,7 +54,7 @@ function TabTrigger({ active, onClick, icon, label, }) {
53
54
  }
54
55
  function LinkTab({ controller, extras, }) {
55
56
  const Icon = VIS_ICONS[controller.visibility.value];
56
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(CopyField, { field: "share-link", label: controller.labels.shareLink, value: controller.shareUrl, controller: controller }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: controller.labels.generalAccess }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { "aria-hidden": true, className: "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(Icon, { size: 16, strokeWidth: 1.75 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(VisibilitySelect, { controller: controller }), _jsx("div", { className: "sr-only text-xs text-muted-foreground", children: controller.visibility.description })] })] })] }), extras] }));
57
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(ShareCopyRow, { label: controller.labels.shareLink, value: controller.shareUrl, copyLabel: controller.labels.copy, copiedLabel: "Copied", onCopy: (value) => controller.copy("share-link", value), className: "mb-4" }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: controller.labels.generalAccess }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { "aria-hidden": true, className: "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(Icon, { size: 16, strokeWidth: 1.75 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(VisibilitySelect, { controller: controller }), _jsx("div", { className: "sr-only text-xs text-muted-foreground", children: controller.visibility.description })] })] })] }), extras] }));
57
58
  }
58
59
  function InviteTab({ controller, showVisibility, }) {
59
60
  const Icon = VIS_ICONS[controller.visibility.value];
@@ -71,11 +72,11 @@ function PersonRow({ person, canManage, removeLabel, onRemove, }) {
71
72
  return (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(DesignSystemAvatar, { name: person.label, fallback: person.principalType === "org" ? (_jsx(IconUsersGroup, { size: 14, strokeWidth: 1.75 })) : (person.avatarText), size: "compact", className: "inline-flex h-7 w-7 shrink-0 text-[11px] font-semibold" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: person.label }), _jsx(Status, { tone: "neutral", size: "compact", className: "border-0 bg-transparent px-0 text-xs text-muted-foreground", children: person.roleLabel }), canManage && person.share ? (_jsx(IconButton, { intent: "danger", emphasis: "ghost", size: "compact", icon: _jsx(IconTrash, { size: 14 }), label: removeLabel, "aria-label": removeLabel, onPress: () => onRemove(person.share), className: cn(BUTTON_GHOST_ICON, "[&_svg]:!size-auto") })) : null] }));
72
73
  }
73
74
  function DefaultEmbedBody({ controller, }) {
74
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(CopyField, { field: "embed-url", label: controller.labels.embedUrl, value: controller.embedUrl, controller: controller }), _jsx(CopyField, { field: "embed-code", label: controller.labels.embedCode, value: controller.embedCode, controller: controller, multiline: true })] }));
75
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(ShareCopyRow, { label: controller.labels.embedUrl, value: controller.embedUrl, copyLabel: controller.labels.copy, copiedLabel: "Copied", onCopy: (value) => controller.copy("embed-url", value) }), _jsx(CopyField, { field: "embed-code", label: controller.labels.embedCode, value: controller.embedCode, controller: controller, multiline: true })] }));
75
76
  }
76
77
  function CopyField({ field, label, value, controller, multiline, }) {
77
78
  const copied = controller.copiedField === field;
78
- return (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }), _jsxs("div", { className: "flex items-stretch gap-2", children: [multiline ? (_jsx(TextArea, { readOnly: true, value: value, onChange: () => undefined, "aria-label": label, className: "flex-1 h-20 text-xs font-mono" })) : (_jsx(TextField, { readOnly: true, value: value, onChange: () => undefined, "aria-label": label, className: "flex-1 min-w-0 text-xs font-mono" })), _jsx(IconButton, { emphasis: "outline", size: "compact", icon: copied ? _jsx(IconCheck, { size: 14 }) : _jsx(IconCopy, { size: 14 }), label: controller.labels.copy, onPress: () => void controller.copy(field, value), "aria-label": controller.labels.copy, className: cn(BUTTON_OUTLINE_SM, "!w-9 !px-0 [&_svg]:!size-auto") })] })] }));
79
+ return (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }), _jsxs("div", { className: "flex items-stretch gap-2", children: [multiline ? (_jsx(TextArea, { readOnly: true, value: value, onChange: () => undefined, "aria-label": label, className: "flex-1 h-20 text-xs font-mono" })) : null, _jsx(IconButton, { emphasis: "outline", size: "compact", icon: copied ? _jsx(IconCheck, { size: 14 }) : _jsx(IconCopy, { size: 14 }), label: controller.labels.copy, onPress: () => void controller.copy(field, value), "aria-label": controller.labels.copy, className: cn(BUTTON_OUTLINE_SM, "!w-9 !px-0 [&_svg]:!size-auto") })] })] }));
79
80
  }
80
81
  function RoleSelect({ controller }) {
81
82
  return (_jsx(Picker, { mode: "select", options: controller.invite.roleOptions, value: controller.invite.role, onChange: (value) => {
@@ -466,17 +466,11 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
466
466
  // - savedId in newlyCreatedRef (we just created it this session) → keep
467
467
  // it; the server hasn't seen it yet because there's no POST anymore,
468
468
  // the row gets written when the user sends a message.
469
- // - savedId is set but neither on the server nor newly created here →
470
- // it's an empty tab the user left open. A never-messaged tab is never
471
- // POSTed (that was the 127-ghost-threads problem), and the only record
472
- // that it's a deliberately-open tab — newlyCreatedRef — is wiped by the
473
- // reload. So on refresh we can't tell it apart from a stale ghost.
474
- // Keep it exactly as the user left it: re-register it as an optimistic
475
- // empty tab rather than resurrecting an unrelated old conversation. The
476
- // composer is fully functional with this id (the server writes the row
477
- // on first message, same as any new tab), so there's no 404 to avoid.
478
- // This is what makes "the state you left is the state you see on
479
- // refresh" hold — stale (>12h) tabs are still cleared downstream.
469
+ // - savedId is set but not on the current page → look it up directly. A
470
+ // found thread stays active. An auto-creating surface replaces a confirmed
471
+ // 404 with a fresh local tab rather than reusing an inaccessible server id.
472
+ // A list-only surface keeps the saved id as an optimistic empty tab so a
473
+ // never-posted tab remains selectable after reload.
480
474
  // - No savedId → synthesize a fresh local id (no POST; server creates the
481
475
  // row on first message). The server may contain chats from another
482
476
  // branch, preview, or project that shares the same user/database, so
@@ -505,7 +499,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
505
499
  ? loadedThreads.find((t) => t.id === restoredId)
506
500
  : undefined;
507
501
  // One page, so absence from it is not absence from the server — this is what
508
- // separates an older real thread from the ghost tab reclassified below.
502
+ // separates an older real thread from an unavailable saved id below.
509
503
  const restoredThread = lookupRestored && !restoredOnPage
510
504
  ? await fetchThreadById(apiUrl, restoredId)
511
505
  : restoredOnPage;
@@ -515,11 +509,13 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
515
509
  setIsLoading(false);
516
510
  return;
517
511
  }
512
+ const restoredIsUnavailable = restoredThread === null && lookupRestored && !restoredOnPage;
518
513
  const restoredBelongsElsewhere = Boolean(restoredThread &&
519
514
  !threadCanStayVisibleInScope(restoredThread.scope ?? null, scopeRef.current));
520
- if (restoredBelongsElsewhere)
515
+ const restoredNeedsReplacement = restoredBelongsElsewhere || (restoredIsUnavailable && autoCreate);
516
+ if (restoredNeedsReplacement)
521
517
  setActiveThreadId(null);
522
- const savedId = restoredBelongsElsewhere ? null : restoredId;
518
+ const savedId = restoredNeedsReplacement ? null : restoredId;
523
519
  const loadedHasSavedId = Boolean(savedId &&
524
520
  (restoredThread || loadedThreads.some((t) => t.id === savedId)));
525
521
  const savedIdCameFromRoute = Boolean(savedId) &&
@@ -30,7 +30,7 @@
30
30
  */
31
31
  import crypto from "node:crypto";
32
32
  import http from "node:http";
33
- import { getRequestUserEmail } from "../server/request-context.js";
33
+ import { getRequestRunContext, getRequestUserEmail, } from "../server/request-context.js";
34
34
  import { failExpiredSandboxExecution, getSandboxExecutionForOwner, } from "./sandbox/executions-store.js";
35
35
  import { BACKGROUND_DEFAULT_TIMEOUT_MS, BACKGROUND_MAX_TIMEOUT_MS, SANDBOX_EXECUTION_REDRIVE_AFTER_MS, driveSandboxExecution, enqueueSandboxExecution, getSandboxAdapter, isQueuedSandboxAdapter, registerSandboxExecutionRunner, resolveExecutionSandboxAdapter, } from "./sandbox/index.js";
36
36
  const DEFAULT_TIMEOUT_MS = 120_000;
@@ -289,6 +289,7 @@ async function enqueueBackgroundRunCode(input) {
289
289
  owner,
290
290
  orgId: input.context?.orgId ?? null,
291
291
  threadId: input.context?.threadId ?? null,
292
+ allowedActionNames: getRequestRunContext()?.allowedActionNames,
292
293
  });
293
294
  const hasGetTool = Boolean(input.getActions()["get-code-execution"]);
294
295
  return JSON.stringify({
@@ -96,6 +96,7 @@ export interface EnqueueSandboxExecutionInput {
96
96
  owner: string;
97
97
  orgId?: string | null;
98
98
  threadId?: string | null;
99
+ allowedActionNames?: readonly string[];
99
100
  }
100
101
  export interface EnqueueSandboxExecutionResult {
101
102
  execution: SandboxExecutionRow;
@@ -106,6 +106,7 @@ export async function enqueueSandboxExecution(input) {
106
106
  code: input.code,
107
107
  timeoutMs: input.timeoutMs,
108
108
  maxOutputChars: input.maxOutputChars,
109
+ allowedActionNames: input.allowedActionNames,
109
110
  });
110
111
  let driveNote;
111
112
  try {
@@ -201,7 +202,13 @@ export async function processQueuedSandboxExecution(executionId) {
201
202
  threadId: claimed.threadId ?? undefined,
202
203
  actionName: "run-code",
203
204
  };
204
- const output = await runWithRequestContext({ userEmail: claimed.owner, orgId: claimed.orgId ?? undefined }, () => runner.execute({
205
+ const output = await runWithRequestContext({
206
+ userEmail: claimed.owner,
207
+ orgId: claimed.orgId ?? undefined,
208
+ run: claimed.allowedActionNames === undefined
209
+ ? undefined
210
+ : { allowedActionNames: claimed.allowedActionNames },
211
+ }, () => runner.execute({
205
212
  code: claimed.code,
206
213
  timeoutMs: claimed.timeoutMs,
207
214
  context,
@@ -51,6 +51,7 @@ export interface SandboxExecutionRow {
51
51
  timedOut: boolean;
52
52
  error: string | null;
53
53
  bridgeToolsUsed: string[];
54
+ allowedActionNames: string[] | undefined;
54
55
  createdAt: number;
55
56
  startedAt: number | null;
56
57
  finishedAt: number | null;
@@ -65,6 +66,7 @@ export interface CreateSandboxExecutionInput {
65
66
  maxOutputChars: number;
66
67
  maxAttempts?: number;
67
68
  runtime?: string;
69
+ allowedActionNames?: readonly string[];
68
70
  }
69
71
  export interface FinalizeSandboxExecutionInput {
70
72
  status: Extract<SandboxExecutionStatus, "succeeded" | "failed" | "timed_out">;