@agent-native/core 0.147.0 → 0.148.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 (197) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +115 -68
  3. package/corpus/templates/analytics/DEVELOPING.md +50 -3
  4. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +190 -0
  5. package/corpus/templates/analytics/app/i18n/index.ts +14 -29
  6. package/corpus/templates/analytics/app/i18n-data.ts +10 -10
  7. package/corpus/templates/analytics/app/pages/Settings.tsx +5 -21
  8. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -3
  9. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +197 -18
  10. package/corpus/templates/analytics/server/db/schema.ts +23 -0
  11. package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +3 -1
  12. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +1 -1
  13. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +113 -13
  14. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +140 -0
  15. package/corpus/templates/analytics/server/lib/first-party-analytics-volume.ts +207 -0
  16. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +40 -9
  17. package/corpus/templates/analytics/server/lib/gcloud.ts +148 -20
  18. package/corpus/templates/analytics/server/plugins/db.ts +42 -0
  19. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +115 -68
  20. package/corpus/templates/assets/app/i18n/index.ts +14 -29
  21. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +115 -68
  22. package/corpus/templates/brain/app/i18n/index.ts +14 -29
  23. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +115 -68
  24. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  25. package/corpus/templates/chat/app/i18n/index.ts +14 -29
  26. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +115 -68
  27. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -7
  28. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +18 -13
  29. package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +87 -370
  30. package/corpus/templates/clips/app/components/settings/slack-section.tsx +59 -101
  31. package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +136 -213
  32. package/corpus/templates/clips/app/i18n/en-US.ts +11 -8
  33. package/corpus/templates/clips/desktop/src/app.tsx +1 -1
  34. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +115 -68
  35. package/corpus/templates/content/app/i18n/index.ts +14 -29
  36. package/corpus/templates/crm/app/i18n/index.ts +14 -29
  37. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +115 -68
  38. package/corpus/templates/design/actions/analyze-brand-assets.ts +15 -15
  39. package/corpus/templates/design/actions/import-code.ts +2 -43
  40. package/corpus/templates/design/app/i18n/index.ts +14 -29
  41. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +115 -68
  42. package/corpus/templates/dispatch/app/i18n-data.ts +1 -1
  43. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +115 -68
  44. package/corpus/templates/factory/AGENTS.md +40 -49
  45. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +66 -0
  46. package/corpus/templates/factory/actions/evaluate-triage-item.ts +37 -0
  47. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -0
  48. package/corpus/templates/factory/actions/get-triage-item.ts +25 -4
  49. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +55 -0
  50. package/corpus/templates/factory/actions/ingest-github-observation.ts +21 -0
  51. package/corpus/templates/factory/actions/list-factory-audit.ts +118 -0
  52. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  53. package/corpus/templates/factory/actions/list-triage-items.ts +24 -2
  54. package/corpus/templates/factory/actions/poll-github-sources.ts +60 -0
  55. package/corpus/templates/factory/actions/poll-sentry-errors.ts +39 -0
  56. package/corpus/templates/factory/actions/poll-slack-channel.ts +45 -1
  57. package/corpus/templates/factory/actions/run-factory-automation.ts +1 -1
  58. package/corpus/templates/factory/actions/save-factory-automation.ts +2 -2
  59. package/corpus/templates/factory/actions/start-builder-for-item.ts +51 -0
  60. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +685 -0
  61. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +6 -0
  62. package/corpus/templates/factory/app/i18n/en-US.ts +25 -0
  63. package/corpus/templates/factory/app/routes/factory.tsx +95 -42
  64. package/corpus/templates/factory/server/db/schema.ts +37 -0
  65. package/corpus/templates/factory/server/lib/require-factory-automation.ts +5 -1
  66. package/corpus/templates/factory/server/plugins/factory-migrations.ts +29 -0
  67. package/corpus/templates/factory/server/triage/audit.ts +79 -0
  68. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +115 -68
  69. package/corpus/templates/forms/app/i18n/index.ts +14 -29
  70. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +115 -68
  71. package/corpus/templates/macros/app/i18n/index.ts +14 -29
  72. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +115 -68
  73. package/corpus/templates/mail/app/i18n/index.ts +14 -29
  74. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +115 -68
  75. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +7 -7
  76. package/corpus/templates/plan/app/i18n/index.ts +14 -29
  77. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +115 -68
  78. package/corpus/templates/slides/actions/analyze-brand-assets.ts +15 -15
  79. package/corpus/templates/slides/actions/import-code.ts +2 -43
  80. package/corpus/templates/slides/app/i18n/index.ts +14 -29
  81. package/corpus/templates/slides/server/routes/api/preview-images.get.ts +4 -1
  82. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +115 -68
  83. package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +1 -84
  84. package/corpus/templates/tasks/app/hooks/use-mobile.tsx +1 -21
  85. package/corpus/templates/tasks/app/i18n/index.ts +14 -29
  86. package/dist/agent/engine/ai-sdk-engine.js +2 -1
  87. package/dist/agent/engine/openai-compatible-endpoint.d.ts +2 -0
  88. package/dist/agent/engine/openai-compatible-endpoint.js +5 -1
  89. package/dist/agent/engine/provider-endpoint-validation.d.ts +9 -0
  90. package/dist/agent/engine/provider-endpoint-validation.js +26 -0
  91. package/dist/agent/engine/provider-env-vars.d.ts +1 -1
  92. package/dist/agent/engine/provider-env-vars.js +1 -1
  93. package/dist/agent/engine/registry.d.ts +8 -6
  94. package/dist/agent/engine/registry.js +37 -24
  95. package/dist/agent/tool-search.d.ts +1 -0
  96. package/dist/agent/tool-search.js +17 -1
  97. package/dist/automations/service.js +4 -10
  98. package/dist/brand-kit/brand-signals.d.ts +13 -0
  99. package/dist/brand-kit/brand-signals.js +15 -0
  100. package/dist/brand-kit/index.d.ts +2 -2
  101. package/dist/brand-kit/index.js +1 -1
  102. package/dist/cli/plan-blocks.d.ts +3 -18
  103. package/dist/cli/plan-blocks.js +2 -95
  104. package/dist/cli/plan-publish-store.d.ts +2 -61
  105. package/dist/cli/plan-publish-store.js +2 -127
  106. package/dist/cli/skills-content/connection.d.ts +1 -1
  107. package/dist/cli/skills-content/connection.js +1 -52
  108. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  109. package/dist/cli/skills-content/visual-recap-skill.js +1 -553
  110. package/dist/client/agent-engine-key.d.ts +14 -4
  111. package/dist/client/agent-engine-key.js +51 -5
  112. package/dist/client/agent-provider-catalog.d.ts +20 -0
  113. package/dist/client/agent-provider-catalog.js +100 -0
  114. package/dist/client/blocks/index.d.ts +1 -0
  115. package/dist/client/blocks/index.js +1 -0
  116. package/dist/client/blocks/library/diagram.js +1 -1
  117. package/dist/client/blocks/library/sanitize-html.js +66 -2
  118. package/dist/client/blocks/library/wireframe-kit.js +69 -0
  119. package/dist/client/chat/run-recovery.js +4 -48
  120. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  121. package/dist/client/chat-first/apps-rail.js +4 -4
  122. package/dist/client/chat-first/copy.js +1 -0
  123. package/dist/client/chat-first/types.d.ts +1 -0
  124. package/dist/client/chat-first.js +4 -3
  125. package/dist/client/i18n.d.ts +14 -0
  126. package/dist/client/i18n.js +18 -0
  127. package/dist/client/settings/ProviderSetupForm.d.ts +21 -0
  128. package/dist/client/settings/ProviderSetupForm.js +145 -0
  129. package/dist/client/settings/SettingsPanel.js +52 -49
  130. package/dist/client/settings/index.d.ts +2 -0
  131. package/dist/client/settings/index.js +2 -0
  132. package/dist/coding-tools/run-code.d.ts +5 -0
  133. package/dist/coding-tools/run-code.js +196 -36
  134. package/dist/coding-tools/tool-orchestration.d.ts +10 -0
  135. package/dist/coding-tools/tool-orchestration.js +107 -0
  136. package/dist/collab/routes.d.ts +1 -1
  137. package/dist/jobs/frontmatter.d.ts +9 -0
  138. package/dist/jobs/frontmatter.js +16 -0
  139. package/dist/jobs/scheduler.js +8 -8
  140. package/dist/jobs/tools.d.ts +1 -1
  141. package/dist/jobs/tools.js +2 -7
  142. package/dist/localization/default-messages.d.ts +20 -0
  143. package/dist/localization/default-messages.js +25 -5
  144. package/dist/notifications/routes.d.ts +3 -3
  145. package/dist/observability/routes.d.ts +2 -2
  146. package/dist/onboarding/app-profile.js +1 -1
  147. package/dist/progress/routes.d.ts +1 -1
  148. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  149. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  150. package/dist/scripts/agent-engines/test-agent-engine.js +34 -10
  151. package/dist/server/agent-chat/run-code-tools.d.ts +1 -1
  152. package/dist/server/agent-chat/run-code-tools.js +3 -1
  153. package/dist/server/agent-chat-plugin.js +6 -6
  154. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  155. package/dist/server/agent-engine-api-key-route.js +43 -12
  156. package/dist/server/agents-bundle.js +2 -1
  157. package/dist/server/auth.js +21 -5
  158. package/dist/server/credential-provider.js +1 -0
  159. package/dist/server/design-token-utils.d.ts +18 -0
  160. package/dist/server/design-token-utils.js +31 -0
  161. package/dist/server/onboarding-html.js +22 -2
  162. package/dist/server/realtime-token.d.ts +1 -1
  163. package/dist/styles/blocks.css +132 -0
  164. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  165. package/dist/templates/chat/app/i18n/index.ts +14 -29
  166. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  167. package/dist/templates/default/app/i18n/index.ts +14 -29
  168. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  169. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  170. package/dist/triggers/dispatcher.js +1 -8
  171. package/docs/content/actions.mdx +4 -1
  172. package/docs/content/agent-surfaces.mdx +8 -6
  173. package/docs/content/automations.mdx +1 -1
  174. package/docs/content/database.mdx +6 -1
  175. package/docs/content/docs-components.mdx +22 -14
  176. package/docs/content/drop-in-agent.mdx +1 -1
  177. package/docs/content/environment-variables.mdx +39 -38
  178. package/docs/content/getting-started-actions.mdx +5 -4
  179. package/docs/content/getting-started-pages.mdx +7 -6
  180. package/docs/content/getting-started.mdx +4 -3
  181. package/docs/content/notifications.mdx +6 -6
  182. package/docs/content/progress.mdx +5 -5
  183. package/docs/content/routing.mdx +3 -1
  184. package/docs/content/server-database.mdx +13 -4
  185. package/docs/content/server-overview.mdx +9 -9
  186. package/docs/content/server-plugins.mdx +10 -4
  187. package/docs/content/server-routes.mdx +7 -2
  188. package/docs/content/tracking.mdx +4 -4
  189. package/docs/content/what-is-agent-native.mdx +4 -4
  190. package/package.json +4 -4
  191. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  192. package/src/templates/chat/app/i18n/index.ts +14 -29
  193. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  194. package/src/templates/default/app/i18n/index.ts +14 -29
  195. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  196. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  197. package/corpus/templates/plan/app/components/plan/wireframe/sanitize-html.ts +0 -343
