@agent-native/core 0.175.5 → 0.175.6-nightly-20260827114203

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.
@@ -1,6 +1,7 @@
1
1
  import { listFileUploadProviders } from "@agent-native/core/file-upload";
2
2
  import {
3
- resolveHasBuilderPrivateKey,
3
+ CredentialStoreUnavailableError,
4
+ hasBuilderApiCredentialCustody,
4
5
  runWithRequestContext,
5
6
  } from "@agent-native/core/server";
6
7
 
@@ -63,8 +64,9 @@ export async function hasRequestReplayStorage(
63
64
  }
64
65
 
65
66
  try {
66
- return await resolveHasBuilderPrivateKey();
67
- } catch {
67
+ return await hasBuilderApiCredentialCustody();
68
+ } catch (err) {
69
+ if (err instanceof CredentialStoreUnavailableError) throw err;
68
70
  return false;
69
71
  }
70
72
  };
@@ -21,7 +21,10 @@ import {
21
21
  uploadFile,
22
22
  getActiveFileUploadProvider,
23
23
  } from "@agent-native/core/file-upload";
24
- import { resolveHasBuilderPrivateKey } from "@agent-native/core/server";
24
+ import {
25
+ CredentialStoreUnavailableError,
26
+ hasBuilderApiCredentialCustody,
27
+ } from "@agent-native/core/server";
25
28
 
