@agent-native/core 0.161.17 → 0.161.20

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 (130) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/adding-a-feature/SKILL.md +10 -0
  3. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  4. package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +29 -2
  5. package/corpus/templates/analytics/AGENTS.md +1 -0
  6. package/corpus/templates/analytics/server/plugins/auth.ts +4 -0
  7. package/corpus/templates/assets/.agents/skills/adding-a-feature/SKILL.md +10 -0
  8. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  9. package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +29 -2
  10. package/corpus/templates/assets/AGENTS.md +1 -0
  11. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +10 -0
  12. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  13. package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +29 -2
  14. package/corpus/templates/brain/AGENTS.md +1 -0
  15. package/corpus/templates/calendar/.agents/skills/adding-a-feature/SKILL.md +10 -0
  16. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  17. package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +29 -2
  18. package/corpus/templates/calendar/AGENTS.md +1 -0
  19. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  20. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  21. package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  22. package/corpus/templates/chat/AGENTS.md +4 -0
  23. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  24. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +29 -2
  25. package/corpus/templates/clips/AGENTS.md +1 -0
  26. package/corpus/templates/clips/app/components/player/video-player.tsx +4 -0
  27. package/corpus/templates/clips/app/hooks/use-playback-position.ts +10 -2
  28. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +1 -0
  29. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +26 -25
  30. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -0
  31. package/corpus/templates/clips/changelog/2026-08-18-clips-no-longer-opens-welcome-window-on-first-launch.md +6 -0
  32. package/corpus/templates/clips/changelog/2026-08-18-pending-recording-files-now-use-a-clips-prefixed-name.md +6 -0
  33. package/corpus/templates/clips/desktop/src/main.tsx +0 -3
  34. package/corpus/templates/clips/desktop/src/shared/config.ts +0 -1
  35. package/corpus/templates/clips/desktop/src/styles.css +0 -118
  36. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +0 -1
  37. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +0 -45
  38. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +1 -4
  39. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +0 -10
  40. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +57 -5
  41. package/corpus/templates/content/.agents/skills/adding-a-feature/SKILL.md +10 -0
  42. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  43. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +29 -2
  44. package/corpus/templates/content/AGENTS.md +1 -0
  45. package/corpus/templates/crm/.agents/skills/adding-a-feature/SKILL.md +10 -0
  46. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  47. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +29 -2
  48. package/corpus/templates/crm/AGENTS.md +1 -0
  49. package/corpus/templates/design/.agents/skills/adding-a-feature/SKILL.md +10 -0
  50. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  51. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +29 -2
  52. package/corpus/templates/design/AGENTS.md +1 -0
  53. package/corpus/templates/design/app/i18n-data.ts +11 -0
  54. package/corpus/templates/design/app/lib/agent-chat.ts +9 -8
  55. package/corpus/templates/dispatch/.agents/skills/adding-a-feature/SKILL.md +10 -0
  56. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  57. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +29 -2
  58. package/corpus/templates/dispatch/AGENTS.md +5 -5
  59. package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +10 -0
  60. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  61. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +29 -2
  62. package/corpus/templates/factory/AGENTS.md +1 -0
  63. package/corpus/templates/forms/.agents/skills/adding-a-feature/SKILL.md +10 -0
  64. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  65. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +29 -2
  66. package/corpus/templates/forms/AGENTS.md +1 -0
  67. package/corpus/templates/macros/.agents/skills/adding-a-feature/SKILL.md +10 -0
  68. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  69. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +29 -2
  70. package/corpus/templates/macros/AGENTS.md +1 -0
  71. package/corpus/templates/mail/.agents/skills/adding-a-feature/SKILL.md +10 -0
  72. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  73. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +29 -2
  74. package/corpus/templates/mail/AGENTS.md +1 -0
  75. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +10 -0
  76. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  77. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +29 -2
  78. package/corpus/templates/plan/AGENTS.md +1 -0
  79. package/corpus/templates/slides/.agents/skills/adding-a-feature/SKILL.md +10 -0
  80. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  81. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +29 -2
  82. package/corpus/templates/slides/AGENTS.md +2 -1
  83. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +10 -0
  84. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  85. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +29 -2
  86. package/corpus/templates/tasks/AGENTS.md +1 -0
  87. package/dist/action.js +43 -1
  88. package/dist/client/chat/run-recovery.js +1 -1
  89. package/dist/client/route-state.js +17 -13
  90. package/dist/collab/routes.d.ts +1 -1
  91. package/dist/observability/routes.d.ts +2 -2
  92. package/dist/progress/routes.d.ts +1 -1
  93. package/dist/server/auth.d.ts +6 -0
  94. package/dist/server/auth.js +23 -5
  95. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  96. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  97. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  98. package/dist/templates/chat/AGENTS.md +4 -0
  99. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
  100. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  101. package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -2
  102. package/dist/templates/default/AGENTS.md +4 -0
  103. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  104. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
  105. package/dist/templates/headless/AGENTS.md +4 -0
  106. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
  107. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  108. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
  109. package/dist/templates/workspace-core/AGENTS.md +4 -0
  110. package/dist/templates/workspace-root/AGENTS.md +9 -0
  111. package/docs/content/deployment.mdx +70 -0
  112. package/docs/content/faq.mdx +1 -1
  113. package/package.json +1 -1
  114. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  115. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  116. package/src/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  117. package/src/templates/chat/AGENTS.md +4 -0
  118. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
  119. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  120. package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -2
  121. package/src/templates/default/AGENTS.md +4 -0
  122. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  123. package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
  124. package/src/templates/headless/AGENTS.md +4 -0
  125. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
  126. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  127. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
  128. package/src/templates/workspace-core/AGENTS.md +4 -0
  129. package/src/templates/workspace-root/AGENTS.md +9 -0
  130. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +0 -314
