@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/analytics/AGENTS.md +1 -0
- package/corpus/templates/analytics/server/plugins/auth.ts +4 -0
- package/corpus/templates/assets/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/assets/AGENTS.md +1 -0
- package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/brain/AGENTS.md +1 -0
- package/corpus/templates/calendar/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/calendar/AGENTS.md +1 -0
- package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/chat/AGENTS.md +4 -0
- package/corpus/templates/chat/vitest.config.ts +12 -7
- package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/clips/AGENTS.md +1 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +4 -0
- package/corpus/templates/clips/app/hooks/use-playback-position.ts +10 -2
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +26 -25
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -0
- package/corpus/templates/clips/changelog/2026-08-18-clips-no-longer-opens-welcome-window-on-first-launch.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-18-clips-now-recovers-interrupted-uploads-and-reliably-restarts.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-18-meeting-notifications-no-longer-show-decorative-left-side-lines.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-18-pending-recording-files-now-use-a-clips-prefixed-name.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/updater.ts +21 -1
- package/corpus/templates/clips/desktop/src/main.tsx +0 -3
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +0 -4
- package/corpus/templates/clips/desktop/src/shared/config.ts +0 -1
- package/corpus/templates/clips/desktop/src/styles.css +0 -136
- package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +0 -1
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +0 -45
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +1 -4
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +63 -8
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +98 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +54 -20
- package/corpus/templates/content/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/content/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/content/AGENTS.md +1 -0
- package/corpus/templates/crm/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/crm/AGENTS.md +1 -0
- package/corpus/templates/design/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/design/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/design/AGENTS.md +1 -0
- package/corpus/templates/design/app/global.css +6 -0
- package/corpus/templates/design/app/i18n-data.ts +11 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +9 -8
- package/corpus/templates/design/app/root.tsx +1 -1
- package/corpus/templates/design/changelog/2026-08-18-design-chat-context-stays-out-of-the-composer.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/dispatch/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/dispatch/AGENTS.md +5 -5
- package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/factory/AGENTS.md +1 -0
- package/corpus/templates/forms/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/forms/AGENTS.md +1 -0
- package/corpus/templates/macros/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/macros/AGENTS.md +1 -0
- package/corpus/templates/mail/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/mail/AGENTS.md +1 -0
- package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/plan/AGENTS.md +1 -0
- package/corpus/templates/slides/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/slides/AGENTS.md +2 -1
- package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +29 -2
- package/corpus/templates/tasks/AGENTS.md +1 -0
- package/dist/action.d.ts +1 -0
- package/dist/action.js +44 -2
- package/dist/agent/production-agent.js +28 -5
- package/dist/client/chat/run-recovery.js +1 -1
- package/dist/client/route-state.js +17 -13
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts +6 -0
- package/dist/server/auth.js +23 -5
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/dist/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
- package/dist/templates/chat/AGENTS.md +4 -0
- package/dist/templates/chat/vitest.config.ts +12 -7
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -2
- package/dist/templates/default/AGENTS.md +4 -0
- package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
- package/dist/templates/headless/AGENTS.md +4 -0
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
- package/dist/templates/workspace-core/AGENTS.md +4 -0
- package/dist/templates/workspace-root/AGENTS.md +9 -0
- package/docs/content/deployment.mdx +70 -0
- package/docs/content/faq.mdx +1 -1
- package/package.json +1 -1
- package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/src/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
- package/src/templates/chat/AGENTS.md +4 -0
- package/src/templates/chat/vitest.config.ts +12 -7
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -2
- package/src/templates/default/AGENTS.md +4 -0
- package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
- package/src/templates/headless/AGENTS.md +4 -0
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
- package/src/templates/workspace-core/AGENTS.md +4 -0
- package/src/templates/workspace-root/AGENTS.md +9 -0
- 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-
|
|
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,
|
|
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("
|
|
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
|
|
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
|
-
"
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
397
|
-
|
|
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
|
|
7
|
-
|
|
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
|
|
7
|
-
|
|
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
|
|
7
|
-
|
|
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
|