26
29
  import {
27
30
  getPresignedS3ObjectUrl,
@@ -93,9 +96,9 @@ export async function isObjectStorageConfigured(): Promise<boolean> {
93
96
  const active = getActiveFileUploadProvider();
94
97
  if (active && active.id !== "sql") return true;
95
98
  try {
96
- if (await resolveHasBuilderPrivateKey()) return true;
97
- } catch {
98
- /* fall through */
99
+ if (await hasBuilderApiCredentialCustody()) return true;
100
+ } catch (err) {
101
+ if (err instanceof CredentialStoreUnavailableError) throw err;
99
102
  }
100
103
  return false;
101
104
  }
@@ -37,7 +37,7 @@ import {
37
37
  writeAppState,
38
38
  } from "@agent-native/core/application-state";
39
39
  import { ssrfSafeFetch } from "@agent-native/core/extensions/url-safety";
40
- import { resolveHasBuilderPrivateKey } from "@agent-native/core/server";
40
+ import { resolveHasBuilderGatewayCredential } from "@agent-native/core/server";
41
41
  import { assertAccess } from "@agent-native/core/sharing";
42
42
  import { transcribeWithBuilder } from "@agent-native/core/transcription/builder";
43
43
  import { and, eq } from "drizzle-orm";
@@ -1263,11 +1263,12 @@ const requestTranscriptAction = defineAction({
1263
1263
  }
1264
1264
 
1265
1265
  // ── Builder transcription (cloud fallback) ────────────────────────
1266
- // Builder proxy is available when the current user has connected
1267
- // Builder via OAuth (per-user app_secrets) OR when BUILDER_PRIVATE_KEY
1268
- // is set at the deployment level. Use the per-user-aware resolver so
1269
- // a sidebar OAuth connection actually wires through to transcription.
1270
- if (await resolveHasBuilderPrivateKey()) {
1266
+ // Gate on the gateway credential, not on Builder connection custody:
1267
+ // `transcribeWithBuilder` authenticates through `resolveBuilderGatewayAuth`,
1268
+ // which reads gateway credentials and a legacy private key and never
1269
+ // consults an OAuth grant. Gating on custody would select this path for an
1270
+ // OAuth-only user, write pending state, prepare audio, and only then fail.
1271
+ if (await resolveHasBuilderGatewayCredential()) {
1271
1272
  if (!regeneratingReadyTranscript) {
1272
1273
  await upsertTranscriptRow(db, {
1273
1274
  recordingId: args.recordingId,
@@ -4,7 +4,6 @@ import {
4
4
  useActionMutation,
5
5
  } from "@agent-native/core/client/hooks";
6
6
  import { useT } from "@agent-native/core/client/i18n";
7
- import { ShareAgentsSection } from "@agent-native/toolkit/sharing";
8
7
  import {
9
8
  useCallback,
10
9
  useEffect,
@@ -16,11 +15,12 @@ import {
16
15
  import { toast } from "sonner";
17
16
 
18
17
  import {
19
- CopyField,
18
+ CopyButton,
20
19
  GeneralAccessSelect,
21
- MakePublicCard,
22
- SharePeopleTab,
23
- copyToClipboard,
20
+ InvitePeopleField,
21
+ PeopleAccessSection,
22
+ ShareSectionLabel,
23
+ nestedLayerDismissGuards,
24
24
  useResourceVisibilityMutation,
25
25
  type SharesQuery,
26
26
  type SharesResponse,
@@ -32,6 +32,7 @@ import {
32
32
  PopoverContent,
33
33
  PopoverTrigger,
34
34
  } from "@/components/ui/popover";
35
+ import { Separator } from "@/components/ui/separator";
35
36
  import { Switch } from "@/components/ui/switch";
36
37
 
37
38
  export interface ShareMeetingPopoverProps {
@@ -52,6 +53,7 @@ export function ShareMeetingPopover({
52
53
  <PopoverTrigger asChild>{children}</PopoverTrigger>
53
54
  <PopoverContent
54
55
  align="end"
56
+ {...nestedLayerDismissGuards()}
55
57
  className="max-h-[calc(100vh-1rem)] w-[440px] max-w-[calc(100vw-1rem)] overflow-y-auto border-border p-0"
56
58
  >
57
59
  <ShareMeetingContent
@@ -85,6 +87,14 @@ function ShareMeetingContent({
85
87
 
86
88
  const data = sharesQuery.data;
87
89
  const canManage = data?.role === "owner" || data?.role === "admin";
90
+ const visibility: Visibility =
91
+ (data?.visibility as Visibility | null) ?? "private";
92
+ const { setResourceVisibility, isPending } = useResourceVisibilityMutation(
93
+ "meeting",
94
+ meetingId,
95
+ sharesQuery,
96
+ );
97
+ const visibilityPending = isPending || sharesQuery.isLoading;
88
98
 
89
99
  return (
90
100
  <div className="min-w-0 px-4 py-3">
@@ -93,6 +103,9 @@ function ShareMeetingContent({
93
103
  shareUrl={shareUrl}
94
104
  sharesQuery={sharesQuery}
95
105
  canManage={canManage}
106
+ visibility={visibility}
107
+ visibilityPending={visibilityPending}
108
+ onVisibilityChange={setResourceVisibility}
96
109
  shareTranscript={shareTranscript}
97
110
  transcriptReady={transcriptReady}
98
111
  />
@@ -105,6 +118,9 @@ function LinkTab({
105
118
  shareUrl,
106
119
  sharesQuery,
107
120
  canManage,
121
+ visibility,
122
+ visibilityPending,
123
+ onVisibilityChange,
108
124
  shareTranscript,
109
125
  transcriptReady,
110
126
  }: {
@@ -112,6 +128,12 @@ function LinkTab({
112
128
  shareUrl: string;
113
129
  sharesQuery: SharesQuery;
114
130
  canManage: boolean;
131
+ visibility: Visibility;
132
+ visibilityPending: boolean;
133
+ onVisibilityChange: (
134
+ next: Visibility,
135
+ options?: { onSuccess?: () => void },
136
+ ) => void;
115
137
  shareTranscript: boolean;
116
138
  transcriptReady: boolean;
117
139
  }) {
@@ -121,14 +143,7 @@ function LinkTab({
121
143
  { id: string; shareTranscript: boolean }
122
144
  >("update-meeting");
123
145
  const [includeTranscript, setIncludeTranscript] = useState(shareTranscript);
124
- const { setResourceVisibility, isPending } = useResourceVisibilityMutation(
125
- "meeting",
126
- meetingId,
127
- sharesQuery,
128
- );
129
146
  const data = sharesQuery.data;
130
- const visibility: Visibility =
131
- (data?.visibility as Visibility | null) ?? "private";
132
147
  const isPublic = visibility === "public";
133
148
  const sharesLoaded = data?.visibility != null;
134
149
  const createAgentLink = useActionMutation("create-agent-resource-link");
@@ -136,7 +151,6 @@ function LinkTab({
136
151
  const agentLinkRequestIdRef = useRef(0);
137
152
  const [agentLink, setAgentLink] = useState("");
138
153
  const [agentLinkError, setAgentLinkError] = useState(false);
139
- const [agentDetailsOpen, setAgentDetailsOpen] = useState(false);
140
154
 
141
155
  useEffect(() => {
142
156
  createAgentLinkAsyncRef.current = createAgentLink.mutateAsync;
@@ -172,10 +186,6 @@ function LinkTab({
172
186
  };
173
187
  }, [isPublic, loadAgentLink, meetingId, sharesLoaded, visibility]);
174
188
 
175
- useEffect(() => {
176
- if (isPublic) setAgentDetailsOpen(false);
177
- }, [isPublic]);
178
-
179
189
  const agentShareDisabled =
180
190
  sharesQuery.isLoading ||
181
191
  !sharesLoaded ||
@@ -206,23 +216,47 @@ function LinkTab({
206
216
 
207
217
  return (
208
218
  <div className="space-y-3">
209
- <CopyField
210
- label={
211
- isPublic
212
- ? t("clipsFinalRaw.shareLink")
213
- : t("shareDialog.shareWithHumans")
214
- }
215
- value={shareUrl}
216
- disabled={!sharesLoaded}
217
- />
219
+ {canManage ? (
220
+ <InvitePeopleField
221
+ resourceType="meeting"
222
+ resourceId={meetingId}
223
+ sharesQuery={sharesQuery}
224
+ onError={(err) =>
225
+ toast.error(
226
+ err instanceof Error
227
+ ? err.message
228
+ : t("clipsFinalRaw.inviteFailed"),
229
+ )
230
+ }
231
+ />
232
+ ) : null}
218
233
 
219
- <GeneralAccessSelect
220
- visibility={visibility}
221
- canManage={canManage}
222
- isPending={isPending}
223
- onChange={(next) => setResourceVisibility(next)}
224
- showDescription={false}
225
- />
234
+ <div className="space-y-2">
235
+ <ShareSectionLabel>{t("shareUi.whoHasAccess")}</ShareSectionLabel>
236
+ <div className="flex flex-col gap-1">
237
+ <PeopleAccessSection
238
+ resourceType="meeting"
239
+ resourceId={meetingId}
240
+ sharesQuery={sharesQuery}
241
+ canManage={canManage}
242
+ onError={(err, action) =>
243
+ toast.error(
244
+ err instanceof Error
245
+ ? err.message
246
+ : action === "permission"
247
+ ? t("clipsFinalRaw.permissionUpdateFailed")
248
+ : t("clipsFinalRaw.removePersonFailed"),
249
+ )
250
+ }
251
+ />
252
+ <GeneralAccessSelect
253
+ visibility={visibility}
254
+ canManage={canManage}
255
+ isPending={visibilityPending}
256
+ onChange={onVisibilityChange}
257
+ />
258
+ </div>
259
+ </div>
226
260
 
227
261
  <div className="flex items-center justify-between gap-4 rounded-md border border-border px-3 py-2.5">
228
262
  <div className="min-w-0">
@@ -251,73 +285,42 @@ function LinkTab({
251
285
  />
252
286
  </div>
253
287
 
254
- <SharePeopleTab
255
- resourceType="meeting"
256
- resourceId={meetingId}
257
- sharesQuery={sharesQuery}
258
- canManage={canManage}
259
- onError={(err, action) =>
260
- toast.error(
261
- err instanceof Error
262
- ? err.message
263
- : action === "invite"
264
- ? t("clipsFinalRaw.inviteFailed")
265
- : action === "permission"
266
- ? t("clipsFinalRaw.permissionUpdateFailed")
267
- : t("clipsFinalRaw.removePersonFailed"),
268
- )
269
- }
270
- />
288
+ <CopyButton value={shareUrl} disabled={!sharesLoaded}>
289
+ {t("shareUi.copyLink")}
290
+ </CopyButton>
271
291
 
272
- {!isPublic && canManage ? (
273
- <MakePublicCard
274
- isPending={isPending}
275
- onMakePublic={() =>
276
- setResourceVisibility("public", {
277
- onSuccess: () => copyToClipboard(shareUrl),
278
- })
279
- }
280
- />
281
- ) : null}
292
+ <Separator />
282
293
 
283
- <ShareAgentsSection
284
- label={t("shareDialog.shareWithAgents")}
285
- open={agentDetailsOpen}
286
- onOpenChange={setAgentDetailsOpen}
287
- contentClassName="clips-collapsible-content"
288
- >
289
- <div className="space-y-2">
290
- <CopyField
291
- label={t("shareDialog.shareLink")}
294
+ <div className="flex items-center justify-between gap-4">
295
+ <div className="min-w-0">
296
+ <div className="text-sm font-medium">
297
+ {t("shareDialog.shareWithAgents")}
298
+ </div>
299
+ <p className="text-xs text-muted-foreground">
300
+ {t("shareMeeting.agentLinkDescription")}
301
+ </p>
302
+ </div>
303
+ {agentLinkError ? (
304
+ <Button
305
+ type="button"
306
+ variant="outline"
307
+ size="sm"
308
+ className="shrink-0"
309
+ onClick={() => void loadAgentLink()}
310
+ disabled={createAgentLink.isPending}
311
+ >
312
+ {t("shareDialog.retryAgentLink")}
313
+ </Button>
314
+ ) : (
315
+ <CopyButton
292
316
  value={visibleAgentLink}
293
317
  disabled={agentShareDisabled}
294
- />
295
- {sharesLoaded ? (
296
- <>
297
- <p className="text-xs text-muted-foreground">
298
- {t("shareMeeting.agentLinkDescription")}
299
- </p>
300
- {agentLinkError ? (
301
- <div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
302
- <p className="text-xs text-muted-foreground">
303
- {t("shareDialog.agentLinkUnavailable")}
304
- </p>
305
- <Button
306
- type="button"
307
- variant="ghost"
308
- size="sm"
309
- className="h-7"
310
- onClick={() => void loadAgentLink()}
311
- disabled={createAgentLink.isPending}
312
- >
313
- {t("shareDialog.retryAgentLink")}
314
- </Button>
315
- </div>
316
- ) : null}
317
- </>
318
- ) : null}
319
- </div>
320
- </ShareAgentsSection>
318
+ className="shrink-0"
319
+ >
320
+ {t("shareUi.copy")}
321
+ </CopyButton>
322
+ )}
323
+ </div>
321
324
  </div>
322
325
  );
323
326
  }