@@ -183,6 +183,8 @@ export interface VideoPlayerProps {
183
183
  defaultSpeed?: number;
184
184
  /** Autoplay on mount. */
185
185
  autoPlay?: boolean;
186
+ /** Persist resume position for an authenticated viewer. */
187
+ persistPlaybackPosition?: boolean;
186
188
  /** Start time in ms. */
187
189
  startMs?: number;
188
190
  /** Comment + chapter overlays for the scrubber. */
@@ -247,6 +249,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
247
249
  thumbnailUrl,
248
250
  defaultSpeed = 1.2,
249
251
  autoPlay,
252
+ persistPlaybackPosition = true,
250
253
  startMs,
251
254
  editsJson,
252
255
  comments,
@@ -520,6 +523,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
520
523
  recordingId,
521
524
  videoEl: playbackVideoEl,
522
525
  durationMs,
526
+ enabled: persistPlaybackPosition,
523
527
  explicitStartMs: startMs,
524
528
  allowRestoreWhilePlaying: autoPlay,
525
529
  onRestore: restorePlaybackPosition,
@@ -13,6 +13,7 @@ export interface UsePlaybackPositionOptions {
13
13
  recordingId: string;
14
14
  videoEl: HTMLVideoElement | null;
15
15
  durationMs: number;
16
+ enabled?: boolean;
16
17
  explicitStartMs?: number;
17
18
  allowRestoreWhilePlaying?: boolean;
18
19
  onRestore?: (positionMs: number) => void;
@@ -27,6 +28,7 @@ export function usePlaybackPosition({
27
28
  recordingId,
28
29
  videoEl,
29
30
  durationMs,
31
+ enabled = true,
30
32
  explicitStartMs,
31
33
  allowRestoreWhilePlaying = false,
32
34
  onRestore,
@@ -38,7 +40,7 @@ export function usePlaybackPosition({
38
40
  onRestoreRef.current = onRestore;
39
41
 
40
42
  useEffect(() => {
41
- if (!videoEl || !recordingId) return;
43
+ if (!enabled || !videoEl || !recordingId) return;
42
44
 
43
45
  const controller = new AbortController();
44
46
  let cancelled = false;
@@ -184,5 +186,11 @@ export function usePlaybackPosition({
184
186
  window.removeEventListener("pagehide", onPageHide);
185
187
  document.removeEventListener("visibilitychange", onVisibilityChange);
186
188
  };
187
- }, [allowRestoreWhilePlaying, explicitStartMs, recordingId, videoEl]);
189
+ }, [
190
+ allowRestoreWhilePlaying,
191
+ enabled,
192
+ explicitStartMs,
193
+ recordingId,
194
+ videoEl,
195
+ ]);
188
196
  }
@@ -236,6 +236,7 @@ export default function EmbedRoute() {
236
236
  videoFormat={recording.videoFormat}
237
237
  embedProvider={isLoomEmbedBacked ? "loom" : null}
238
238
  durationMs={recording.durationMs}
239
+ persistPlaybackPosition={false}
239
240
  editsJson={recording.editsJson}
240
241
  thumbnailUrl={recording.thumbnailUrl}
241
242
  defaultSpeed={parsePlaybackSpeed(recording.defaultSpeed) ?? 1.2}
@@ -187,6 +187,30 @@ function nativeSaveFailureMessage(reason: string | null | undefined): string {
187
187
  return "The desktop recorder finished and saved a local copy, but Clips could not upload it. You can retry from the Clips menu without recording again.";
188
188
  }
189
189
 
190
+ export function BackToLibraryButton() {
191
+ const t = useT();
192
+ const navigate = useNavigate();
193
+
194
+ return (
195
+ <Tooltip>
196
+ <TooltipTrigger asChild>
197
+ <Button
198
+ variant="ghost"
199
+ size="icon"
200
+ className="shrink-0"
201
+ onClick={() => navigate("/library", { replace: true })}
202
+ aria-label={t("recordingPage.backToLibrary")}
203
+ >
204
+ <IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
205
+ </Button>
206
+ </TooltipTrigger>
207
+ <TooltipContent side="bottom" align="start">
208
+ {t("recordingPage.backToLibrary")}
209
+ </TooltipContent>
210
+ </Tooltip>
211
+ );
212
+ }
213
+
190
214
  function parseTimeParam(raw: string | null): number {
191
215
  if (!raw) return 0;
192
216
  const value = raw.trim();
@@ -965,17 +989,7 @@ export default function RecordingPage() {
965
989
  return (
966
990
  <div className="flex min-h-screen w-full flex-col bg-background">
967
991
  <header className="flex min-w-0 shrink-0 items-center gap-3 border-b border-border px-3 py-2 sm:px-4 sm:py-3">
968
- <Button
969
- asChild
970
- variant="ghost"
971
- size="icon"
972
- className="shrink-0"
973
- aria-label={t("recordingPage.backToLibrary")}
974
- >
975
- <Link to="/library" replace>
976
- <IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
977
- </Link>
978
- </Button>
992
+ <BackToLibraryButton />
979
993
  <div className="min-w-0 flex-1">
980
994
  <p className="truncate text-sm font-medium">{visibleTitle}</p>
981
995
  <p className="truncate text-xs text-muted-foreground">
@@ -1294,20 +1308,7 @@ export default function RecordingPage() {
1294
1308
  {/* Main video column */}
1295
1309
  <div className="flex w-full min-w-0 flex-col xl:flex-1">
1296
1310
  <header className="flex min-w-0 shrink-0 items-center gap-2 border-b border-border px-3 py-2 sm:gap-3 sm:px-4 sm:py-3">
1297
- <Button
1298
- asChild
1299
- variant="ghost"
1300
- size="icon"
1301
- className="shrink-0 sm:w-auto sm:px-2"
1302
- aria-label={t("recordingPage.backToLibrary")}
1303
- >
1304
- <Link to="/library" replace>
1305
- <IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
1306
- <span className="hidden sm:inline">
1307
- {t("recordingPage.backToLibrary")}
1308
- </span>
1309
- </Link>
1310
- </Button>
1311
+ <BackToLibraryButton />
1311
1312
  <div className="flex-1 min-w-0">
1312
1313
  <EditableRecordingTitle
1313
1314
  recordingId={recording.id}
@@ -1226,6 +1226,7 @@ export default function ShareRoute() {
1226
1226
  videoFormat={recording.videoFormat}
1227
1227
  embedProvider={isLoomEmbedBacked ? "loom" : null}
1228
1228
  durationMs={recording.durationMs}
1229
+ persistPlaybackPosition={Boolean(session)}
1229
1230
  editsJson={recording.editsJson}
1230
1231
  thumbnailUrl={recording.thumbnailUrl}
1231
1232
  role={viewerRole ?? (viewerCanEdit ? "owner" : "viewer")}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: removed
3
+ date: 2026-08-18
4
+ ---
5
+
6
+ Clips no longer opens a welcome window on first launch.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-18
4
+ ---
5
+
6
+ Pending recording files now use a Clips-prefixed name instead of a feature-ambiguous stem.
@@ -11,7 +11,6 @@ import { FlowBar } from "./overlays/flow-bar";
11
11
  import { MeetingNotification } from "./overlays/meeting-notification";
12
12
  import { MeetingNub } from "./overlays/meeting-nub";
13
13
  import { MonitorPicker } from "./overlays/monitor-picker";
14
- import { Onboarding } from "./overlays/onboarding";
15
14
  import { Preparing } from "./overlays/preparing";
16
15
  import { RecordingPill } from "./overlays/recording-pill";
17
16
  import { RegionGuideEditor, RegionGuides } from "./overlays/region-guides";
@@ -47,8 +46,6 @@ function pickRoute(route: string): React.ReactElement {
47
46
  return <Finalizing />;
48
47
  case "preparing":
49
48
  return <Preparing />;
50
- case "onboarding":
51
- return <Onboarding />;
52
49
  case "meeting-notif":
53
50
  return <MeetingNotification />;
54
51
  case "meeting-nub":
@@ -129,7 +129,6 @@ export interface FeatureConfig {
129
129
  showInScreenCapture: boolean;
130
130
  regionGuides: RegionGuidesConfig;
131
131
  screenMemory: ScreenMemoryConfig;
132
- onboardingComplete: boolean;
133
132
  whisperModelEnabled: boolean;
134
133
  whisperModelId: string;
135
134
  }
@@ -5263,124 +5263,6 @@ body[data-clips-route="recording-pill"] #root {
5263
5263
  }
5264
5264
  }
5265
5265
 
5266
- /* ------------------------------------------------------------------------- */
5267
- /* Overlay: onboarding */
5268
- /* ------------------------------------------------------------------------- */
5269
-
5270
- .onboarding-root {
5271
- position: fixed;
5272
- inset: 0;
5273
- display: flex;
5274
- align-items: center;
5275
- justify-content: center;
5276
- background: #212121;
5277
- color: #f5f5f5;
5278
- }
5279
-
5280
- .onboarding-card {
5281
- display: flex;
5282
- flex-direction: column;
5283
- align-items: center;
5284
- gap: 20px;
5285
- padding: 40px 36px;
5286
- max-width: 380px;
5287
- width: 100%;
5288
- }
5289
-
5290
- .onboarding-title {
5291
- font-size: 22px;
5292
- font-weight: 700;
5293
- color: white;
5294
- margin: 0;
5295
- letter-spacing: -0.3px;
5296
- }
5297
-
5298
- .onboarding-subtitle {
5299
- font-size: 14px;
5300
- color: #a3a3a3;
5301
- margin: -8px 0 0;
5302
- }
5303
-
5304
- .onboarding-features {
5305
- display: flex;
5306
- flex-direction: column;
5307
- gap: 10px;
5308
- width: 100%;
5309
- }
5310
-
5311
- .onboarding-feature {
5312
- display: flex;
5313
- align-items: flex-start;
5314
- gap: 12px;
5315
- padding: 14px 16px;
5316
- background: rgba(255, 255, 255, 0.04);
5317
- border: 1px solid rgba(255, 255, 255, 0.08);
5318
- border-radius: 12px;
5319
- cursor: pointer;
5320
- user-select: none;
5321
- }
5322
-
5323
- .onboarding-feature:hover {
5324
- background: rgba(255, 255, 255, 0.06);
5325
- }
5326
-
5327
- .onboarding-checkbox {
5328
- width: 18px;
5329
- height: 18px;
5330
- margin: 1px 0 0;
5331
- accent-color: #16a34a;
5332
- cursor: pointer;
5333
- flex-shrink: 0;
5334
- }
5335
-
5336
- .onboarding-feature-text {
5337
- display: flex;
5338
- flex-direction: column;
5339
- gap: 3px;
5340
- }
5341
-
5342
- .onboarding-feature-name {
5343
- font-size: 14px;
5344
- font-weight: 600;
5345
- color: white;
5346
- }
5347
-
5348
- .onboarding-feature-desc {
5349
- font-size: 12px;
5350
- color: #a3a3a3;
5351
- line-height: 1.4;
5352
- }
5353
-
5354
- .onboarding-cta {
5355
- width: 100%;
5356
- border: none;
5357
- border-radius: 12px;
5358
- padding: 12px 16px;
5359
- background: #f5f5f5;
5360
- color: #212121;
5361
- font-size: 14px;
5362
- font-weight: 600;
5363
- cursor: pointer;
5364
- height: 44px;
5365
- display: inline-flex;
5366
- align-items: center;
5367
- justify-content: center;
5368
- margin-top: 4px;
5369
- }
5370
-
5371
- .onboarding-cta:hover {
5372
- background: #e5e5e5;
5373
- }
5374
-
5375
- .onboarding-cta:active {
5376
- transform: translateY(1px);
5377
- }
5378
-
5379
- .onboarding-cta:disabled {
5380
- opacity: 0.6;
5381
- cursor: not-allowed;
5382
- }
5383
-
5384
5266
  /* ------------------------------------------------------------------------- */
5385
5267
  /* Overlay: meeting notification */
5386
5268
  /* ------------------------------------------------------------------------- */
@@ -9,7 +9,6 @@
9
9
  "bubble",
10
10
  "signin",
11
11
  "finalizing",
12
- "onboarding",
13
12
  "meeting-notif",
14
13
  "meeting-nub",
15
14
  "flow-bar",
@@ -48,7 +48,6 @@ const REGION_GUIDES_LABEL: &str = "region-guides";
48
48
  const REGION_GUIDE_EDITOR_LABEL: &str = "region-guide-editor";
49
49
  const REGION_RECORD_BORDER_LABEL: &str = "region-record-border";
50
50
  const MONITOR_PICKER_LABEL_PREFIX: &str = "monitor-picker-";
51
- const ONBOARDING_LABEL: &str = "onboarding";
52
51
  const OVERLAY_LABELS: &[&str] = &[
53
52
  COUNTDOWN_LABEL,
54
53
  TOOLBAR_LABEL,
@@ -59,8 +58,6 @@ const OVERLAY_LABELS: &[&str] = &[
59
58
  REGION_GUIDES_LABEL,
60
59
  REGION_RECORD_BORDER_LABEL,
61
60
  ];
62
- const ONBOARDING_WIDTH_LOGICAL: f64 = 560.0;
63
- const ONBOARDING_HEIGHT_LOGICAL: f64 = 640.0;
64
61
 
65
62
  /// Physical-pixel bubble sizes. Logical px on retina = physical / 2, so these
66
63
  /// map to ~96 (small) and ~180 (medium) logical px — matching Loom's camera
@@ -599,48 +596,6 @@ pub async fn hide_finalizing(app: AppHandle) -> Result<(), String> {
599
596
  Ok(())
600
597
  }
601
598
 
602
- /// First-run onboarding window (ONBOARD-WINDOW). Unlike the transparent,
603
- /// click-through overlays above, this is a normal decorated, focused window
604
- /// with its own solid dark background (`.onboarding-root` in styles.css) —
605
- /// centered on the primary display so it reads as a real app window, not a
606
- /// HUD. Called once from `lib.rs`'s `setup()` when `onboarding_complete` is
607
- /// false. Reuses an existing window if one is somehow already open (e.g. a
608
- /// hot-reload re-triggering setup in dev) instead of building a second one.
609
- pub fn show_onboarding_window(app: &AppHandle) {
610
- if let Some(existing) = app.get_webview_window(ONBOARDING_LABEL) {
611
- let _ = existing.show();
612
- let _ = existing.set_focus();
613
- return;
614
- }
615
- let win =
616
- match WebviewWindowBuilder::new(app, ONBOARDING_LABEL, build_overlay_url("onboarding"))
617
- .title("Welcome to Clips")
618
- .inner_size(ONBOARDING_WIDTH_LOGICAL, ONBOARDING_HEIGHT_LOGICAL)
619
- .resizable(false)
620
- .center()
621
- .focused(true)
622
- .build()
623
- {
624
- Ok(w) => w,
625
- Err(e) => {
626
- eprintln!("[clips-tray] onboarding window build failed: {}", e);
627
- return;
628
- }
629
- };
630
- let _ = win.show();
631
- }
632
-
633
- /// Close the first-run onboarding window once the overlay's finish handler
634
- /// has saved feature config + opened the popover. Idempotent — closing an
635
- /// already-closed/never-built window is a no-op.
636
- #[tauri::command]
637
- pub async fn hide_onboarding_window(app: AppHandle) -> Result<(), String> {
638
- if let Some(w) = app.get_webview_window(ONBOARDING_LABEL) {
639
- let _ = w.close();
640
- }
641
- Ok(())
642
- }
643
-
644
599
  /// Full-screen, click-through recording guides. The React view renders the
645
600
  /// saved translucent rectangles, while AppKit marks the whole overlay as
646
601
  /// non-shareable so the guides stay private to the recorder.
@@ -134,7 +134,6 @@ pub struct FeatureConfig {
134
134
  pub region_guides: RegionGuidesConfig,
135
135
  #[serde(default)]
136
136
  pub screen_memory: ScreenMemoryConfig,
137
- pub onboarding_complete: bool,
138
137
  #[serde(default = "default_whisper_model_enabled")]
139
138
  pub whisper_model_enabled: bool,
140
139
  #[serde(default = "default_whisper_model_id")]
@@ -238,7 +237,6 @@ impl Default for FeatureConfig {
238
237
  show_in_screen_capture: false,
239
238
  region_guides: RegionGuidesConfig::default(),
240
239
  screen_memory: ScreenMemoryConfig::default(),
241
- onboarding_complete: false,
242
240
  whisper_model_enabled: default_whisper_model_enabled(),
243
241
  whisper_model_id: default_whisper_model_id(),
244
242
  }
@@ -437,8 +435,7 @@ mod tests {
437
435
  let config: FeatureConfig = serde_json::from_value(serde_json::json!({
438
436
  "clipsEnabled": true,
439
437
  "meetingsEnabled": true,
440
- "voiceEnabled": true,
441
- "onboardingComplete": true
438
+ "voiceEnabled": true
442
439
  }))
443
440
  .unwrap();
444
441
 
@@ -116,7 +116,6 @@ pub fn run() {
116
116
  clips::close_signin,
117
117
  clips::show_flow_bar,
118
118
  clips::hide_flow_bar,
119
- clips::hide_onboarding_window,
120
119
  clips::complete_voice_dictation,
121
120
  clips::paste_last_dictation,
122
121
  clips::set_recording_state,
@@ -343,15 +342,6 @@ pub fn run() {
343
342
  tray::build_tray(app)?;
344
343
  config::sync_launch_at_login(app.handle());
345
344
  let feature_config = config::feature_config(app.handle());
346
- // ONBOARD-WINDOW: first-run onboarding never had a Rust-side
347
- // window to appear in — the `#onboarding` hash route and overlay
348
- // component existed, but nothing ever called
349
- // WebviewWindowBuilder for it. Build it here, after the popover
350
- // exists, gated on the persisted `onboarding_complete` flag so
351
- // returning users never see it again.
352
- if !feature_config.onboarding_complete {
353
- clips::show_onboarding_window(app.handle());
354
- }
355
345
  screen_memory::sync_from_config(app.handle(), &feature_config);
356
346
  // Re-show always-on region guides after relaunch/reboot when the
357
347
  // setting is on (no-op if a recording owns the window or the
@@ -270,6 +270,19 @@ mod native_upload_mode_tests {
270
270
  }
271
271
  }
272
272
 
273
+ #[cfg(test)]
274
+ mod pending_recording_file_name_tests {
275
+ use super::pending_recording_file_stem;
276
+
277
+ #[test]
278
+ fn uses_a_clips_specific_pending_recording_prefix() {
279
+ assert_eq!(
280
+ pending_recording_file_stem("FxGL6zX7HWhU", 4242),
281
+ "clips-pending-recording-FxGL6zX7HWhU-4242",
282
+ );
283
+ }
284
+ }
285
+
273
286
  #[derive(Default)]
274
287
  pub struct NativeFullscreenRecordingState {
275
288
  inner: Mutex<Option<NativeFullscreenSession>>,
@@ -2764,14 +2777,26 @@ fn discard_session(session: &mut NativeFullscreenSession) {
2764
2777
 
2765
2778
  /// Sibling path next to the original pending recording, numbered with
2766
2779
  /// the segment counter so multiple resume cycles don't clobber each
2767
- /// other. Example: `clips-fullscreen-<id>-<pid>-seg2.mp4`.
2780
+ /// other. Example: `clips-pending-recording-<id>-<pid>-seg2.mp4`.
2768
2781
  fn segment_path_for(
2769
2782
  app: &AppHandle,
2770
2783
  safe_id: &str,
2771
2784
  extension: &str,
2772
2785
  counter: u32,
2773
2786
  ) -> Result<PathBuf, String> {
2774
- pending_recording_path(app, &format!("{safe_id}-seg{counter}"), extension)
2787
+ let base_path = pending_recording_path(app, safe_id, extension)?;
2788
+ let base_stem = base_path
2789
+ .file_stem()
2790
+ .and_then(|value| value.to_str())
2791
+ .ok_or_else(|| "pending recording path has no valid file stem".to_string())?;
2792
+ Ok(base_path.with_file_name(format!(
2793
+ "{base_stem}-seg{counter}.{}",
2794
+ extension.trim_start_matches('.')
2795
+ )))
2796
+ }
2797
+
2798
+ fn pending_recording_file_stem(safe_id: &str, pid: u32) -> String {
2799
+ format!("clips-pending-recording-{safe_id}-{pid}")
2775
2800
  }
2776
2801
 
2777
2802
  /// Dispatches to the right backend starter for resume. Mirrors the
@@ -3202,7 +3227,9 @@ fn orphan_recording_id(path: &Path) -> Option<String> {
3202
3227
  return None;
3203
3228
  }
3204
3229
  let stem = path.file_stem().and_then(|value| value.to_str())?;
3205
- let value = if let Some(value) = stem.strip_prefix("rewind-") {
3230
+ let value = if let Some(value) = stem.strip_prefix("clips-pending-recording-") {
3231
+ value
3232
+ } else if let Some(value) = stem.strip_prefix("rewind-") {
3206
3233
  value
3207
3234
  } else if let Some(value) = stem.strip_prefix("clips-fullscreen-") {
3208
3235
  value
@@ -3525,15 +3552,40 @@ mod orphan_recovery_tests {
3525
3552
  orphan_recording_id(Path::new("clips-fullscreen-e4esSx9NZCZa-1234.mov")),
3526
3553
  Some("e4esSx9NZCZa".into())
3527
3554
  );
3555
+ assert_eq!(
3556
+ orphan_recording_id(Path::new("clips-pending-recording-e4esSx9NZCZa-1234.mp4")),
3557
+ Some("e4esSx9NZCZa".into())
3558
+ );
3528
3559
  assert!(is_recording_segment_path(Path::new(
3529
3560
  "clips-fullscreen-e4esSx9NZCZa-1234-seg2.mp4"
3530
3561
  )));
3562
+ assert!(is_recording_segment_path(Path::new(
3563
+ "clips-pending-recording-e4esSx9NZCZa-1234-seg2.mp4"
3564
+ )));
3531
3565
  assert!(
3532
3566
  orphan_recording_id(Path::new("clips-fullscreen-e4esSx9NZCZa-1234-seg2.mp4")).is_none()
3533
3567
  );
3568
+ assert!(orphan_recording_id(Path::new(
3569
+ "clips-pending-recording-e4esSx9NZCZa-1234-seg2.mp4"
3570
+ ))
3571
+ .is_none());
3534
3572
  assert!(orphan_recording_id(Path::new("unrelated.mp4")).is_none());
3535
3573
  }
3536
3574
 
3575
+ #[test]
3576
+ fn recovers_new_pending_recording_name_without_intent_sidecar() {
3577
+ let intent = orphan_recording_intent(
3578
+ Path::new("clips-pending-recording-recording-123-456.mp4"),
3579
+ "https://clips.example.test/",
3580
+ )
3581
+ .unwrap()
3582
+ .unwrap();
3583
+
3584
+ assert_eq!(intent.recording_id, "recording-123");
3585
+ assert_eq!(intent.server_url, "https://clips.example.test");
3586
+ assert!(!intent.custom_pipeline);
3587
+ }
3588
+
3537
3589
  #[test]
3538
3590
  fn intent_sidecars_round_trip_and_map_back_to_media() {
3539
3591
  let root = std::env::temp_dir().join(format!(
@@ -4167,8 +4219,8 @@ fn pending_recording_path(
4167
4219
  extension: &str,
4168
4220
  ) -> Result<PathBuf, String> {
4169
4221
  Ok(pending_uploads_dir(app)?.join(format!(
4170
- "clips-fullscreen-{safe_id}-{}.{}",
4171
- std::process::id(),
4222
+ "{}.{}",
4223
+ pending_recording_file_stem(safe_id, std::process::id()),
4172
4224
  extension.trim_start_matches('.')
4173
4225
  )))
4174
4226
  }
@@ -76,6 +76,16 @@ Builder/internal data, or customer data in the action, UI, seed data, fixtures,
76
76
  docs, prompts, or generated extension/app content. Register required secrets,
77
77
  use OAuth helpers, or read scoped values from the vault/credential store.
78
78
 
79
+ Before writing custom setup or credential UI, perform the shared-primitive
80
+ preflight from `agent-native-toolkit`: inspect the workspace/provider connection
81
+ catalog first, then settings, secrets/vault, OAuth, onboarding, and provider API
82
+ support. Reuse an existing connection's app grant and scoped credential
83
+ resolver; only use app-local vault or OAuth primitives when no reusable
84
+ connection exists. Keep any wizard thin and provider-specific - it may explain
85
+ prerequisites, ordering, or readiness, but must not become a second vault or
86
+ custom credential transport. Do not mark each field `required` by reflex;
87
+ model onboarding around the logical connection outcome.
88
+
79
89
  If the feature involves attachments, images, recordings, screenshots, exports,
80
90
  or other file-like payloads, design the upload path in the same change:
81
91
  provider upload first, then URL/id/blob handle in SQL. Do not add base64/binary
@@ -152,6 +152,30 @@ preference, notification preference, or usage/billing surface, register it as a
152
152
  settings tab or app settings panel first. Only add sidebar UI when it is needed
153
153
  in the moment of agent use.
154
154
 
155
+ ## Integration Setup Preflight
156
+
157
+ Before building any setup, settings, credential, OAuth, or connection surface,
158
+ search the workspace/provider connection catalog first. If the provider already
159
+ has a reusable connection, use its catalog, app grant, and scoped credential
160
+ resolver rather than registering a parallel secret. Only then classify fields
161
+ that still need app-local setup by lifecycle and scope:
162
+
163
+ | Need | Default primitive |
164
+ | --- | --- |
165
+ | Deploy- or app-level configuration | Runtime configuration or deployment env vars |
166
+ | Existing workspace/provider connection | Workspace-connection catalog/grant plus `resolveWorkspaceConnectionCredential(s)ForApp` |
167
+ | App-local API/service key with no reusable connection | `registerRequiredSecret({ kind: "api-key" })` and the vault |
168
+ | Authorization-code or refresh-token flow | `kind: "oauth"` with `@agent-native/core/oauth-tokens` |
169
+ | Account, customer, or other non-secret identifiers | Scoped connection metadata or app data |
170
+ | Provider-specific prerequisites, sequencing, or health | A thin app-local guide over the shared primitives |
171
+
172
+ Do not register every provider field as a generic secret, mark every field as
173
+ required, or create a second credential-management surface. One logical
174
+ connection should normally produce one onboarding outcome. A custom setup page
175
+ is appropriate only when it adds domain-specific guidance or readiness checks;
176
+ it should link to or call the shared settings, OAuth, and action surfaces rather
177
+ than duplicating their storage or transport.
178
+
155
179
  ## Reusable Kits
156
180
 
157
181
  - **Settings kit**: a searchable settings page with account, workspace, AI
@@ -3,8 +3,8 @@ name: secrets
3
3
  description: >-
4
4
  Declaratively register API keys and service credentials a template needs so
5
5
  they appear in the agent sidebar settings UI and the onboarding checklist.
6
- Use for any third-party API key (OpenAI, Stripe, Twilio, etc.) and for
7
- surfacing OAuth connections in the unified settings UI.
6
+ Use before adding any third-party credential or setup UI so API keys, OAuth
7
+ connections, and scoped configuration use the correct shared primitive.
8
8
  scope: dev
9
9
  metadata:
10
10
  internal: true
@@ -24,6 +24,33 @@ encrypted `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
24
  `${keys.NAME}` substitution. Examples must use obvious placeholders such as
25
25
  `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not real-looking copied values.
26
26
 
27
+ ## Credential Modeling Preflight
28
+
29
+ Before registering a provider's fields, inspect the workspace/provider connection
30
+ catalog first. If a reusable connection exists, use its app grant and scoped
31
+ `resolveWorkspaceConnectionCredential(s)ForApp` path instead of registering a
32
+ parallel secret. Only classify fields for app-local setup when no reusable
33
+ connection exists:
34
+
35
+ - **API or service key** - register it as `kind: "api-key"` with the narrowest
36
+ correct `scope`, a human label, a description, a docs link, and a validator.
37
+ - **OAuth authorization or refresh token** - use the OAuth token store and
38
+ register a `kind: "oauth"` entry so the shared UI renders Connect and the
39
+ runtime owns status, refresh, and reauthorization.
40
+ - **Deploy- or app-level configuration** - use deployment/runtime
41
+ configuration, not a per-user secret row.
42
+ - **Account, customer, manager, or other non-secret identifier** - store it as
43
+ scoped connection metadata or app data, not as a masked secret field.
44
+
45
+ `required: true` is for a logical setup requirement. If a provider needs
46
+ several values, do not automatically create one required checklist item per
47
+ field; use one composite onboarding step or a registered connection readiness
48
+ check.
49
+
50
+ Custom setup UI is allowed for provider-specific prerequisites, ordering, or
51
+ health checks, but it must delegate credential storage and connection state to
52
+ the shared vault/OAuth/settings surfaces.
53
+
27
54
  ## When to use
28
55
 
29
56
  Use this for any external credential your template needs: API keys, service
@@ -49,6 +49,7 @@ Read the relevant skill before deeper work:
49
49
  thumbnails, or replay chunks in app tables, `application_state`, `settings`,
50
50
  or `resources`; persist URLs, ids, or handles instead.
51
51
  - Never hardcode API keys, tokens, webhook URLs, signing secrets, private Builder/internal data, customer data, or credential-looking literals. Use secrets/OAuth/runtime configuration and obvious placeholders in examples.
52
+ - For external integrations, inspect the workspace/provider connection catalog first; reuse its scoped resolver.
52
53
 
53
54
  ## Application State
54
55