@agent-native/core 0.168.6 → 0.168.7
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/templates/clips/actions/list-recordings.ts +14 -0
- package/corpus/templates/clips/app/components/library/recording-card.tsx +2 -2
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +20 -3
- package/corpus/templates/clips/app/hooks/use-library.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +12 -5
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/ssr-handler.js +19 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "drizzle-orm";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
|
|
17
|
+
import { effectiveDuration, parseEdits } from "../app/lib/timestamp-mapping.js";
|
|
17
18
|
import { getDb, schema } from "../server/db/index.js";
|
|
18
19
|
import {
|
|
19
20
|
agentRecordingAccessFilter,
|
|
@@ -303,6 +304,9 @@ export default defineAction({
|
|
|
303
304
|
thumbnailUrl: schema.recordings.thumbnailUrl,
|
|
304
305
|
animatedThumbnailUrl: schema.recordings.animatedThumbnailUrl,
|
|
305
306
|
durationMs: schema.recordings.durationMs,
|
|
307
|
+
// Needed to derive the edited-length badge below; dropped before the
|
|
308
|
+
// response is built so the raw edits blob never reaches the client.
|
|
309
|
+
editsJson: schema.recordings.editsJson,
|
|
306
310
|
status: schema.recordings.status,
|
|
307
311
|
uploadProgress: schema.recordings.uploadProgress,
|
|
308
312
|
failureReason: schema.recordings.failureReason,
|
|
@@ -417,7 +421,17 @@ export default defineAction({
|
|
|
417
421
|
description: r.description,
|
|
418
422
|
thumbnailUrl: r.thumbnailUrl,
|
|
419
423
|
animatedThumbnailUrl: r.animatedThumbnailUrl,
|
|
424
|
+
// Raw source length. StitchManager sums this across queued
|
|
425
|
+
// recordings to size the concatenated export, which always
|
|
426
|
+
// includes each source's full untrimmed media — trims are applied
|
|
427
|
+
// at export/playback time, not by dropping bytes from the source.
|
|
420
428
|
durationMs: r.durationMs,
|
|
429
|
+
// Edited length, not the original recorded length — matches what
|
|
430
|
+
// the clip page itself shows once trims/cuts are applied.
|
|
431
|
+
effectiveDurationMs: effectiveDuration(
|
|
432
|
+
r.durationMs,
|
|
433
|
+
parseEdits(r.editsJson),
|
|
434
|
+
),
|
|
421
435
|
status: r.status,
|
|
422
436
|
uploadProgress: r.uploadProgress,
|
|
423
437
|
failureReason: r.failureReason,
|
|
@@ -98,8 +98,8 @@ export function RecordingCard({
|
|
|
98
98
|
const pendingTrashRef = useRef(false);
|
|
99
99
|
|
|
100
100
|
const duration = useMemo(
|
|
101
|
-
() => formatDuration(recording.
|
|
102
|
-
[recording.
|
|
101
|
+
() => formatDuration(recording.effectiveDurationMs),
|
|
102
|
+
[recording.effectiveDurationMs],
|
|
103
103
|
);
|
|
104
104
|
const relative = useMemo(() => {
|
|
105
105
|
const date = new Date(recording.createdAt);
|
|
@@ -51,6 +51,7 @@ export function RecordingOptionsMenu({
|
|
|
51
51
|
const [open, setOpen] = useState(false);
|
|
52
52
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
53
53
|
const deletedWhileOpenRef = useRef(false);
|
|
54
|
+
const pendingDeleteConfirmRef = useRef(false);
|
|
54
55
|
const showDownload = canDownload && Boolean(onDownload);
|
|
55
56
|
const showDelete = canDelete;
|
|
56
57
|
const trashRecording = useActionMutation<any, { id: string }>(
|
|
@@ -99,7 +100,22 @@ export function RecordingOptionsMenu({
|
|
|
99
100
|
<IconDotsVertical className="h-4 w-4" />
|
|
100
101
|
</Button>
|
|
101
102
|
</DropdownMenuTrigger>
|
|
102
|
-
<DropdownMenuContent
|
|
103
|
+
<DropdownMenuContent
|
|
104
|
+
align="end"
|
|
105
|
+
className="w-44"
|
|
106
|
+
onCloseAutoFocus={(event) => {
|
|
107
|
+
// Opening the AlertDialog while this menu is still tearing down
|
|
108
|
+
// leaves `pointer-events: none` stuck on <body>: two dismissable
|
|
109
|
+
// layers overlap and the survivor never restores the style. Wait
|
|
110
|
+
// for the menu to finish closing, and keep focus off the trigger
|
|
111
|
+
// so the dialog owns it.
|
|
112
|
+
if (pendingDeleteConfirmRef.current) {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
pendingDeleteConfirmRef.current = false;
|
|
115
|
+
setOpen(true);
|
|
116
|
+
}
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
103
119
|
{showDownload ? (
|
|
104
120
|
<DropdownMenuItem
|
|
105
121
|
onSelect={handleDownload}
|
|
@@ -116,7 +132,8 @@ export function RecordingOptionsMenu({
|
|
|
116
132
|
<DropdownMenuItem
|
|
117
133
|
onSelect={(event) => {
|
|
118
134
|
event.preventDefault();
|
|
119
|
-
|
|
135
|
+
pendingDeleteConfirmRef.current = true;
|
|
136
|
+
setMenuOpen(false);
|
|
120
137
|
}}
|
|
121
138
|
className="text-destructive focus:text-destructive"
|
|
122
139
|
>
|
|
@@ -132,7 +149,7 @@ export function RecordingOptionsMenu({
|
|
|
132
149
|
if (!deletedWhileOpenRef.current) return;
|
|
133
150
|
deletedWhileOpenRef.current = false;
|
|
134
151
|
event.preventDefault();
|
|
135
|
-
|
|
152
|
+
onDeleted?.();
|
|
136
153
|
}}
|
|
137
154
|
>
|
|
138
155
|
<AlertDialogHeader>
|
|
@@ -15,6 +15,7 @@ export interface RecordingSummary {
|
|
|
15
15
|
thumbnailUrl: string | null;
|
|
16
16
|
animatedThumbnailUrl: string | null;
|
|
17
17
|
durationMs: number;
|
|
18
|
+
effectiveDurationMs: number;
|
|
18
19
|
status: "uploading" | "processing" | "ready" | "failed";
|
|
19
20
|
uploadProgress?: number;
|
|
20
21
|
failureReason?: string | null;
|
|
@@ -247,9 +247,8 @@ function nativeSaveFailureMessage(reason: string | null | undefined): string {
|
|
|
247
247
|
return "The desktop recorder finished and saved a local copy, but Clips could not upload it. You can retry from the Clips menu without recording again.";
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
export function
|
|
250
|
+
export function BackButton({ onBack }: { onBack: () => void }) {
|
|
251
251
|
const t = useT();
|
|
252
|
-
const navigate = useNavigate();
|
|
253
252
|
|
|
254
253
|
return (
|
|
255
254
|
<Tooltip>
|
|
@@ -258,7 +257,7 @@ export function BackToLibraryButton() {
|
|
|
258
257
|
variant="ghost"
|
|
259
258
|
size="icon"
|
|
260
259
|
className="shrink-0"
|
|
261
|
-
onClick={
|
|
260
|
+
onClick={onBack}
|
|
262
261
|
aria-label={t("recordingPage.backToLibrary")}
|
|
263
262
|
>
|
|
264
263
|
<IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
|
|
@@ -1053,7 +1052,9 @@ export default function RecordingPage() {
|
|
|
1053
1052
|
return (
|
|
1054
1053
|
<div className="flex min-h-screen w-full flex-col bg-background">
|
|
1055
1054
|
<header className="flex min-w-0 shrink-0 items-center gap-2 border-b border-border px-3 py-2 sm:px-4 sm:py-3">
|
|
1056
|
-
<
|
|
1055
|
+
<BackButton
|
|
1056
|
+
onBack={() => navigate("/library", { replace: true })}
|
|
1057
|
+
/>
|
|
1057
1058
|
<div className="min-w-0 flex-1">
|
|
1058
1059
|
<p className="truncate text-sm font-medium">{visibleTitle}</p>
|
|
1059
1060
|
<p className="truncate text-xs text-muted-foreground">
|
|
@@ -1369,7 +1370,13 @@ export default function RecordingPage() {
|
|
|
1369
1370
|
{/* Main video column */}
|
|
1370
1371
|
<div className="flex w-full min-w-0 flex-col xl:flex-1">
|
|
1371
1372
|
<header className="flex min-w-0 shrink-0 items-center gap-2 border-b border-border px-3 py-2 sm:px-4 sm:py-3">
|
|
1372
|
-
<
|
|
1373
|
+
<BackButton
|
|
1374
|
+
onBack={
|
|
1375
|
+
editing
|
|
1376
|
+
? () => setEditing(false)
|
|
1377
|
+
: () => navigate("/library", { replace: true })
|
|
1378
|
+
}
|
|
1379
|
+
/>
|
|
1373
1380
|
<div className="flex-1 min-w-0">
|
|
1374
1381
|
<EditableRecordingTitle
|
|
1375
1382
|
recordingId={recording.id}
|
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
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
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;
|
|
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
17
17
|
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
|
-
error?: undefined;
|
|
21
20
|
configured?: undefined;
|
|
22
21
|
connectPath?: undefined;
|
|
23
22
|
url: string;
|
|
24
23
|
id: string;
|
|
25
24
|
provider: string;
|
|
25
|
+
error?: undefined;
|
|
26
26
|
}>;
|
|
27
27
|
export default _default;
|
|
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
error?: undefined;
|
|
45
44
|
summary: import("./types.js").TraceSummary;
|
|
46
45
|
spans: import("./types.js").TraceSpan[];
|
|
47
46
|
id?: undefined;
|
|
47
|
+
error?: undefined;
|
|
48
48
|
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
|
-
error?: undefined;
|
|
51
50
|
summary?: undefined;
|
|
52
51
|
spans?: undefined;
|
|
53
52
|
id: string;
|
|
53
|
+
error?: undefined;
|
|
54
54
|
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
56
|
summary?: undefined;
|
|
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
error: any;
|
|
60
60
|
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
65
|
+
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|
|
@@ -48,11 +48,11 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
error: string;
|
|
52
51
|
ok?: undefined;
|
|
52
|
+
error: string;
|
|
53
53
|
} | {
|
|
54
|
-
ok: boolean;
|
|
55
54
|
error?: undefined;
|
|
55
|
+
ok: boolean;
|
|
56
56
|
}>;
|
|
57
57
|
/** POST /_agent-native/resources/upload — upload a file as a resource */
|
|
58
58
|
export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
|
|
@@ -73,10 +73,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
|
|
|
73
73
|
runId: string | null;
|
|
74
74
|
expiresAt: number | null;
|
|
75
75
|
metadata: string | null;
|
|
76
|
+
error?: undefined;
|
|
76
77
|
url: string;
|
|
77
78
|
provider: string;
|
|
78
79
|
storageSetupRequired?: undefined;
|
|
79
|
-
error?: undefined;
|
|
80
80
|
} | {
|
|
81
81
|
error: string;
|
|
82
82
|
storageSetupRequired: boolean;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -37,17 +37,17 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
37
37
|
ok?: undefined;
|
|
38
38
|
status?: undefined;
|
|
39
39
|
} | {
|
|
40
|
-
error?: undefined;
|
|
41
40
|
ok: boolean;
|
|
42
41
|
status: string;
|
|
42
|
+
error?: undefined;
|
|
43
43
|
} | {
|
|
44
44
|
ok?: undefined;
|
|
45
45
|
error: string;
|
|
46
46
|
removed?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
error?: undefined;
|
|
49
48
|
ok: boolean;
|
|
50
49
|
removed: boolean;
|
|
50
|
+
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
@@ -58,13 +58,13 @@ export declare function createTestSecretHandler(): import("h3").EventHandlerWith
|
|
|
58
58
|
error: string;
|
|
59
59
|
note?: undefined;
|
|
60
60
|
} | {
|
|
61
|
-
error?: undefined;
|
|
62
61
|
ok: boolean;
|
|
63
62
|
note?: undefined;
|
|
64
|
-
} | {
|
|
65
63
|
error?: undefined;
|
|
64
|
+
} | {
|
|
66
65
|
ok: boolean;
|
|
67
66
|
note: string;
|
|
67
|
+
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
-
error?: undefined;
|
|
99
98
|
ok: boolean;
|
|
100
99
|
key: string;
|
|
101
|
-
} | {
|
|
102
100
|
error?: undefined;
|
|
101
|
+
} | {
|
|
103
102
|
ok: boolean;
|
|
104
103
|
removed: boolean;
|
|
104
|
+
error?: undefined;
|
|
105
105
|
}>>;
|
|
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
-
error?: undefined;
|
|
30
29
|
token: string;
|
|
31
30
|
expiresAt: string;
|
|
32
31
|
ttlSeconds: number;
|
|
32
|
+
error?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -157,8 +157,26 @@ function injectDefaultSocialImageMeta(html, imageUrl) {
|
|
|
157
157
|
return html;
|
|
158
158
|
return html.slice(0, headCloseIdx) + tags.join("") + html.slice(headCloseIdx);
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* A "not found" shell is exactly as impersonal as a 200 shell, and leaving it
|
|
162
|
+
* uncached is expensive in a way that is invisible until it is not: every dead
|
|
163
|
+
* link, stale bookmark, renamed slug and crawler miss re-invoked the render
|
|
164
|
+
* function, and Netlify runs one request per container. Measured on
|
|
165
|
+
* www.agent-native.com, `/docs/<anything>` cost ~5s and cost the SAME ~5s on
|
|
166
|
+
* the very next identical request, because `no-cache` meant nothing was ever
|
|
167
|
+
* stored. Those invocations draw from the account-wide concurrency pool every
|
|
168
|
+
* other site shares, so one crawler walking dead links slowed unrelated apps.
|
|
169
|
+
*
|
|
170
|
+
* Only 404/410 join the shared policy. A 5xx is transient and must stay
|
|
171
|
+
* uncacheable — pinning one at the edge for the SWR window would turn a blip
|
|
172
|
+
* into an outage. 401/403 stay out because an auth-shaped response is the one
|
|
173
|
+
* error that could carry viewer-specific meaning.
|
|
174
|
+
*/
|
|
175
|
+
const CACHEABLE_ERROR_STATUSES = new Set([404, 410]);
|
|
160
176
|
function isSsrHtmlOrDataResponse(headers, status, pathname) {
|
|
161
|
-
if (status < 200
|
|
177
|
+
if (status < 200)
|
|
178
|
+
return false;
|
|
179
|
+
if (status >= 400 && !CACHEABLE_ERROR_STATUSES.has(status))
|
|
162
180
|
return false;
|
|
163
181
|
const contentType = headers.get("content-type")?.toLowerCase() ?? "";
|
|
164
182
|
if (contentType.includes("text/html"))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.168.
|
|
3
|
+
"version": "0.168.7",
|
|
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": {
|