@agent-native/core 0.161.16 → 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 (148) 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/chat/vitest.config.ts +12 -7
  24. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  25. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +29 -2
  26. package/corpus/templates/clips/AGENTS.md +1 -0
  27. package/corpus/templates/clips/app/components/player/video-player.tsx +4 -0
  28. package/corpus/templates/clips/app/hooks/use-playback-position.ts +10 -2
  29. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +1 -0
  30. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +26 -25
  31. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -0
  32. package/corpus/templates/clips/changelog/2026-08-18-clips-no-longer-opens-welcome-window-on-first-launch.md +6 -0
  33. package/corpus/templates/clips/changelog/2026-08-18-clips-now-recovers-interrupted-uploads-and-reliably-restarts.md +6 -0
  34. package/corpus/templates/clips/changelog/2026-08-18-meeting-notifications-no-longer-show-decorative-left-side-lines.md +6 -0
  35. package/corpus/templates/clips/changelog/2026-08-18-pending-recording-files-now-use-a-clips-prefixed-name.md +6 -0
  36. package/corpus/templates/clips/desktop/src/lib/updater.ts +21 -1
  37. package/corpus/templates/clips/desktop/src/main.tsx +0 -3
  38. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +0 -4
  39. package/corpus/templates/clips/desktop/src/shared/config.ts +0 -1
  40. package/corpus/templates/clips/desktop/src/styles.css +0 -136
  41. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +0 -1
  42. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +0 -45
  43. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +1 -4
  44. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -11
  45. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +63 -8
  46. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +98 -0
  47. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +54 -20
  48. package/corpus/templates/content/.agents/skills/adding-a-feature/SKILL.md +10 -0
  49. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  50. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +29 -2
  51. package/corpus/templates/content/AGENTS.md +1 -0
  52. package/corpus/templates/crm/.agents/skills/adding-a-feature/SKILL.md +10 -0
  53. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  54. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +29 -2
  55. package/corpus/templates/crm/AGENTS.md +1 -0
  56. package/corpus/templates/design/.agents/skills/adding-a-feature/SKILL.md +10 -0
  57. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  58. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +29 -2
  59. package/corpus/templates/design/AGENTS.md +1 -0
  60. package/corpus/templates/design/app/global.css +6 -0
  61. package/corpus/templates/design/app/i18n-data.ts +11 -0
  62. package/corpus/templates/design/app/lib/agent-chat.ts +9 -8
  63. package/corpus/templates/design/app/root.tsx +1 -1
  64. package/corpus/templates/design/changelog/2026-08-18-design-chat-context-stays-out-of-the-composer.md +6 -0
  65. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  66. package/corpus/templates/dispatch/.agents/skills/adding-a-feature/SKILL.md +10 -0
  67. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  68. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +29 -2
  69. package/corpus/templates/dispatch/AGENTS.md +5 -5
  70. package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +10 -0
  71. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  72. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +29 -2
  73. package/corpus/templates/factory/AGENTS.md +1 -0
  74. package/corpus/templates/forms/.agents/skills/adding-a-feature/SKILL.md +10 -0
  75. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  76. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +29 -2
  77. package/corpus/templates/forms/AGENTS.md +1 -0
  78. package/corpus/templates/macros/.agents/skills/adding-a-feature/SKILL.md +10 -0
  79. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  80. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +29 -2
  81. package/corpus/templates/macros/AGENTS.md +1 -0
  82. package/corpus/templates/mail/.agents/skills/adding-a-feature/SKILL.md +10 -0
  83. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  84. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +29 -2
  85. package/corpus/templates/mail/AGENTS.md +1 -0
  86. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +10 -0
  87. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  88. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +29 -2
  89. package/corpus/templates/plan/AGENTS.md +1 -0
  90. package/corpus/templates/slides/.agents/skills/adding-a-feature/SKILL.md +10 -0
  91. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  92. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +29 -2
  93. package/corpus/templates/slides/AGENTS.md +2 -1
  94. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +10 -0
  95. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  96. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +29 -2
  97. package/corpus/templates/tasks/AGENTS.md +1 -0
  98. package/dist/action.d.ts +1 -0
  99. package/dist/action.js +44 -2
  100. package/dist/agent/production-agent.js +28 -5
  101. package/dist/client/chat/run-recovery.js +1 -1
  102. package/dist/client/route-state.js +17 -13
  103. package/dist/collab/struct-routes.d.ts +1 -1
  104. package/dist/notifications/routes.d.ts +1 -1
  105. package/dist/observability/routes.d.ts +5 -5
  106. package/dist/secrets/routes.d.ts +3 -3
  107. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  108. package/dist/server/auth.d.ts +6 -0
  109. package/dist/server/auth.js +23 -5
  110. package/dist/server/realtime-token.d.ts +1 -1
  111. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  112. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  113. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  114. package/dist/templates/chat/AGENTS.md +4 -0
  115. package/dist/templates/chat/vitest.config.ts +12 -7
  116. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
  117. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  118. package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -2
  119. package/dist/templates/default/AGENTS.md +4 -0
  120. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  121. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
  122. package/dist/templates/headless/AGENTS.md +4 -0
  123. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
  124. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  125. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
  126. package/dist/templates/workspace-core/AGENTS.md +4 -0
  127. package/dist/templates/workspace-root/AGENTS.md +9 -0
  128. package/docs/content/deployment.mdx +70 -0
  129. package/docs/content/faq.mdx +1 -1
  130. package/package.json +1 -1
  131. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  132. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  133. package/src/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  134. package/src/templates/chat/AGENTS.md +4 -0
  135. package/src/templates/chat/vitest.config.ts +12 -7
  136. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
  137. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  138. package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -2
  139. package/src/templates/default/AGENTS.md +4 -0
  140. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  141. package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
  142. package/src/templates/headless/AGENTS.md +4 -0
  143. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
  144. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  145. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
  146. package/src/templates/workspace-core/AGENTS.md +4 -0
  147. package/src/templates/workspace-root/AGENTS.md +9 -0
  148. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +0 -314