@@ -4,12 +4,8 @@ import {
4
4
  } from "@agent-native/core/client/api-path";
5
5
  import { useActionQuery } from "@agent-native/core/client/hooks";
6
6
  import { useT } from "@agent-native/core/client/i18n";
7
- import {
8
- IconBrandSlack,
9
- IconExternalLink,
10
- IconLoader2,
11
- IconTrash,
12
- } from "@tabler/icons-react";
7
+ import { SettingsGroup, SettingsRow } from "@agent-native/core/client/settings";
8
+ import { IconBrandSlack, IconLoader2, IconTrash } from "@tabler/icons-react";
13
9
  import { useState } from "react";
14
10
  import { toast } from "sonner";
15
11
 
@@ -24,13 +20,6 @@ import {
24
20
  AlertDialogTitle,
25
21
  } from "@/components/ui/alert-dialog";
26
22
  import { Button } from "@/components/ui/button";
27
- import {
28
- Card,
29
- CardContent,
30
- CardDescription,
31
- CardHeader,
32
- CardTitle,
33
- } from "@/components/ui/card";
34
23
 
35
24
  interface SlackInstallation {
36
25
  id: string;
@@ -188,103 +177,72 @@ export function SlackSection() {
188
177
 
189
178
  return (
190
179
  <>
191
- <Card id="slack" className="scroll-mt-16">
192
- <CardHeader>
193
- <CardTitle className="text-base flex items-center gap-2">
194
- <IconBrandSlack className="size-4 text-primary" />
195
- {t("settings.slackTitle")}
196
- </CardTitle>
197
- <CardDescription>{t("settings.slackDescription")}</CardDescription>
198
- </CardHeader>
199
- <CardContent className="space-y-4">
200
- <div className="flex flex-col gap-3 rounded-md border border-border bg-accent/30 px-3 py-3 sm:flex-row sm:items-center sm:justify-between">
201
- <div className="min-w-0">
202
- <div className="flex items-center gap-2 text-sm font-medium">
203
- <IconBrandSlack className="h-4 w-4 text-muted-foreground" />
204
- {slackStatus.isLoading
205
- ? t("settings.checkingSlack")
206
- : connected
207
- ? t("settings.slackConnected", {
208
- count: installations.length,
209
- })
210
- : oauthConfigured
211
- ? t("common.notConnected")
212
- : t("settings.slackOauthNeeded")}
213
- </div>
214
- <p className="mt-0.5 text-xs text-muted-foreground">
215
- {t("settings.slackPreviewDescription")}
216
- </p>
217
- </div>
180
+ <SettingsGroup
181
+ id="slack"
182
+ title={t("settings.slackTitle")}
183
+ description={t("settings.slackDescription")}
184
+ >
185
+ <SettingsRow
186
+ label={
187
+ slackStatus.isLoading
188
+ ? t("settings.checkingSlack")
189
+ : connected
190
+ ? t("settings.slackConnected", {
191
+ count: installations.length,
192
+ })
193
+ : oauthConfigured
194
+ ? t("common.notConnected")
195
+ : t("settings.slackOauthNeeded")
196
+ }
197
+ description={
198
+ !oauthConfigured
199
+ ? t("settings.slackClientMissing")
200
+ : !signingConfigured
201
+ ? t("settings.slackSigningMissing")
202
+ : t("settings.slackPreviewDescription")
203
+ }
204
+ icon={<IconBrandSlack className="text-primary" />}
205
+ control={
218
206
  <Button
219
207
  type="button"
220
- variant="outline"
208
+ variant="default"
221
209
  size="sm"
222
- className="shrink-0"
223
210
  onClick={handleConnect}
224
211
  disabled={connecting || slackStatus.isLoading || !oauthConfigured}
225
212
  >
226
- {connecting ? (
227
- <IconLoader2 className="h-4 w-4 animate-spin" />
228
- ) : (
229
- <IconExternalLink className="h-4 w-4" />
230
- )}
213
+ {connecting ? <IconLoader2 className="animate-spin" /> : null}
231
214
  {t("settings.connectSlack")}
232
215
  </Button>
233
- </div>
234
-
235
- {!oauthConfigured ? (
236
- <div className="rounded-md border border-border p-3 text-xs text-muted-foreground">
237
- {t("settings.slackClientMissing")}
238
- </div>
239
- ) : null}
240
-
241
- {!signingConfigured ? (
242
- <div className="rounded-md border border-border p-3 text-xs text-muted-foreground">
243
- {t("settings.slackSigningMissing")}
244
- </div>
245
- ) : null}
216
+ }
217
+ />
246
218
 
247
- {installations.length > 0 ? (
248
- <div className="space-y-2">
249
- {installations.map((installation) => (
250
- <div
251
- key={installation.id}
252
- className="flex items-center justify-between gap-3 rounded-md border border-border px-3 py-2"
253
- >
254
- <div className="min-w-0">
255
- <div className="truncate text-sm font-medium">
256
- {installation.teamName || installation.teamId}
257
- </div>
258
- <div className="mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground">
259
- <span>{installation.status}</span>
260
- {installation.enterpriseName ? (
261
- <span>{installation.enterpriseName}</span>
262
- ) : null}
263
- <span>
264
- {t("settings.connectedBy", {
265
- email: installation.ownerEmail,
266
- })}
267
- </span>
268
- </div>
269
- </div>
270
- <Button
271
- type="button"
272
- variant="ghost"
273
- size="icon"
274
- className="shrink-0"
275
- aria-label={t("settings.disconnectSlackLabel", {
276
- team: installation.teamName || installation.teamId,
277
- })}
278
- onClick={() => setDisconnectTarget(installation)}
279
- >
280
- <IconTrash className="h-4 w-4" />
281
- </Button>
282
- </div>
283
- ))}
284
- </div>
285
- ) : null}
286
- </CardContent>
287
- </Card>
219
+ {installations.map((installation) => (
220
+ <SettingsRow
221
+ key={installation.id}
222
+ label={installation.teamName || installation.teamId}
223
+ description={[
224
+ installation.status,
225
+ installation.enterpriseName,
226
+ t("settings.connectedBy", { email: installation.ownerEmail }),
227
+ ]
228
+ .filter(Boolean)
229
+ .join(" · ")}
230
+ control={
231
+ <Button
232
+ type="button"
233
+ variant="ghost"
234
+ size="icon"
235
+ aria-label={t("settings.disconnectSlackLabel", {
236
+ team: installation.teamName || installation.teamId,
237
+ })}
238
+ onClick={() => setDisconnectTarget(installation)}
239
+ >
240
+ <IconTrash />
241
+ </Button>
242
+ }
243
+ />
244
+ ))}
245
+ </SettingsGroup>
288
246
 
289
247
  <AlertDialog
290
248
  open={!!disconnectTarget}
@@ -1,27 +1,11 @@
1
1
  import { agentNativePath } from "@agent-native/core/client/api-path";
2
2
  import { useT } from "@agent-native/core/client/i18n";
3
- import {
4
- IconCheck,
5
- IconChevronDown,
6
- IconCloud,
7
- IconExternalLink,
8
- IconKey,
9
- IconLoader2,
10
- IconServer,
11
- IconTrash,
12
- } from "@tabler/icons-react";
3
+ import { SettingsGroup, SettingsRow } from "@agent-native/core/client/settings";
4
+ import { IconCheck, IconLoader2, IconTrash } from "@tabler/icons-react";
13
5
  import { useState } from "react";
14
6
  import { toast } from "sonner";
15
7
 
16
- import { Badge } from "@/components/ui/badge";
17
8
  import { Button } from "@/components/ui/button";
18
- import {
19
- Card,
20
- CardContent,
21
- CardDescription,
22
- CardHeader,
23
- CardTitle,
24
- } from "@/components/ui/card";
25
9
  import {
26
10
  Collapsible,
27
11
  CollapsibleContent,
@@ -31,7 +15,6 @@ import { Input } from "@/components/ui/input";
31
15
  import { Label } from "@/components/ui/label";
32
16
  import type { SecretStatus } from "@/hooks/use-secret-status";
33
17
  import type { useVideoStorageStatus } from "@/hooks/use-video-storage-status";
34
- import { cn } from "@/lib/utils";
35
18
 
36
19
  import type { BuilderConnection } from "./types";
37
20
 
@@ -232,212 +215,152 @@ export function VideoStorageSection({
232
215
  }
233
216
 
234
217
  return (
235
- <Card id="video-storage" className="scroll-mt-16">
236
- <CardHeader>
237
- <CardTitle className="text-base flex items-center gap-2">
238
- <IconCloud className="size-4 text-primary" />
239
- {t("settings.videoStorage")}
240
- </CardTitle>
241
- <CardDescription>
242
- {t("settings.videoStorageDescription")}
243
- </CardDescription>
244
- </CardHeader>
245
- <CardContent className="space-y-4">
246
- <div
247
- className={cn(
248
- "flex flex-col gap-3 rounded-md border px-3 py-3 sm:flex-row sm:items-center sm:justify-between",
249
- builder.connected
250
- ? "border-primary/35 bg-primary/5"
251
- : "border-border bg-accent/30",
252
- )}
253
- >
254
- <div className="min-w-0">
255
- <div className="flex items-center gap-2 text-sm font-medium">
218
+ <SettingsGroup id="video-storage" title={t("settings.videoStorage")}>
219
+ <Collapsible open={expanded} onOpenChange={setExpanded}>
220
+ <SettingsRow
221
+ label="Builder.io"
222
+ description={
223
+ builder.loading
224
+ ? t("settings.checkingBuilder")
225
+ : s3Configured && storageConfigured && activeProviderName
226
+ ? t("settings.s3CurrentProvider", {
227
+ providerName: activeProviderName,
228
+ })
229
+ : builder.connected
230
+ ? builder.orgName
231
+ ? t("settings.builderConnectedFor", {
232
+ orgName: builder.orgName,
233
+ })
234
+ : t("settings.builderConnectedGeneric")
235
+ : t("settings.builderIncludes")
236
+ }
237
+ control={
238
+ <div className="flex flex-wrap items-center justify-end gap-2">
256
239
  {builder.connected ? (
257
- <IconCheck className="h-4 w-4 text-primary" />
240
+ <span className="inline-flex items-center gap-1.5 text-sm font-medium text-primary">
241
+ <IconCheck className="h-4 w-4" />
242
+ {t("common.connected")}
243
+ </span>
258
244
  ) : (
259
- <IconKey className="h-4 w-4 text-muted-foreground" />
260
- )}
261
- {builder.loading
262
- ? t("settings.checkingBuilder")
263
- : builder.connected
264
- ? t("settings.builderConnected")
265
- : t("settings.connectBuilder")}
266
- </div>
267
- <p className="mt-0.5 text-xs text-muted-foreground">
268
- {builder.connected
269
- ? builder.orgName
270
- ? t("settings.builderConnectedFor", {
271
- orgName: builder.orgName,
245
+ <Button
246
+ type="button"
247
+ variant="default"
248
+ size="sm"
249
+ onClick={() =>
250
+ builder.start({
251
+ trackingSource: "clips_settings_video_storage",
252
+ trackingFlow: "video_storage",
272
253
  })
273
- : t("settings.builderConnectedGeneric")
274
- : t("settings.builderIncludes")}
275
- </p>
276
- </div>
277
- {builder.connected ? (
278
- <Badge variant="secondary" className="shrink-0">
279
- {t("common.connected")}
280
- </Badge>
281
- ) : (
282
- <Button
283
- type="button"
284
- variant="outline"
285
- size="sm"
286
- className="shrink-0"
287
- onClick={() =>
288
- builder.start({
289
- trackingSource: "clips_settings_video_storage",
290
- trackingFlow: "video_storage",
291
- })
292
- }
293
- disabled={builder.connecting || builder.loading}
294
- >
295
- {builder.connecting ? (
296
- <IconLoader2 className="h-4 w-4 animate-spin" />
297
- ) : (
298
- <IconExternalLink className="h-4 w-4" />
299
- )}
300
- {t("settings.connectBuilder")}
301
- </Button>
302
- )}
303
- </div>
304
-
305
- <Collapsible open={expanded} onOpenChange={setExpanded}>
306
- <div className="rounded-md border border-border">
307
- <div className="flex flex-col gap-3 px-3 py-3 sm:flex-row sm:items-center sm:justify-between">
308
- <div className="min-w-0">
309
- <div className="flex flex-wrap items-center gap-2 text-sm font-medium">
310
- <IconServer className="h-4 w-4 text-muted-foreground" />
311
- {t("settings.s3Title")}
312
- <Badge variant="outline" className="text-[10px]">
313
- {t("settings.secondary")}
314
- </Badge>
315
- {s3Configured ? (
316
- <Badge variant="secondary" className="text-[10px]">
317
- {t("settings.active")}
318
- </Badge>
254
+ }
255
+ disabled={builder.connecting || builder.loading}
256
+ >
257
+ {builder.connecting ? (
258
+ <IconLoader2 className="h-4 w-4 animate-spin" />
319
259
  ) : null}
320
- </div>
321
- <p className="mt-0.5 text-xs text-muted-foreground">
322
- {builder.connected
323
- ? t("settings.s3BuilderConnectedDescription")
324
- : storageConfigured && activeProviderName
325
- ? t("settings.s3CurrentProvider", {
326
- providerName: activeProviderName,
327
- })
328
- : t("settings.s3OwnBucketDescription")}
329
- </p>
330
- </div>
260
+ {t("settings.connectBuilder")}
261
+ </Button>
262
+ )}
331
263
  <CollapsibleTrigger asChild>
264
+ <Button type="button" variant="outline" size="sm">
265
+ {expanded
266
+ ? t("settings.hideS3")
267
+ : s3Configured
268
+ ? t("settings.providerManage")
269
+ : t("settings.configureS3")}
270
+ </Button>
271
+ </CollapsibleTrigger>
272
+ </div>
273
+ }
274
+ />
275
+
276
+ <CollapsibleContent>
277
+ <div className="space-y-4 border-t border-border px-5 py-4">
278
+ <div className="grid gap-4 sm:grid-cols-2">
279
+ {S3_STORAGE_FIELDS.map((field) => {
280
+ const configured = Boolean(secrets.configured[field.key]);
281
+ const last4 = secrets.last4[field.key];
282
+ return (
283
+ <div key={field.key} className="space-y-1.5">
284
+ <div className="flex items-center justify-between gap-2">
285
+ <Label htmlFor={field.key}>{t(field.labelKey)}</Label>
286
+ {configured ? (
287
+ <span className="flex items-center gap-1 text-[10px] font-medium text-primary">
288
+ <IconCheck className="h-3 w-3" />
289
+ {last4 ? `••••${last4}` : t("settings.keySet")}
290
+ </span>
291
+ ) : null}
292
+ </div>
293
+ <Input
294
+ id={field.key}
295
+ type={
296
+ "secret" in field && field.secret ? "password" : "text"
297
+ }
298
+ value={values[field.key] ?? ""}
299
+ onChange={(event) => {
300
+ setValues((current) => ({
301
+ ...current,
302
+ [field.key]: event.target.value,
303
+ }));
304
+ if (errors[field.key]) {
305
+ setErrors((current) => {
306
+ const next = { ...current };
307
+ delete next[field.key];
308
+ return next;
309
+ });
310
+ }
311
+ }}
312
+ placeholder={
313
+ configured
314
+ ? t("settings.replaceKey")
315
+ : field.placeholder
316
+ }
317
+ autoComplete="off"
318
+ disabled={saving}
319
+ className={
320
+ errors[field.key] ? "border-destructive" : undefined
321
+ }
322
+ />
323
+ {errors[field.key] ? (
324
+ <p className="text-[11px] text-destructive">
325
+ {errors[field.key]}
326
+ </p>
327
+ ) : null}
328
+ </div>
329
+ );
330
+ })}
331
+ </div>
332
+
333
+ <div className="flex items-center justify-end gap-2">
334
+ {S3_STORAGE_FIELDS.some(
335
+ (field) => secrets.configured[field.key],
336
+ ) ? (
332
337
  <Button
333
338
  type="button"
334
339
  variant="ghost"
335
340
  size="sm"
336
- className="shrink-0"
341
+ onClick={handleClearAll}
342
+ disabled={clearing || saving}
343
+ className="text-muted-foreground hover:text-destructive"
337
344
  >
338
- {expanded ? t("settings.hideS3") : t("settings.configureS3")}
339
- <IconChevronDown
340
- className={cn(
341
- "h-4 w-4 transition-transform",
342
- expanded && "rotate-180",
343
- )}
344
- />
345
+ {clearing ? (
346
+ <IconLoader2 className="h-4 w-4 animate-spin" />
347
+ ) : (
348
+ <IconTrash className="h-4 w-4" />
349
+ )}
350
+ {t("settings.clearAllS3")}
345
351
  </Button>
346
- </CollapsibleTrigger>
352
+ ) : null}
353
+ <Button
354
+ onClick={handleSave}
355
+ disabled={saving || storageStatus.isLoading}
356
+ >
357
+ {saving && <IconLoader2 className="h-4 w-4 animate-spin" />}
358
+ {t("settings.saveStorage")}
359
+ </Button>
347
360
  </div>
348
-
349
- <CollapsibleContent>
350
- <div className="space-y-4 border-t border-border px-3 py-4">
351
- <div className="grid gap-4 sm:grid-cols-2">
352
- {S3_STORAGE_FIELDS.map((field) => {
353
- const configured = Boolean(secrets.configured[field.key]);
354
- const last4 = secrets.last4[field.key];
355
- return (
356
- <div key={field.key} className="space-y-1.5">
357
- <div className="flex items-center justify-between gap-2">
358
- <Label htmlFor={field.key}>{t(field.labelKey)}</Label>
359
- {configured ? (
360
- <span className="flex items-center gap-1 text-[10px] font-medium text-primary">
361
- <IconCheck className="h-3 w-3" />
362
- {last4 ? `••••${last4}` : t("settings.keySet")}
363
- </span>
364
- ) : null}
365
- </div>
366
- <Input
367
- id={field.key}
368
- type={
369
- "secret" in field && field.secret
370
- ? "password"
371
- : "text"
372
- }
373
- value={values[field.key] ?? ""}
374
- onChange={(event) => {
375
- setValues((current) => ({
376
- ...current,
377
- [field.key]: event.target.value,
378
- }));
379
- if (errors[field.key]) {
380
- setErrors((current) => {
381
- const next = { ...current };
382
- delete next[field.key];
383
- return next;
384
- });
385
- }
386
- }}
387
- placeholder={
388
- configured
389
- ? t("settings.replaceKey")
390
- : field.placeholder
391
- }
392
- autoComplete="off"
393
- disabled={saving}
394
- className={
395
- errors[field.key] ? "border-destructive" : undefined
396
- }
397
- />
398
- {errors[field.key] ? (
399
- <p className="text-[11px] text-destructive">
400
- {errors[field.key]}
401
- </p>
402
- ) : null}
403
- </div>
404
- );
405
- })}
406
- </div>
407
-
408
- <div className="flex items-center justify-end gap-2">
409
- {S3_STORAGE_FIELDS.some(
410
- (field) => secrets.configured[field.key],
411
- ) ? (
412
- <Button
413
- type="button"
414
- variant="ghost"
415
- size="sm"
416
- onClick={handleClearAll}
417
- disabled={clearing || saving}
418
- className="text-muted-foreground hover:text-destructive"
419
- >
420
- {clearing ? (
421
- <IconLoader2 className="h-4 w-4 animate-spin" />
422
- ) : (
423
- <IconTrash className="h-4 w-4" />
424
- )}
425
- {t("settings.clearAllS3")}
426
- </Button>
427
- ) : null}
428
- <Button
429
- onClick={handleSave}
430
- disabled={saving || storageStatus.isLoading}
431
- >
432
- {saving && <IconLoader2 className="h-4 w-4 animate-spin" />}
433
- {t("settings.saveStorage")}
434
- </Button>
435
- </div>
436
- </div>
437
- </CollapsibleContent>
438
361
  </div>
439
- </Collapsible>
440
- </CardContent>
441
- </Card>
362
+ </CollapsibleContent>
363
+ </Collapsible>
364
+ </SettingsGroup>
442
365
  );
443
366
  }
@@ -675,11 +675,11 @@ All notable user-facing changes to Clips are documented here. Open it any time f
675
675
  "Builder.io is the primary storage path for Clips uploads. S3 is available when you need to bring your own bucket.",
676
676
  checkingBuilder: "Checking Builder.io",
677
677
  builderConnected: "Builder.io connected",
678
- connectBuilder: "Use Builder.io (free)",
678
+ connectBuilder: "Use Builder.io",
679
679
  builderConnectedFor: "Using Builder.io for {{orgName}}.",
680
680
  builderConnectedGeneric: "New clips use the connected Builder.io provider.",
681
681
  builderIncludes:
682
- "Includes object storage, uploads, and managed transcription for new clips.",
682
+ "Builder.io's free tier includes object storage, uploads, and managed transcription for new clips.",
683
683
  s3Title: "S3-compatible storage",
684
684
  secondary: "Secondary",
685
685
  active: "Active",
@@ -705,8 +705,7 @@ All notable user-facing changes to Clips are documented here. Open it any time f
705
705
  "Bucket name must be 3–63 lowercase letters, numbers, or hyphens",
706
706
  s3RegionInvalid: 'Must be a valid region (e.g. us-east-1) or "auto"',
707
707
  apiSetup: "AI setup",
708
- apiSetupDescription:
709
- "Connect AI with Builder.io free credits or your own LLM keys.",
708
+ apiSetupDescription: "Choose how Clips connects to AI.",
710
709
  builderEasySetup: "Builder.io free credits",
711
710
  builderAiAvailable:
712
711
  "Included AI credits and managed transcription are available for Clips.",
@@ -714,8 +713,13 @@ All notable user-facing changes to Clips are documented here. Open it any time f
714
713
  "Use Builder.io first for included AI credits, object storage, uploads, and managed transcription.",
715
714
  providerKeyTitle: "Bring your own provider key",
716
715
  providerKeyDescription:
717
- "Add Anthropic, OpenAI, Gemini, Groq, or OpenRouter keys for provider-billed usage.",
716
+ "Choose Anthropic, OpenAI, OpenRouter, Gemini, Groq, Mistral, Cohere, or Ollama for provider-billed usage.",
718
717
  providerKeysSet: "{{count}} set",
718
+ providerActionTitle: "AI provider",
719
+ providerActionDescription:
720
+ "Builder.io includes a free tier, or use custom keys.",
721
+ providerManage: "Manage",
722
+ providerCustomKeys: "Custom keys",
719
723
  checkingProviderKeys: "Checking provider keys…",
720
724
  keySet: "Set",
721
725
  keyCleared: "Storage credentials cleared",
@@ -725,8 +729,7 @@ All notable user-facing changes to Clips are documented here. Open it any time f
725
729
  apiKeySaved: "API key saved",
726
730
  apiKeyFailed: "Failed to save key",
727
731
  slackTitle: "Agent-Native Clips for Slack",
728
- slackDescription:
729
- "Share a public clip, paste the link in Slack, and it plays inline — no extra steps for viewers. Connect each workspace once.",
732
+ slackDescription: "Preview public Clips links inline in Slack.",
730
733
  checkingSlack: "Checking Slack",
731
734
  slackConnected_one: "{{count}} workspace connected",
732
735
  slackConnected_other: "{{count}} workspaces connected",
@@ -1145,7 +1148,7 @@ All notable user-facing changes to Clips are documented here. Open it any time f
1145
1148
  "Didn't hear back from Builder in 5 minutes. Check the popup and try again.",
1146
1149
  builderConnected: "Builder.io connected",
1147
1150
  waitingForBuilder: "Waiting for Builder...",
1148
- connectBuilder: "Use Builder.io (free)",
1151
+ connectBuilder: "Use Builder.io",
1149
1152
  free: "Free",
1150
1153
  configureS3: "configure S3-compatible storage",
1151
1154
  whyPrompt: "Why am I seeing this?",
@@ -7689,7 +7689,7 @@ function Setup({
7689
7689
  className="secondary"
7690
7690
  onClick={connectBuilder}
7691
7691
  >
7692
- Use Builder.io (free)
7692
+ Use Builder.io
7693
7693
  </button>
7694
7694
  ) : null}
7695
7695
  </div>