@agent-native/core 0.79.24 → 0.79.26

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 CHANGED
@@ -28,4 +28,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
  ## Generated Counts
29
29
 
30
30
  - core files: 2032
31
- - template files: 4436
31
+ - template files: 4437
@@ -1,5 +1,23 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.79.26
4
+
5
+ ### Patch Changes
6
+
7
+ - 27f630c: Fix Windows desktop (Tauri) email/password sign-in. The login endpoint now
8
+ returns the session token to the Windows WebView2 origin
9
+ (`http://tauri.localhost` / `https://tauri.localhost`), which was missing from
10
+ the desktop token allowlist, so sign-in no longer silently bounces back to the
11
+ form. Also stop reporting wrong-password failures as "Enter a valid email
12
+ address" — credential errors now surface as "Invalid email or password" while
13
+ genuine malformed-email input still gets the friendly format message.
14
+
15
+ ## 0.79.25
16
+
17
+ ### Patch Changes
18
+
19
+ - 8bac54f: Expose the optional image model menu through AgentSidebar so app sidebars can show secondary generation model controls.
20
+
3
21
  ## 0.79.24
4
22
 
5
23
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.79.24",
3
+ "version": "0.79.26",
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": {
@@ -2340,6 +2340,8 @@ export interface AgentSidebarProps {
2340
2340
  dynamicSuggestions?: AssistantChatProps["dynamicSuggestions"];
2341
2341
  /** Optional controls rendered in the chat composer toolbar. */
2342
2342
  composerToolbarSlot?: AssistantChatProps["composerToolbarSlot"];
2343
+ /** Optional secondary model menu shown inside the chat composer model picker. */
2344
+ imageModelMenu?: AssistantChatProps["imageModelMenu"];
2343
2345
  /** Optional content rendered at the bottom of the chat thread. */
2344
2346
  threadFooterSlot?: AssistantChatProps["threadFooterSlot"];
2345
2347
  /** Initial sidebar width in pixels. Mount-only; user resize and a saved
@@ -2389,6 +2391,7 @@ export function AgentSidebar({
2389
2391
  suggestions,
2390
2392
  dynamicSuggestions,
2391
2393
  composerToolbarSlot,
2394
+ imageModelMenu,
2392
2395
  threadFooterSlot,
2393
2396
  defaultSidebarWidth,
2394
2397
  sidebarWidth,
@@ -2924,6 +2927,7 @@ export function AgentSidebar({
2924
2927
  suggestions={suggestions}
2925
2928
  dynamicSuggestions={dynamicSuggestions}
2926
2929
  composerToolbarSlot={composerToolbarSlot}
2930
+ imageModelMenu={imageModelMenu}
2927
2931
  threadFooterSlot={threadFooterSlot}
2928
2932
  missingApiKeySetupLayout="sidebar"
2929
2933
  onCollapse={() => setOpenPersisted(false)}
@@ -842,6 +842,11 @@ function publicAuthError(
842
842
  }
843
843
 
844
844
  function isAuthEmailValidationMessage(message: string): boolean {
845
+ // Credential failures (e.g. Better Auth's "Invalid email or password") mention
846
+ // "email" + "invalid" but are NOT email-format errors — don't rewrite them to
847
+ // the "enter a valid email" message, or every wrong-password attempt looks like
848
+ // a malformed-email error.
849
+ if (/password|credential/i.test(message)) return false;
845
850
  return (
846
851
  /\bemail\b/i.test(message) &&
847
852
  /(invalid|input|required|format)/i.test(message)
@@ -1129,6 +1134,8 @@ const _desktopExchanges = new Map<string, DesktopExchangeEntry>();
1129
1134
  const DESKTOP_EXCHANGE_ERROR_PREFIX = "__error__::";
1130
1135
  const DESKTOP_AUTH_TOKEN_BODY_ORIGINS = new Set([
1131
1136
  "tauri://localhost",
1137
+ "http://tauri.localhost",
1138
+ "https://tauri.localhost",
1132
1139
  "http://localhost:1420",
1133
1140
  ]);
1134
1141
 
@@ -9,11 +9,16 @@ import {
9
9
  useT,
10
10
  } from "@agent-native/core/client";
11
11
  import { InvitationBanner } from "@agent-native/core/client/org";
12
+ import {
13
+ EMBED_MODE_QUERY_PARAM,
14
+ EMBED_TOKEN_QUERY_PARAM,
15
+ } from "@agent-native/core/shared";
12
16
  import { IconMenu2 } from "@tabler/icons-react";
13
17
  import { useState, useEffect } from "react";
14
18
  import { useLocation, useNavigate } from "react-router";
15
19
 
16
20
  import { GenerationResults } from "@/components/generation/GenerationResults";
21
+ import { useImageModelMenu } from "@/hooks/use-image-model-menu";
17
22
  import { useNavigationState } from "@/hooks/use-navigation-state";
18
23
  import { ASSETS_CHAT_STORAGE_KEY } from "@/lib/chat";
19
24
  import { cn } from "@/lib/utils";
@@ -35,11 +40,22 @@ function isEmbeddedWindow() {
35
40
  }
36
41
  }
37
42
 
43
+ function searchParamsEnableEmbeddedMode(search: string): boolean {
44
+ const params = new URLSearchParams(search);
45
+ const embedMode = params.get(EMBED_MODE_QUERY_PARAM);
46
+ return (
47
+ params.has(EMBED_TOKEN_QUERY_PARAM) ||
48
+ embedMode === "1" ||
49
+ embedMode === "true"
50
+ );
51
+ }
52
+
38
53
  export function Layout({ children }: LayoutProps) {
39
54
  useNavigationState();
40
55
  const location = useLocation();
41
56
  const navigate = useNavigate();
42
57
  const t = useT();
58
+ const imageModelMenu = useImageModelMenu();
43
59
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
44
60
  const isCreateRoute =
45
61
  location.pathname === "/" || location.pathname.startsWith("/chat/");
@@ -64,7 +80,10 @@ export function Layout({ children }: LayoutProps) {
64
80
  location.pathname === "/extensions" ||
65
81
  location.pathname.startsWith("/extensions/");
66
82
  const chromeless =
67
- (isPicker && (isEmbeddedWindow() || isEmbedAuthActive())) ||
83
+ (isPicker &&
84
+ (searchParamsEnableEmbeddedMode(location.search) ||
85
+ isEmbeddedWindow() ||
86
+ isEmbedAuthActive())) ||
68
87
  location.pathname.endsWith("/embed");
69
88
 
70
89
  if (chromeless) {
@@ -143,6 +162,7 @@ export function Layout({ children }: LayoutProps) {
143
162
  threadFooterSlot={({ threadId }) => (
144
163
  <GenerationResults threadId={threadId} />
145
164
  )}
165
+ imageModelMenu={imageModelMenu}
146
166
  >
147
167
  {appFrame}
148
168
  </AgentSidebar>
@@ -0,0 +1,74 @@
1
+ import {
2
+ readClientAppState,
3
+ useT,
4
+ writeClientAppState,
5
+ } from "@agent-native/core/client";
6
+ import { useCallback, useEffect, useMemo, useState } from "react";
7
+
8
+ // The composer's model picker shows the chat LLM (Claude/OpenAI/Gemini). The
9
+ // Assets app also drives a separate image model, so expose it as a secondary
10
+ // menu wherever Assets chat is mounted.
11
+ const IMAGE_MODEL_STATE_KEY = "imageGenerationModel";
12
+ const DEFAULT_IMAGE_MODEL = "gemini-3.1-flash-image";
13
+ const IMAGE_MODEL_OPTIONS = [
14
+ {
15
+ value: "gemini-3-pro-image",
16
+ modelName: "Gemini 3 Pro",
17
+ descriptorKey: "create.modelBestQuality",
18
+ },
19
+ {
20
+ value: "gemini-3.1-flash-image",
21
+ modelName: "Gemini 3.1 Flash",
22
+ descriptorKey: "create.modelFast",
23
+ },
24
+ { value: "gemini-2.5-flash-image", modelName: "Gemini 2.5 Flash" },
25
+ ] as const;
26
+
27
+ export function useImageModelMenu() {
28
+ const t = useT();
29
+ const [imageModel, setImageModel] = useState<string>(DEFAULT_IMAGE_MODEL);
30
+
31
+ // Hydrate the saved image-model default so the picker reflects the user's
32
+ // last choice across sessions.
33
+ useEffect(() => {
34
+ let cancelled = false;
35
+ void readClientAppState<{ model?: string }>(IMAGE_MODEL_STATE_KEY)
36
+ .then((state) => {
37
+ const stored = state?.model;
38
+ if (
39
+ !cancelled &&
40
+ stored &&
41
+ IMAGE_MODEL_OPTIONS.some((option) => option.value === stored)
42
+ ) {
43
+ setImageModel(stored);
44
+ }
45
+ })
46
+ .catch(() => {});
47
+ return () => {
48
+ cancelled = true;
49
+ };
50
+ }, []);
51
+
52
+ const handleImageModelChange = useCallback((value: string) => {
53
+ setImageModel(value);
54
+ void writeClientAppState(IMAGE_MODEL_STATE_KEY, { model: value }).catch(
55
+ () => {},
56
+ );
57
+ }, []);
58
+
59
+ return useMemo(
60
+ () => ({
61
+ value: imageModel,
62
+ options: IMAGE_MODEL_OPTIONS.map((option) => ({
63
+ value: option.value,
64
+ label:
65
+ "descriptorKey" in option && option.descriptorKey
66
+ ? `${option.modelName} · ${t(option.descriptorKey)}`
67
+ : option.modelName,
68
+ })),
69
+ onChange: handleImageModelChange,
70
+ label: t("create.imageModel"),
71
+ }),
72
+ [handleImageModelChange, imageModel, t],
73
+ );
74
+ }
@@ -2,40 +2,17 @@ import {
2
2
  AgentChatSurface,
3
3
  getBrowserTabId,
4
4
  markAgentChatHomeHandoff,
5
- readClientAppState,
6
5
  sendToAgentChat,
7
6
  useT,
8
- writeClientAppState,
9
7
  } from "@agent-native/core/client";
10
8
  import { IconPhoto, IconSparkles, IconVideo } from "@tabler/icons-react";
11
- import { useCallback, useEffect, useState } from "react";
9
+ import { useEffect } from "react";
12
10
  import { useNavigate, useParams } from "react-router";
13
11
 
14
12
  import { GenerationResults } from "@/components/generation/GenerationResults";
13
+ import { useImageModelMenu } from "@/hooks/use-image-model-menu";
15
14
  import { ASSETS_CHAT_STORAGE_KEY } from "@/lib/chat";
16
15
 
17
- // The composer's model picker shows the chat LLM (Claude/OpenAI/Gemini). The
18
- // Assets app also drives a separate *image* model, so we surface it in the same
19
- // menu — otherwise "Claude" reads as the image generator, which it isn't. The
20
- // choice persists in per-user application state so the generate-image action
21
- // (server-side) can read it as the default model. Values must be valid
22
- // IMAGE_MODELS ids from shared/api.
23
- const IMAGE_MODEL_STATE_KEY = "imageGenerationModel";
24
- const DEFAULT_IMAGE_MODEL = "gemini-3.1-flash-image";
25
- const IMAGE_MODEL_OPTIONS = [
26
- {
27
- value: "gemini-3-pro-image",
28
- modelName: "Gemini 3 Pro",
29
- descriptorKey: "create.modelBestQuality",
30
- },
31
- {
32
- value: "gemini-3.1-flash-image",
33
- modelName: "Gemini 3.1 Flash",
34
- descriptorKey: "create.modelFast",
35
- },
36
- { value: "gemini-2.5-flash-image", modelName: "Gemini 2.5 Flash" },
37
- ] as const;
38
-
39
16
  // Empty-state starters. Clicking one prefills the composer (without sending) so
40
17
  // the user can finish the thought instead of staring at a chip that does
41
18
  // nothing. `submit: false` = prefill only; `openSidebar: false` keeps focus on
@@ -81,7 +58,7 @@ export default function CreatePage() {
81
58
  const { threadId } = useParams();
82
59
  const navigate = useNavigate();
83
60
  const t = useT();
84
- const [imageModel, setImageModel] = useState<string>(DEFAULT_IMAGE_MODEL);
61
+ const imageModelMenu = useImageModelMenu();
85
62
 
86
63
  useEffect(() => {
87
64
  function handleChatRunning(event: Event) {
@@ -96,34 +73,6 @@ export default function CreatePage() {
96
73
  window.removeEventListener("agentNative.chatRunning", handleChatRunning);
97
74
  }, []);
98
75
 
99
- // Hydrate the saved image-model default so the picker reflects the user's
100
- // last choice across sessions.
101
- useEffect(() => {
102
- let cancelled = false;
103
- void readClientAppState<{ model?: string }>(IMAGE_MODEL_STATE_KEY)
104
- .then((state) => {
105
- const stored = state?.model;
106
- if (
107
- !cancelled &&
108
- stored &&
109
- IMAGE_MODEL_OPTIONS.some((option) => option.value === stored)
110
- ) {
111
- setImageModel(stored);
112
- }
113
- })
114
- .catch(() => {});
115
- return () => {
116
- cancelled = true;
117
- };
118
- }, []);
119
-
120
- const handleImageModelChange = useCallback((value: string) => {
121
- setImageModel(value);
122
- void writeClientAppState(IMAGE_MODEL_STATE_KEY, { model: value }).catch(
123
- () => {},
124
- );
125
- }, []);
126
-
127
76
  return (
128
77
  <div className="flex h-full min-h-0 flex-col bg-background">
129
78
  <AgentChatSurface
@@ -141,18 +90,7 @@ export default function CreatePage() {
141
90
  threadFooterSlot={({ threadId }) => (
142
91
  <GenerationResults threadId={threadId} />
143
92
  )}
144
- imageModelMenu={{
145
- value: imageModel,
146
- options: IMAGE_MODEL_OPTIONS.map((option) => ({
147
- value: option.value,
148
- label:
149
- "descriptorKey" in option && option.descriptorKey
150
- ? `${option.modelName} · ${t(option.descriptorKey)}`
151
- : option.modelName,
152
- })),
153
- onChange: handleImageModelChange,
154
- label: t("create.imageModel"),
155
- }}
93
+ imageModelMenu={imageModelMenu}
156
94
  showHeader={false}
157
95
  showTabBar={false}
158
96
  dynamicSuggestions={false}
@@ -1,4 +1,5 @@
1
1
  import {
2
+ AgentToggleButton,
2
3
  appPath,
3
4
  getBrowserTabId,
4
5
  getEmbedAuthToken,
@@ -18,6 +19,10 @@ import {
18
19
  createEmbeddedAppBridge,
19
20
  type EmbeddedAppBridge,
20
21
  } from "@agent-native/core/embedding";
22
+ import {
23
+ EMBED_MODE_QUERY_PARAM,
24
+ EMBED_TOKEN_QUERY_PARAM,
25
+ } from "@agent-native/core/shared";
21
26
  import {
22
27
  IconArrowUpRight,
23
28
  IconCheck,
@@ -114,6 +119,7 @@ const ASPECT_RATIOS = ["16:9", "1:1", "9:16", "4:3", "3:4", "21:9"] as const;
114
119
  const GENERATION_COUNTS = [1, 2, 3, 4, 6] as const;
115
120
  const STARTER_PRESET = DEFAULT_LIBRARY_PRESETS[0];
116
121
  const STARTER_LIBRARY_ID = `starter:${STARTER_PRESET.id}`;
122
+ const MCP_APP_CHAT_BRIDGE_QUERY_PARAM = "__an_mcp_chat_bridge";
117
123
  const PICKER_INLINE_SELECT_CLASS =
118
124
  "h-7 w-auto min-w-0 max-w-full rounded-md border-0 bg-transparent px-1.5 py-1 text-xs font-medium text-muted-foreground shadow-none ring-offset-transparent transition hover:bg-accent/50 hover:text-foreground focus:ring-0 focus:ring-offset-0 sm:px-2 [&>svg]:ms-1 [&>svg]:size-3.5 [&>svg]:opacity-60";
119
125
  type PickerMediaType = "image" | "video";
@@ -266,6 +272,24 @@ function normalizeCandidateRunIds(value: unknown): string[] | undefined {
266
272
  return ids;
267
273
  }
268
274
 
275
+ function searchParamsEnableEmbeddedLibrary(params: URLSearchParams): boolean {
276
+ const embedMode = params.get(EMBED_MODE_QUERY_PARAM);
277
+ return (
278
+ params.has(EMBED_TOKEN_QUERY_PARAM) ||
279
+ embedMode === "1" ||
280
+ embedMode === "true"
281
+ );
282
+ }
283
+
284
+ function searchParamsRequestPicker(params: URLSearchParams): boolean {
285
+ const mcpChatBridge = params.get(MCP_APP_CHAT_BRIDGE_QUERY_PARAM);
286
+ return (
287
+ params.get("__an_picker") === "1" ||
288
+ mcpChatBridge === "1" ||
289
+ mcpChatBridge === "true"
290
+ );
291
+ }
292
+
269
293
  function normalizeHostConfig(value: unknown): HostConfig {
270
294
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
271
295
  const record = value as Record<string, unknown>;
@@ -870,6 +894,7 @@ function LibraryShellHeader({
870
894
  aria-label={t("library.kitActions")}
871
895
  />
872
896
  ) : null}
897
+ <AgentToggleButton />
873
898
  </div>
874
899
  </div>
875
900
  </header>
@@ -1277,7 +1302,7 @@ function AllAssetsBrowser() {
1277
1302
  <button
1278
1303
  type="button"
1279
1304
  onClick={() => navigate(`/library/${asset.libraryId}`)}
1280
- className="absolute bottom-2 left-2 z-10 max-w-[calc(100%-1rem)] truncate rounded-full bg-background/90 px-2.5 py-1 text-[11px] font-medium shadow-sm backdrop-blur transition hover:bg-background"
1305
+ className="absolute bottom-2 left-2 z-10 max-w-[calc(100%-1rem)] truncate rounded-full bg-background/95 px-2.5 py-1 text-[11px] font-medium shadow-sm transition hover:bg-background"
1281
1306
  >
1282
1307
  {(asset as any).libraryTitle}
1283
1308
  </button>
@@ -1292,7 +1317,7 @@ function AllAssetsBrowser() {
1292
1317
  event.stopPropagation();
1293
1318
  chooseAsset(asset);
1294
1319
  }}
1295
- className="absolute right-2 top-2 z-10 inline-flex h-8 w-8 items-center justify-center rounded-full bg-background/80 text-foreground opacity-0 shadow-sm backdrop-blur transition hover:bg-primary hover:text-primary-foreground focus:outline-none focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring group-hover:opacity-100"
1320
+ className="absolute right-2 top-2 z-10 inline-flex h-8 w-8 items-center justify-center rounded-full bg-background/90 text-foreground opacity-0 shadow-sm transition hover:bg-primary hover:text-primary-foreground focus:outline-none focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring group-hover:opacity-100"
1296
1321
  >
1297
1322
  <IconClipboard className="h-4 w-4" />
1298
1323
  </button>
@@ -1892,32 +1917,34 @@ export function LibraryWorkspace({
1892
1917
  return (
1893
1918
  <div className="flex h-full min-h-0 flex-col bg-background text-foreground">
1894
1919
  <section className="min-h-0 min-w-0 flex-1 overflow-hidden">
1895
- {routeSelectedLibraryId || hasLibraries ? (
1896
- <div className="h-full min-h-0 min-w-0 overflow-y-auto">
1897
- <LibraryShellHeader
1898
- selectedLibraryId={routeSelectedLibraryId}
1899
- libraries={libraries}
1900
- isLoading={isLoading}
1901
- onCreateKit={() => setCreateOpen(true)}
1902
- />
1903
- <LibraryCandidateStage
1904
- activeLibraryId={routeSelectedLibraryId}
1905
- foldersByLibraryId={foldersByLibraryId}
1906
- />
1907
- <div className="min-w-0">
1908
- {routeSelectedLibraryId ? (
1909
- <BrandKitDetailRoute
1910
- libraryId={routeSelectedLibraryId}
1911
- headerMode="actions"
1912
- />
1913
- ) : (
1914
- <AllAssetsBrowser />
1915
- )}
1916
- </div>
1917
- </div>
1918
- ) : (
1919
- <EmptyLibraryStarter onCreateBlank={() => setCreateOpen(true)} />
1920
- )}
1920
+ <div className="h-full min-h-0 min-w-0 overflow-y-auto">
1921
+ <LibraryShellHeader
1922
+ selectedLibraryId={routeSelectedLibraryId}
1923
+ libraries={libraries}
1924
+ isLoading={isLoading}
1925
+ onCreateKit={() => setCreateOpen(true)}
1926
+ />
1927
+ {routeSelectedLibraryId || hasLibraries ? (
1928
+ <>
1929
+ <LibraryCandidateStage
1930
+ activeLibraryId={routeSelectedLibraryId}
1931
+ foldersByLibraryId={foldersByLibraryId}
1932
+ />
1933
+ <div className="min-w-0">
1934
+ {routeSelectedLibraryId ? (
1935
+ <BrandKitDetailRoute
1936
+ libraryId={routeSelectedLibraryId}
1937
+ headerMode="actions"
1938
+ />
1939
+ ) : (
1940
+ <AllAssetsBrowser />
1941
+ )}
1942
+ </div>
1943
+ </>
1944
+ ) : (
1945
+ <EmptyLibraryStarter onCreateBlank={() => setCreateOpen(true)} />
1946
+ )}
1947
+ </div>
1921
1948
  </section>
1922
1949
  <CreateLibraryDialog
1923
1950
  open={createOpen}
@@ -1933,8 +1960,7 @@ export function AssetPickerSurface() {
1933
1960
  const [searchParams] = useSearchParams();
1934
1961
  const searchParamsKey = searchParams.toString();
1935
1962
  const mcpChatBridgeActive =
1936
- searchParams.get("__an_mcp_chat_bridge") === "1" ||
1937
- isEmbedMcpChatBridgeActive();
1963
+ searchParamsRequestPicker(searchParams) || isEmbedMcpChatBridgeActive();
1938
1964
  const urlHostConfig = useMemo(() => {
1939
1965
  const params = new URLSearchParams(searchParamsKey);
1940
1966
  return {
@@ -1959,7 +1985,13 @@ export function AssetPickerSurface() {
1959
1985
  } satisfies HostConfig;
1960
1986
  }, [searchParamsKey]);
1961
1987
  const bridgeRef = useRef<EmbeddedAppBridge | null>(null);
1962
- const embedded = useMemo(() => isEmbeddedWindow() || isEmbedAuthActive(), []);
1988
+ const embedded = useMemo(
1989
+ () =>
1990
+ searchParamsEnableEmbeddedLibrary(searchParams) ||
1991
+ isEmbeddedWindow() ||
1992
+ isEmbedAuthActive(),
1993
+ [searchParams],
1994
+ );
1963
1995
  const pickerVariantScopeId = useMemo(
1964
1996
  () =>
1965
1997
  typeof window === "undefined" ? null : `picker:${getBrowserTabId()}`,
@@ -2862,14 +2894,17 @@ export function AssetPickerSurface() {
2862
2894
  </div>
2863
2895
  )}
2864
2896
 
2865
- {mediaType === "image" && selectedLibraryId && pickerVariantScopeId && (
2866
- <LibraryCandidateStage
2867
- activeLibraryId={selectedLibraryId}
2868
- variantScopeId={pickerVariantScopeId}
2869
- onUseAsset={chooseAsset}
2870
- inline
2871
- />
2872
- )}
2897
+ {mediaType === "image" &&
2898
+ selectedLibraryId &&
2899
+ !usingStarterLibrary &&
2900
+ pickerVariantScopeId && (
2901
+ <LibraryCandidateStage
2902
+ activeLibraryId={selectedLibraryId}
2903
+ variantScopeId={pickerVariantScopeId}
2904
+ onUseAsset={chooseAsset}
2905
+ inline
2906
+ />
2907
+ )}
2873
2908
 
2874
2909
  {!selectedLibraryId && (
2875
2910
  <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
@@ -3070,8 +3105,8 @@ export default function LibraryRoute() {
3070
3105
  const { pickerRequested, queryLibraryId } = useMemo(() => {
3071
3106
  const params = new URLSearchParams(searchParamsKey);
3072
3107
  const requested =
3073
- params.get("__an_picker") === "1" ||
3074
- params.get("__an_mcp_chat_bridge") === "1";
3108
+ searchParamsRequestPicker(params) ||
3109
+ searchParamsEnableEmbeddedLibrary(params);
3075
3110
  return {
3076
3111
  pickerRequested: requested,
3077
3112
  queryLibraryId: requested ? null : params.get("libraryId"),
@@ -1881,6 +1881,40 @@ async function startNativeFullscreenRecording(
1881
1881
 
1882
1882
  let uploadResult: NativeFullscreenUploadResult | null = null;
1883
1883
  const viewUrl = `/r/${id}`;
1884
+
1885
+ // A native recording runs two ScreenCaptureKit streams: the screen
1886
+ // recorder and the whisper system-audio recognizer (system_audio.rs
1887
+ // opens its own SCStream with captures_audio). Tearing the transcription
1888
+ // stream down while the recorder is flushing its final `moov` atom
1889
+ // interrupts ScreenCaptureKit (RPRecordingErrorDomain -5814,
1890
+ // "Application connection interrupted"), so the recorder's
1891
+ // SCRecordingOutput aborts before the moov is written and the MP4 is left
1892
+ // permanently corrupt. The teardowns must be sequenced: recorder first,
1893
+ // transcription second.
1894
+ //
1895
+ // We also must not delay the recorder stop, or the clip keeps capturing
1896
+ // past the Stop click (Rust measures duration when the stop command
1897
+ // runs, and transcriptionCapture.stop() blocks on a ~1.5s settle).
1898
+ //
1899
+ // So: start the native finalize+upload now, which stops the recorder
1900
+ // capture immediately; wait for Rust to emit that the recorder has
1901
+ // finalized (moov written); only then tear the transcription stream
1902
+ // down. A timeout longer than the Rust finalize ceiling
1903
+ // (SCK_FINALIZE_TIMEOUT) guards against a lost event so Stop can never
1904
+ // hang.
1905
+ let signalRecorderFinalized: () => void = () => {};
1906
+ const recorderFinalized = new Promise<void>((resolve) => {
1907
+ signalRecorderFinalized = resolve;
1908
+ });
1909
+ const unlistenFinalized = await listen<string>(
1910
+ "clips:native-recording-finalized",
1911
+ (event) => {
1912
+ if (!event.payload || event.payload === id) {
1913
+ signalRecorderFinalized();
1914
+ }
1915
+ },
1916
+ );
1917
+
1884
1918
  const uploadPromise = invoke<NativeFullscreenUploadResult>(
1885
1919
  "native_fullscreen_recording_stop_and_upload",
1886
1920
  {
@@ -1894,6 +1928,12 @@ async function startNativeFullscreenRecording(
1894
1928
  );
1895
1929
  uploadPromise.catch(() => {});
1896
1930
  try {
1931
+ await Promise.race([
1932
+ recorderFinalized,
1933
+ new Promise<void>((resolve) => window.setTimeout(resolve, 15000)),
1934
+ ]);
1935
+ unlistenFinalized();
1936
+
1897
1937
  const capturedTranscript = await transcriptionCapture
1898
1938
  ?.stop()
1899
1939
  .catch((err) => {
@@ -764,6 +764,16 @@ pub async fn native_fullscreen_recording_stop_and_upload(
764
764
  multi_segment,
765
765
  } = take_and_finalize_active_session(&state)?;
766
766
 
767
+ // The recorder's ScreenCaptureKit stream is now fully stopped and its moov
768
+ // atom is written (or has definitively failed). Signal the UI so it can tear
769
+ // down the separate live-transcription SCStream (system_audio.rs) now,
770
+ // without racing the recorder finalize: tearing that stream down while the
771
+ // recorder is still writing its moov interrupts ScreenCaptureKit
772
+ // (RPRecordingErrorDomain -5814) and corrupts the clip. Emitting here, before
773
+ // the slow upload, lets transcription stop promptly while the clip duration
774
+ // stays anchored to the real Stop click. See recorder.ts `handle.stop()`.
775
+ let _ = app.emit("clips:native-recording-finalized", &recording_id);
776
+
767
777
  // The camera bubble is the ONE overlay we deliberately leave
768
778
  // capture-included (see `show_bubble`), so it has to stay on-screen
769
779
  // until the SCStream stops. Now that capture is finalized, tear it
@@ -132,6 +132,8 @@ export interface AgentSidebarProps {
132
132
  dynamicSuggestions?: AssistantChatProps["dynamicSuggestions"];
133
133
  /** Optional controls rendered in the chat composer toolbar. */
134
134
  composerToolbarSlot?: AssistantChatProps["composerToolbarSlot"];
135
+ /** Optional secondary model menu shown inside the chat composer model picker. */
136
+ imageModelMenu?: AssistantChatProps["imageModelMenu"];
135
137
  /** Optional content rendered at the bottom of the chat thread. */
136
138
  threadFooterSlot?: AssistantChatProps["threadFooterSlot"];
137
139
  /** Initial sidebar width in pixels. Mount-only; user resize and a saved
@@ -174,7 +176,7 @@ export interface AgentSidebarProps {
174
176
  * Wraps app content with a toggleable agent sidebar.
175
177
  * Use AgentToggleButton in your header to open/close it.
176
178
  */
177
- export declare function AgentSidebar({ children, emptyStateText, suggestions, dynamicSuggestions, composerToolbarSlot, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position, defaultOpen, animateMobile, animateDesktop, chatViewTransition, storageKey, openOnChatRunning, onFullscreenRequest, scope, browserTabId, threadUrlSync, }: AgentSidebarProps): React.JSX.Element;
179
+ export declare function AgentSidebar({ children, emptyStateText, suggestions, dynamicSuggestions, composerToolbarSlot, imageModelMenu, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position, defaultOpen, animateMobile, animateDesktop, chatViewTransition, storageKey, openOnChatRunning, onFullscreenRequest, scope, browserTabId, threadUrlSync, }: AgentSidebarProps): React.JSX.Element;
178
180
  /**
179
181
  * Focus the agent chat composer input.
180
182
  * Opens the sidebar if closed, then focuses the text input.
@@ -1 +1 @@
1
- {"version":3,"file":"AgentPanel.d.ts","sourceRoot":"","sources":["../../src/client/AgentPanel.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentPanel.d.ts","sourceRoot":"","sources":["../../src/client/AgentPanel.tsx"],"names":[],"mappings":"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"}