@@ -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!(
@@ -4055,7 +4107,11 @@ fn recording_file_has_bytes(path: &Path) -> bool {
4055
4107
  }
4056
4108
 
4057
4109
  fn remove_empty_recording_artifacts(session: &NativeFullscreenSession) {
4058
- for path in session.segments.iter().chain(std::iter::once(&session.path)) {
4110
+ for path in session
4111
+ .segments
4112
+ .iter()
4113
+ .chain(std::iter::once(&session.path))
4114
+ {
4059
4115
  if !path.exists() {
4060
4116
  continue;
4061
4117
  }
@@ -4163,8 +4219,8 @@ fn pending_recording_path(
4163
4219
  extension: &str,
4164
4220
  ) -> Result<PathBuf, String> {
4165
4221
  Ok(pending_uploads_dir(app)?.join(format!(
4166
- "clips-fullscreen-{safe_id}-{}.{}",
4167
- std::process::id(),
4222
+ "{}.{}",
4223
+ pending_recording_file_stem(safe_id, std::process::id()),
4168
4224
  extension.trim_start_matches('.')
4169
4225
  )))
4170
4226
  }
@@ -4495,8 +4551,7 @@ fn write_bytes_atomically(path: &Path, data: &[u8], label: &str) -> Result<(), S
4495
4551
  .and_then(|value| value.to_str())
4496
4552
  .unwrap_or("recording");
4497
4553
  let temporary = path.with_file_name(format!(".{file_name}.tmp"));
4498
- std::fs::write(&temporary, data)
4499
- .map_err(|error| format!("{label} write failed: {error}"))?;
4554
+ std::fs::write(&temporary, data).map_err(|error| format!("{label} write failed: {error}"))?;
4500
4555
  if let Err(error) = std::fs::rename(&temporary, path) {
4501
4556
  let _ = std::fs::remove_file(&temporary);
4502
4557
  return Err(format!("{label} finalize failed: {error}"));
@@ -1,3 +1,9 @@
1
+ use std::{
2
+ ffi::OsString,
3
+ path::{Path, PathBuf},
4
+ process::{Command, Stdio},
5
+ };
6
+
1
7
  use tauri::{AppHandle, Emitter, Manager, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
2
8
 
3
9
  use crate::dlog;
@@ -505,6 +511,78 @@ pub fn frontmost_bundle_id() -> Option<String> {
505
511
  None
506
512
  }
507
513
 
514
+ fn bundle_path_from_executable_path(executable_path: &Path) -> Option<PathBuf> {
515
+ let macos_dir = executable_path.parent()?;
516
+ if macos_dir.file_name()?.to_str()? != "MacOS" {
517
+ return None;
518
+ }
519
+ let contents_dir = macos_dir.parent()?;
520
+ let bundle_path = contents_dir.parent()?;
521
+ Some(bundle_path.to_path_buf())
522
+ }
523
+
524
+ #[tauri::command]
525
+ pub fn restart_bundle_path() -> Result<String, String> {
526
+ let executable_path = std::env::current_exe().map_err(|err| format!("current_exe: {err}"))?;
527
+ #[cfg(target_os = "macos")]
528
+ let bundle_path = bundle_path_from_executable_path(&executable_path).ok_or_else(|| {
529
+ format!(
530
+ "could not derive macOS bundle path from {}",
531
+ executable_path.display()
532
+ )
533
+ })?;
534
+ #[cfg(not(target_os = "macos"))]
535
+ let bundle_path = executable_path;
536
+ Ok(bundle_path.to_string_lossy().into_owned())
537
+ }
538
+
539
+ #[cfg(target_os = "macos")]
540
+ fn spawn_macos_restart_helper(bundle_path: &Path, args: &[OsString]) -> Result<(), String> {
541
+ let parent_pid = std::process::id().to_string();
542
+ let mut command = Command::new("/bin/sh");
543
+ command
544
+ .arg("-c")
545
+ .arg(
546
+ r#"
547
+ parent_pid="$1"
548
+ bundle_path="$2"
549
+ shift 2
550
+ while kill -0 "$parent_pid" >/dev/null 2>&1; do
551
+ sleep 0.1
552
+ done
553
+ if [ "$#" -gt 0 ]; then
554
+ exec /usr/bin/open -n "$bundle_path" --args "$@"
555
+ else
556
+ exec /usr/bin/open -n "$bundle_path"
557
+ fi
558
+ "#,
559
+ )
560
+ .arg("clips-restart-helper")
561
+ .arg(parent_pid)
562
+ .arg(bundle_path)
563
+ .args(args.iter().skip(1))
564
+ .stdin(Stdio::null())
565
+ .stdout(Stdio::null())
566
+ .stderr(Stdio::null());
567
+
568
+ command
569
+ .spawn()
570
+ .map_err(|err| format!("spawn restart helper: {err}"))?;
571
+ Ok(())
572
+ }
573
+
574
+ #[cfg(not(target_os = "macos"))]
575
+ fn spawn_macos_restart_helper(_bundle_path: &Path, _args: &[OsString]) -> Result<(), String> {
576
+ Err("macOS restart helper is unavailable on this platform".to_string())
577
+ }
578
+
579
+ #[tauri::command]
580
+ pub fn schedule_restart_after_exit(bundle_path: String) -> Result<(), String> {
581
+ let bundle_path = PathBuf::from(bundle_path);
582
+ let args: Vec<OsString> = std::env::args_os().collect();
583
+ spawn_macos_restart_helper(&bundle_path, &args)
584
+ }
585
+
508
586
  pub fn set_dictation_active(app: &AppHandle, active: bool) {
509
587
  if let Some(state) = app.try_state::<DictationActive>() {
510
588
  if let Ok(mut g) = state.0.lock() {
@@ -537,3 +615,23 @@ pub fn hide_voice_wake_popover(app: &AppHandle) {
537
615
  }
538
616
  }
539
617
  }
618
+
619
+ #[cfg(test)]
620
+ mod tests {
621
+ use super::bundle_path_from_executable_path;
622
+ use std::path::Path;
623
+
624
+ #[test]
625
+ fn derives_macos_bundle_path_from_app_executable() {
626
+ let executable = Path::new("/Applications/Clips.app/Contents/MacOS/Clips");
627
+ let bundle =
628
+ bundle_path_from_executable_path(executable).expect("expected macOS bundle path");
629
+ assert_eq!(bundle, Path::new("/Applications/Clips.app"));
630
+ }
631
+
632
+ #[test]
633
+ fn rejects_non_bundle_executable_paths() {
634
+ let executable = Path::new("/Users/steve/dev/Clips");
635
+ assert!(bundle_path_from_executable_path(executable).is_none());
636
+ }
637
+ }
@@ -174,6 +174,14 @@ export default defineEventHandler(async (event: H3Event) => {
174
174
  uploadGenerationId?: string;
175
175
  useGenerationFence?: boolean;
176
176
  } | null;
177
+ const requestedStreamingMimeType =
178
+ body?.requestStreaming === true
179
+ ? normalizeVideoMimeType(body.mimeType)
180
+ : null;
181
+ if (body?.requestStreaming === true && !requestedStreamingMimeType) {
182
+ setResponseStatus(event, 400);
183
+ return { error: "A supported video mimeType is required for retry" };
184
+ }
177
185
  const recoveryEnabled = await isFeatureFlagEnabled(UPLOAD_RETRY_RESUME_FLAG, {
178
186
  userEmail: ownerEmail,
179
187
  userKey: ownerEmail,
@@ -353,6 +361,7 @@ export default defineEventHandler(async (event: H3Event) => {
353
361
  if (claimed) cleanupClaim = candidate;
354
362
  }
355
363
 
364
+ let resumableCleanupFailed = false;
356
365
  if (discardedResumableSession) {
357
366
  if (cleanupClaim) {
358
367
  const cleaned = await abortResumableUploadSession(
@@ -360,21 +369,34 @@ export default defineEventHandler(async (event: H3Event) => {
360
369
  { label: `reset-${recordingId}` },
361
370
  );
362
371
  if (!cleaned) {
363
- setResponseStatus(event, 502);
364
- return {
365
- error:
366
- "The previous recording upload could not be cleaned up. Retry the upload restart.",
367
- };
368
- }
369
- const released = await compareAndSetAppState(
370
- cleanupStateKey,
371
- cleanupClaim as unknown as Record<string, unknown>,
372
- null,
373
- );
374
- if (!released) {
372
+ if (!allowsSqlRecordingChunkScratch()) {
373
+ setResponseStatus(event, 502);
374
+ return {
375
+ error:
376
+ "The previous recording upload could not be cleaned up. Retry the upload restart.",
377
+ };
378
+ }
379
+ // The provider-side session may remain orphaned when abort fails,
380
+ // but local buffered scratch is a complete recovery path for the
381
+ // saved file. Keep the cleanup claim for a later retry, remove the
382
+ // stale local session handle, and force the new chunks through the
383
+ // buffered handler instead of returning the same 502 repeatedly.
375
384
  console.warn(
376
- `[reset-chunks-${recordingId}] cleanup claim changed while releasing it`,
385
+ `[reset-chunks-${recordingId}] provider cleanup failed; using buffered retry and retaining cleanup claim`,
377
386
  );
387
+ resumableCleanupFailed = true;
388
+ }
389
+ if (!resumableCleanupFailed) {
390
+ const released = await compareAndSetAppState(
391
+ cleanupStateKey,
392
+ cleanupClaim as unknown as Record<string, unknown>,
393
+ null,
394
+ );
395
+ if (!released) {
396
+ console.warn(
397
+ `[reset-chunks-${recordingId}] cleanup claim changed while releasing it`,
398
+ );
399
+ }
378
400
  }
379
401
  }
380
402
  }
@@ -385,22 +407,34 @@ export default defineEventHandler(async (event: H3Event) => {
385
407
  );
386
408
  // Clear any stale resumable session so a buffered retry does not
387
409
  // accidentally route through handleResumableChunk with stale offsets.
388
- if (!discardedResumableSession || cleanupClaim) {
389
- await deleteResumableSession(recordingId, discardedGenerationId).catch(
390
- () => {},
391
- );
410
+ if (!discardedResumableSession || cleanupClaim || resumableCleanupFailed) {
411
+ try {
412
+ await deleteResumableSession(recordingId, discardedGenerationId);
413
+ } catch (error) {
414
+ console.warn(
415
+ `[reset-chunks-${recordingId}] local resumable session cleanup failed`,
416
+ error,
417
+ );
418
+ setResponseStatus(event, 502);
419
+ return {
420
+ error:
421
+ "The previous recording upload could not be reset locally. Retry the upload restart.",
422
+ };
423
+ }
392
424
  }
393
425
 
394
426
  let uploadMode: UploadMode = "buffered";
395
427
  let compensateStartedSession: (() => Promise<void>) | null = null;
396
- if (body?.requestStreaming === true) {
397
- const mimeType = normalizeVideoMimeType(body.mimeType);
428
+ const bufferedFallbackAvailable = allowsSqlRecordingChunkScratch();
429
+ const shouldRetryStreaming =
430
+ body?.requestStreaming === true && !resumableCleanupFailed;
431
+ if (shouldRetryStreaming) {
432
+ const mimeType = requestedStreamingMimeType;
398
433
  if (!mimeType) {
399
434
  setResponseStatus(event, 400);
400
435
  return { error: "A supported video mimeType is required for retry" };
401
436
  }
402
437
 
403
- const bufferedFallbackAvailable = allowsSqlRecordingChunkScratch();
404
438
  const uploadProvider = await getActiveFileUploadProviderForRequest();
405
439
  if (
406
440
  shouldEnableStreamingUpload({
@@ -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
 
@@ -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
@@ -30,6 +30,7 @@ HubSpot/Salesforce lens; UI and agent share actions.
30
30
 
31
31
  - Workspace Connections own provider credentials. Never request, store, log, or
32
32
  return a provider token.
33
+ - For external integrations, inspect the workspace/provider connection catalog first; reuse its scoped resolver.
33
34
  - The mirror is thin: unknown fields are remote-only, sensitive ones redacted,
34
35
  only allow-listed fields mirrored. Raw payloads, transcripts, media, and
35
36
  base64 never enter SQL — evidence is a URL/id plus a bounded quote.
@@ -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