@agent-native/core 0.161.16 → 0.161.17
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/chat/vitest.config.ts +12 -7
- 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/desktop/src/lib/updater.ts +21 -1
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +0 -4
- package/corpus/templates/clips/desktop/src/styles.css +0 -18
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -1
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +6 -3
- 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/design/app/global.css +6 -0
- 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/dist/action.d.ts +1 -0
- package/dist/action.js +1 -1
- package/dist/agent/production-agent.js +28 -5
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +6 -6
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/chat/vitest.config.ts +12 -7
- package/package.json +1 -1
- package/src/templates/chat/vitest.config.ts +12 -7
package/corpus/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { mergeConfig } from "vitest/config";
|
|
1
|
+
import path from "node:path";
|
|
3
2
|
|
|
4
|
-
import
|
|
3
|
+
import { defineConfig } from "vitest/config";
|
|
5
4
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
// Keep tests independent from vite.config.ts: the production config starts
|
|
6
|
+
// Nitro/Vite watchers and evaluates browser-targeted CommonJS SSR modules.
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
resolve: {
|
|
9
|
+
alias: {
|
|
10
|
+
"@": path.resolve(__dirname, "./app"),
|
|
11
|
+
"@shared": path.resolve(__dirname, "./shared"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { invoke } from "@tauri-apps/api/core";
|
|
2
|
+
import { exit, relaunch } from "@tauri-apps/plugin-process";
|
|
2
3
|
import { check, Update } from "@tauri-apps/plugin-updater";
|
|
3
4
|
import { useEffect, useState } from "react";
|
|
4
5
|
|
|
6
|
+
import { isMacPlatform } from "./platform";
|
|
7
|
+
|
|
5
8
|
declare const __CLIPS_DESKTOP_LOCAL_BUILD__: boolean;
|
|
6
9
|
|
|
7
10
|
export type UpdateStatus =
|
|
@@ -166,9 +169,26 @@ export async function installAndRestart(): Promise<void> {
|
|
|
166
169
|
// bundle no longer matches the running process as short as possible — the
|
|
167
170
|
// process is torn down by `relaunch()` right after, so capture never runs
|
|
168
171
|
// against a swapped-out bundle.
|
|
172
|
+
const restartBundlePath = await invoke<string>("restart_bundle_path").catch(
|
|
173
|
+
(err) => {
|
|
174
|
+
console.error("[clips-updater] restart target lookup failed:", err);
|
|
175
|
+
return null;
|
|
176
|
+
},
|
|
177
|
+
);
|
|
169
178
|
if (pendingUpdate) {
|
|
170
179
|
await pendingUpdate.install();
|
|
171
180
|
}
|
|
181
|
+
if (isMacPlatform() && restartBundlePath) {
|
|
182
|
+
try {
|
|
183
|
+
await invoke("schedule_restart_after_exit", {
|
|
184
|
+
bundlePath: restartBundlePath,
|
|
185
|
+
});
|
|
186
|
+
await exit(0);
|
|
187
|
+
return;
|
|
188
|
+
} catch (err) {
|
|
189
|
+
console.error("[clips-updater] macOS restart handoff failed:", err);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
172
192
|
await relaunch();
|
|
173
193
|
}
|
|
174
194
|
|
|
@@ -386,7 +386,6 @@ export function MeetingNotification() {
|
|
|
386
386
|
return <div className="meeting-notification-root" />;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
const isCalendar = data.type === "calendar";
|
|
390
389
|
const hasJoin = Boolean(data.joinUrl);
|
|
391
390
|
const provider = detectMeetingJoinProvider(data.joinUrl, data.platform);
|
|
392
391
|
const providerName = joinProviderLabel(provider);
|
|
@@ -404,9 +403,6 @@ export function MeetingNotification() {
|
|
|
404
403
|
onMouseEnter={() => setDomHovered(true)}
|
|
405
404
|
onMouseLeave={() => setDomHovered(false)}
|
|
406
405
|
>
|
|
407
|
-
<div
|
|
408
|
-
className={`meeting-notification-bar ${isCalendar ? "meeting-notification-bar-calendar" : "meeting-notification-bar-adhoc"}`}
|
|
409
|
-
/>
|
|
410
406
|
<div className="meeting-notification-content">
|
|
411
407
|
<div className="meeting-notification-title">{data.title}</div>
|
|
412
408
|
<div className="meeting-notification-subtitle">{data.subtitle}</div>
|
|
@@ -5414,24 +5414,6 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
5414
5414
|
max-width: 440px;
|
|
5415
5415
|
}
|
|
5416
5416
|
|
|
5417
|
-
.meeting-notification-bar {
|
|
5418
|
-
width: 3px;
|
|
5419
|
-
height: 36px;
|
|
5420
|
-
border-radius: 999px;
|
|
5421
|
-
flex-shrink: 0;
|
|
5422
|
-
}
|
|
5423
|
-
|
|
5424
|
-
.meeting-notification-bar-calendar {
|
|
5425
|
-
background: #7dd3fc;
|
|
5426
|
-
}
|
|
5427
|
-
|
|
5428
|
-
.meeting-notification-bar-adhoc {
|
|
5429
|
-
background: transparent;
|
|
5430
|
-
border-left: 3px dashed #a3a3a3;
|
|
5431
|
-
width: 3px;
|
|
5432
|
-
border-radius: 0;
|
|
5433
|
-
}
|
|
5434
|
-
|
|
5435
5417
|
.meeting-notification-content {
|
|
5436
5418
|
flex: 1;
|
|
5437
5419
|
min-width: 0;
|
|
@@ -52,7 +52,7 @@ use state::{
|
|
|
52
52
|
};
|
|
53
53
|
use util::{
|
|
54
54
|
configure_overlay_behavior, is_recording_active, present_interactive_window,
|
|
55
|
-
set_capture_excluded,
|
|
55
|
+
restart_bundle_path, schedule_restart_after_exit, set_capture_excluded,
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
// Embedded fallback icon — a tiny 16x16 solid purple PNG so the binary always
|
|
@@ -248,6 +248,8 @@ pub fn run() {
|
|
|
248
248
|
// persistent log file (production debugging)
|
|
249
249
|
logfile::frontend_log,
|
|
250
250
|
logfile::open_logs,
|
|
251
|
+
restart_bundle_path,
|
|
252
|
+
schedule_restart_after_exit,
|
|
251
253
|
])
|
|
252
254
|
.plugin(tauri_plugin_shell::init())
|
|
253
255
|
.plugin(tauri_plugin_deep_link::init())
|
|
@@ -4055,7 +4055,11 @@ fn recording_file_has_bytes(path: &Path) -> bool {
|
|
|
4055
4055
|
}
|
|
4056
4056
|
|
|
4057
4057
|
fn remove_empty_recording_artifacts(session: &NativeFullscreenSession) {
|
|
4058
|
-
for path in session
|
|
4058
|
+
for path in session
|
|
4059
|
+
.segments
|
|
4060
|
+
.iter()
|
|
4061
|
+
.chain(std::iter::once(&session.path))
|
|
4062
|
+
{
|
|
4059
4063
|
if !path.exists() {
|
|
4060
4064
|
continue;
|
|
4061
4065
|
}
|
|
@@ -4495,8 +4499,7 @@ fn write_bytes_atomically(path: &Path, data: &[u8], label: &str) -> Result<(), S
|
|
|
4495
4499
|
.and_then(|value| value.to_str())
|
|
4496
4500
|
.unwrap_or("recording");
|
|
4497
4501
|
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}"))?;
|
|
4502
|
+
std::fs::write(&temporary, data).map_err(|error| format!("{label} write failed: {error}"))?;
|
|
4500
4503
|
if let Err(error) = std::fs::rename(&temporary, path) {
|
|
4501
4504
|
let _ = std::fs::remove_file(&temporary);
|
|
4502
4505
|
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({
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
@source "./**/*.{ts,tsx}";
|
|
9
9
|
|
|
10
|
+
/* Keep agent context available to the Design agent without making the
|
|
11
|
+
* composer carry implementation details as visible chips. */
|
|
12
|
+
[data-design-app] .agent-composer-context-row {
|
|
13
|
+
display: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
:root {
|
|
11
17
|
--background: 0 0% 100%;
|
|
12
18
|
--foreground: 0 0% 10%;
|
|
@@ -70,7 +70,7 @@ const LOCALE_INIT_SCRIPT = getLocaleInitScript();
|
|
|
70
70
|
|
|
71
71
|
export function Layout({ children }: { children: React.ReactNode }) {
|
|
72
72
|
return (
|
|
73
|
-
<html lang="en" suppressHydrationWarning>
|
|
73
|
+
<html lang="en" data-design-app suppressHydrationWarning>
|
|
74
74
|
<head>
|
|
75
75
|
<meta charSet="utf-8" />
|
|
76
76
|
<meta
|
|
@@ -65,6 +65,8 @@ export default createAgentChatPlugin({
|
|
|
65
65
|
resolveOrgId: async (event) => (await getOrgContext(event)).orgId,
|
|
66
66
|
systemPrompt: `You are an AI prototyping assistant. You create and edit designs, files, design systems, variants, exports, sharing, and connected repository context through actions and shared application state.
|
|
67
67
|
|
|
68
|
+
Final responses should be concise and operational. Lead with what changed or what is needed. For ordinary design actions, use 1-3 short sentences or at most 3 flat bullets. Do not narrate your process, repeat the user's request, paste HTML or tool results, or write an essay. Mention screenshots and audits only as brief completion evidence. Expand only when the user explicitly asks for an explanation or detailed critique.
|
|
69
|
+
|
|
68
70
|
When a user message begins with [Reprompt selection], the design must remain unchanged until the user accepts a preview. Call propose-node-rewrite with the exact repromptId, target, and baseVersionHash from the message. Never call edit-design, update-design, update-file, generate-design, apply-visual-edit, or any other content-writing action for that turn. The proposal action stores preview state only; the frontend-only resolve-node-rewrite action persists a chosen variant after the user presses Accept.
|
|
69
71
|
|
|
70
72
|
When a user message begins with [Selection question], answer about the captured element and selected subtree without changing the design. You may use read-only actions when more context is needed, but do not call any content-writing action. If the user actually intended an edit, explain that they can choose Preview change from the composer mode menu and resend.
|
package/dist/action.d.ts
CHANGED
|
@@ -706,6 +706,7 @@ export interface ActionDefinition<TInput, TReturn> {
|
|
|
706
706
|
*/
|
|
707
707
|
export declare function defineAction<TSchema extends StandardSchemaV1, TReturn, TOutputSchema extends StandardSchemaV1 | undefined = undefined>(options: DefineActionWithSchema<TSchema, TReturn, TOutputSchema>): ActionDefinition<StandardSchemaV1.InferInput<TSchema>, TReturn>;
|
|
708
708
|
export declare function defineAction<TParams extends Record<string, ParameterSchema> | undefined, TReturn>(options: DefineActionWithParams<TParams, TReturn>): ActionDefinition<InferParams<TParams>, TReturn>;
|
|
709
|
+
export declare function stripUnsupportedSchemaKeywords<T>(node: T): T;
|
|
709
710
|
/**
|
|
710
711
|
* Compact signature of an action's parameters, e.g.
|
|
711
712
|
* `{ deckId*: string, operation*: "edit"|"replace", slideId?: string }` where
|
package/dist/action.js
CHANGED
|
@@ -393,7 +393,7 @@ const TYPE_SUBSTITUTE_KEYS = [
|
|
|
393
393
|
"$ref",
|
|
394
394
|
"not",
|
|
395
395
|
];
|
|
396
|
-
function stripUnsupportedSchemaKeywords(node) {
|
|
396
|
+
export function stripUnsupportedSchemaKeywords(node) {
|
|
397
397
|
if (!node || typeof node !== "object" || Array.isArray(node))
|
|
398
398
|
return node;
|
|
399
399
|
const obj = node;
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import Ajv from "ajv";
|
|
3
3
|
import { defineEventHandler, getHeader, setResponseHeader, setResponseStatus, getMethod, } from "h3";
|
|
4
4
|
import { parseA2AAgentActivityPart } from "../a2a/activity.js";
|
|
5
|
-
import { describeToolParameterSignature, isAgentActionStopError, } from "../action.js";
|
|
5
|
+
import { describeToolParameterSignature, isAgentActionStopError, stripUnsupportedSchemaKeywords, } from "../action.js";
|
|
6
6
|
import { readAppState } from "../application-state/script-helpers.js";
|
|
7
7
|
import { isReadOnlyShellCommand } from "../coding-tools/index.js";
|
|
8
8
|
import { getDbExec, isTransientDatabaseError } from "../db/client.js";
|
|
@@ -2385,18 +2385,41 @@ function extractToolSearchResultNamesFromMessages(messages) {
|
|
|
2385
2385
|
}
|
|
2386
2386
|
return names;
|
|
2387
2387
|
}
|
|
2388
|
+
/**
|
|
2389
|
+
* Every tool the model is offered passes through here -- `defineAction`
|
|
2390
|
+
* schemas, the hand-written ones in extensions/mcp/context tools, and whatever
|
|
2391
|
+
* a third-party MCP server advertises. `defineAction` sanitizes at
|
|
2392
|
+
* construction; nothing else did, so `extension-data-set` shipped a `data`
|
|
2393
|
+
* property carrying a description and no `type`, and OpenAI 400'd the whole
|
|
2394
|
+
* request -- every tool in the payload, not just that one. Sanitizing here is
|
|
2395
|
+
* what makes that unrepresentable rather than a thing each definition site has
|
|
2396
|
+
* to remember.
|
|
2397
|
+
*
|
|
2398
|
+
* Cloned first: the hand-written schemas are module constants, so rewriting in
|
|
2399
|
+
* place would mutate shared state on first use.
|
|
2400
|
+
*/
|
|
2388
2401
|
function normalizeToolInputSchema(schema) {
|
|
2389
2402
|
if (!schema)
|
|
2390
2403
|
return { type: "object", properties: {} };
|
|
2391
2404
|
if (schema.type !== "object")
|
|
2392
2405
|
return null;
|
|
2406
|
+
let cloned;
|
|
2407
|
+
try {
|
|
2408
|
+
cloned = JSON.parse(JSON.stringify(schema));
|
|
2409
|
+
}
|
|
2410
|
+
catch {
|
|
2411
|
+
// A schema that will not round-trip cannot be safely rewritten, and
|
|
2412
|
+
// shipping it unsanitized is how this class of 400 reaches the provider.
|
|
2413
|
+
return null;
|
|
2414
|
+
}
|
|
2415
|
+
const safe = stripUnsupportedSchemaKeywords(cloned);
|
|
2393
2416
|
return {
|
|
2394
|
-
...
|
|
2417
|
+
...safe,
|
|
2395
2418
|
type: "object",
|
|
2396
|
-
properties:
|
|
2397
|
-
?
|
|
2419
|
+
properties: safe.properties && typeof safe.properties === "object"
|
|
2420
|
+
? safe.properties
|
|
2398
2421
|
: {},
|
|
2399
|
-
required: Array.isArray(
|
|
2422
|
+
required: Array.isArray(safe.required) ? safe.required : [],
|
|
2400
2423
|
};
|
|
2401
2424
|
}
|
|
2402
2425
|
function stringifyToolInput(input) {
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
ok?: undefined;
|
|
30
29
|
error: string;
|
|
30
|
+
ok?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error: string;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
|
-
error?: undefined;
|
|
46
44
|
summary: import("./types.js").TraceSummary;
|
|
47
45
|
spans: import("./types.js").TraceSpan[];
|
|
48
46
|
id?: undefined;
|
|
49
|
-
} | {
|
|
50
|
-
ok?: undefined;
|
|
51
47
|
error?: undefined;
|
|
48
|
+
ok?: undefined;
|
|
49
|
+
} | {
|
|
52
50
|
summary?: undefined;
|
|
53
51
|
spans?: undefined;
|
|
54
52
|
id: string;
|
|
55
|
-
|
|
53
|
+
error?: undefined;
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
66
65
|
ok: boolean;
|
|
66
|
+
error?: undefined;
|
|
67
67
|
}>>;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,16 +34,16 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
ok?: undefined;
|
|
38
37
|
status?: undefined;
|
|
38
|
+
ok?: undefined;
|
|
39
39
|
} | {
|
|
40
40
|
ok: boolean;
|
|
41
41
|
status: string;
|
|
42
42
|
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error: string;
|
|
46
45
|
removed?: undefined;
|
|
46
|
+
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
ok: boolean;
|
|
49
49
|
removed: boolean;
|
|
@@ -54,9 +54,9 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
-
ok?: undefined;
|
|
58
57
|
error: string;
|
|
59
58
|
note?: undefined;
|
|
59
|
+
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
61
|
ok: boolean;
|
|
62
62
|
note?: undefined;
|
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
-
error?: undefined;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
key: string;
|
|
33
32
|
baseUrlKey?: string;
|
|
34
33
|
scope: AgentEngineApiKeyScope;
|
|
34
|
+
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
token: string;
|
|
30
31
|
expiresAt: string;
|
|
31
32
|
ttlSeconds: number;
|
|
32
|
-
error?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { mergeConfig } from "vitest/config";
|
|
1
|
+
import path from "node:path";
|
|
3
2
|
|
|
4
|
-
import
|
|
3
|
+
import { defineConfig } from "vitest/config";
|
|
5
4
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
// Keep tests independent from vite.config.ts: the production config starts
|
|
6
|
+
// Nitro/Vite watchers and evaluates browser-targeted CommonJS SSR modules.
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
resolve: {
|
|
9
|
+
alias: {
|
|
10
|
+
"@": path.resolve(__dirname, "./app"),
|
|
11
|
+
"@shared": path.resolve(__dirname, "./shared"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.161.
|
|
3
|
+
"version": "0.161.17",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { mergeConfig } from "vitest/config";
|
|
1
|
+
import path from "node:path";
|
|
3
2
|
|
|
4
|
-
import
|
|
3
|
+
import { defineConfig } from "vitest/config";
|
|
5
4
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
// Keep tests independent from vite.config.ts: the production config starts
|
|
6
|
+
// Nitro/Vite watchers and evaluates browser-targeted CommonJS SSR modules.
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
resolve: {
|
|
9
|
+
alias: {
|
|
10
|
+
"@": path.resolve(__dirname, "./app"),
|
|
11
|
+
"@shared": path.resolve(__dirname, "./shared"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|