@agent-native/core 0.188.0-nightly-20260925170533 → 0.188.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +3 -0
  3. package/corpus/templates/assets/CHANGELOG.md +11 -0
  4. package/corpus/templates/calendar/CHANGELOG.md +7 -0
  5. package/corpus/templates/calendar/changelog/2026-09-24-calendar-no-longer-shows-an-internal-server-error-banner-whe.md +6 -0
  6. package/corpus/templates/calendar/server/handlers/events.ts +0 -163
  7. package/corpus/templates/calendar/server/lib/google-calendar.ts +40 -5
  8. package/corpus/templates/clips/CHANGELOG.md +18 -0
  9. package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
  10. package/corpus/templates/clips/app/routes/share.$shareId.tsx +110 -28
  11. package/corpus/templates/clips/changelog/2026-09-25-shared-clips-remember-your-sidebar-choice-and-help-new-viewe.md +6 -0
  12. package/corpus/templates/content/CHANGELOG.md +4 -0
  13. package/corpus/templates/design/CHANGELOG.md +30 -0
  14. package/corpus/templates/mail/CHANGELOG.md +12 -0
  15. package/corpus/templates/slides/CHANGELOG.md +26 -0
  16. package/dist/automation/index.d.ts +1 -1
  17. package/dist/client/auth/AuthPage.d.ts +1 -0
  18. package/dist/client/auth/AuthPage.js +10 -4
  19. package/dist/client/org/OrgSwitcher.d.ts +1 -1
  20. package/dist/client/org/OrgSwitcher.js +3 -3
  21. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  22. package/dist/notifications/routes.d.ts +3 -3
  23. package/dist/secrets/routes.d.ts +6 -6
  24. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  25. package/dist/server/onboarding-html.js +56 -0
  26. package/docs/content/cross-app-sso.mdx +27 -15
  27. package/package.json +4 -4
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 9957
34
+ - template files: 9959
@@ -7,10 +7,13 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
7
7
 
8
8
  ### Improved
9
9
 
10
+ - Analytics sidebar navigation and footer controls align consistently, with full-width dividers.
10
11
  - The Sessions list hides 0m recordings by default, with a filter to include them.
11
12
 
12
13
  ### Fixed
13
14
 
15
+ - Dashboard emails now match the live dashboard's stacked area chart totals.
16
+ - Editing a dashboard panel no longer brings back a panel you just deleted, and conflicting saves now show a clear error instead of a server error
14
17
  - Session recordings no longer cut off partway through when the daily recording budget runs low; new recordings wait for room instead.
15
18
 
16
19
  ## 2026-09-23
@@ -3,6 +3,17 @@
3
3
  All notable user-facing changes to Assets are documented here. Open it any
4
4
  time from the command menu (Cmd+K → "What's new") or from Settings.
5
5
 
6
+ ## 2026-09-24
7
+
8
+ ### Added
9
+
10
+ - Builder-connected workspaces can generate brand-safe images and videos in Assets.
11
+
12
+ ### Fixed
13
+
14
+ - Video generation avoids duplicate Gemini jobs when a start response is lost
15
+ - Video generation runs recover after temporary provider or storage failures.
16
+
6
17
  ## 2026-09-23
7
18
 
8
19
  ### Fixed
@@ -3,6 +3,13 @@
3
3
  All notable user-facing changes to Agent-Native Calendar are documented here. Open it any
4
4
  time from the command menu (Cmd+K → "What's new") or from Settings.
5
5
 
6
+ ## 2026-09-24
7
+
8
+ ### Fixed
9
+
10
+ - Calendar no longer shows an internal server error banner when a shared workspace Google connection can't be authorized
11
+ - Guest inputs wrap beneath existing guests so the active input stays visible
12
+
6
13
  ## 2026-09-23
7
14
 
8
15
  ### Improved
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-24
4
+ ---
5
+
6
+ Calendar no longer shows an internal server error banner when a shared workspace Google connection can't be authorized
@@ -1,20 +1,11 @@
1
- import { emit } from "@agent-native/core/event-bus";
2
1
  import { readBody, getSession } from "@agent-native/core/server";
3
2
  import {
4
3
  defineEventHandler,
5
- getQuery,
6
4
  getRouterParam,
7
5
  setResponseStatus,
8
- setResponseHeader,
9
6
  type H3Event,
10
7
  } from "h3";
11
8
 
12
- import { ensureOrganizerInAttendees } from "../../actions/event-action-helpers.js";
13
- import type { CalendarEvent } from "../../shared/api.js";
14
- import {
15
- prepareZoomMeetingPatch,
16
- shouldAutoAddGoogleMeet,
17
- } from "../lib/event-video-conferencing.js";
18
9
  import * as googleCalendar from "../lib/google-calendar.js";
19
10
 
20
11
  async function uEmail(event: H3Event): Promise<string> {
@@ -65,160 +56,6 @@ function handleError(event: H3Event, error: any) {
65
56
  return { error: error.message };
66
57
  }
67
58
 
68
- export const listEvents = defineEventHandler(async (event: H3Event) => {
69
- try {
70
- const email = await uEmail(event);
71
- const query = getQuery(event);
72
- const from = query.from as string | undefined;
73
- const to = query.to as string | undefined;
74
- const connected = await googleCalendar.isConnected(email);
75
-
76
- if (!connected) {
77
- return [];
78
- }
79
-
80
- if (!from || !to) {
81
- return [];
82
- }
83
-
84
- const overlayEmailsParam = query.overlayEmails as string | undefined;
85
-
86
- const { events: googleEvents, errors } = await googleCalendar.listEvents(
87
- from,
88
- to,
89
- email,
90
- );
91
-
92
- if (googleEvents.length === 0 && errors.length > 0) {
93
- setResponseStatus(event, 502);
94
- return {
95
- error: errors.map((e) => `${e.email}: ${e.error}`).join("; "),
96
- };
97
- }
98
-
99
- // Fetch overlay people's events in parallel
100
- let allEvents = googleEvents;
101
- if (overlayEmailsParam) {
102
- const overlayEmails = overlayEmailsParam
103
- .split(",")
104
- .filter(Boolean)
105
- .slice(0, 10);
106
- if (overlayEmails.length > 0) {
107
- const { events: overlayEvents } =
108
- await googleCalendar.listOverlayEvents(
109
- from,
110
- to,
111
- overlayEmails,
112
- email,
113
- );
114
- allEvents = [...googleEvents, ...overlayEvents];
115
- }
116
- }
117
-
118
- let events = allEvents;
119
- if (from) {
120
- const fromDate = new Date(from);
121
- events = events.filter((e) => new Date(e.end) >= fromDate);
122
- }
123
- if (to) {
124
- const toDate = new Date(to);
125
- events = events.filter((e) => new Date(e.start) <= toDate);
126
- }
127
-
128
- events.sort(
129
- (a, b) => new Date(a.start).getTime() - new Date(b.start).getTime(),
130
- );
131
- if (errors.length > 0) {
132
- setResponseHeader(event, "X-Account-Errors", JSON.stringify(errors));
133
- }
134
- return events;
135
- } catch (error: any) {
136
- console.error("[listEvents] Error:", error.message);
137
- setResponseStatus(event, 500);
138
- return { error: error.message };
139
- }
140
- });
141
-
142
- export const createEvent = defineEventHandler(async (event: H3Event) => {
143
- try {
144
- const email = await uEmail(event);
145
- const body = await readBody(event);
146
-
147
- if (!(await googleCalendar.isConnected(email))) {
148
- setResponseStatus(event, 400);
149
- return {
150
- error: "Google Calendar not connected. Connect via Settings first.",
151
- };
152
- }
153
-
154
- const acctEmail = await resolveAccountEmail(body.accountEmail, email);
155
-
156
- const { addGoogleMeet, addZoom, ...eventBody } = body;
157
- if (addGoogleMeet === true && addZoom === true) {
158
- setResponseStatus(event, 400);
159
- return { error: "Choose either Google Meet or Zoom, not both." };
160
- }
161
-
162
- const calEvent: CalendarEvent = {
163
- ...eventBody,
164
- id: "",
165
- source: "google",
166
- accountEmail: acctEmail,
167
- // Match Google Calendar UI: when inviting guests, include the
168
- // organizer/self email in attendees so they appear in Guests.
169
- attendees: ensureOrganizerInAttendees(eventBody.attendees, acctEmail),
170
- createdAt: new Date().toISOString(),
171
- updatedAt: new Date().toISOString(),
172
- };
173
-
174
- let zoomMeetingLink: string | undefined;
175
- if (addZoom === true) {
176
- const zoom = await prepareZoomMeetingPatch(email, calEvent);
177
- zoomMeetingLink = zoom.meetingLink;
178
- Object.assign(calEvent, zoom.patch);
179
- }
180
-
181
- const result = await googleCalendar.createEvent(calEvent, {
182
- account: { ownerEmail: email, accountEmail: acctEmail },
183
- addGoogleMeet: shouldAutoAddGoogleMeet(calEvent, {
184
- addGoogleMeet:
185
- typeof addGoogleMeet === "boolean" ? addGoogleMeet : undefined,
186
- addZoom: addZoom === true,
187
- }),
188
- });
189
- if (result.id) {
190
- calEvent.id = `google-${result.id}`;
191
- calEvent.googleEventId = result.id;
192
- }
193
- if (result.htmlLink) calEvent.htmlLink = result.htmlLink;
194
- if (result.meetLink) calEvent.hangoutLink = result.meetLink;
195
- if (result.conferenceData) calEvent.conferenceData = result.conferenceData;
196
- if (zoomMeetingLink) calEvent.meetingLink = zoomMeetingLink;
197
-
198
- try {
199
- emit(
200
- "calendar.event.created",
201
- {
202
- eventId: calEvent.id,
203
- title: calEvent.title || eventBody.title || "",
204
- startTime: calEvent.start,
205
- endTime: calEvent.end,
206
- attendees: calEvent.attendees ?? [],
207
- createdBy: email,
208
- },
209
- { owner: email },
210
- );
211
- } catch {
212
- // best-effort
213
- }
214
-
215
- setResponseStatus(event, 201);
216
- return calEvent;
217
- } catch (error: any) {
218
- return handleError(event, error);
219
- }
220
- });
221
-
222
59
  export const rsvpEvent = defineEventHandler(async (event: H3Event) => {
223
60
  try {
224
61
  const email = await uEmail(event);
@@ -74,6 +74,26 @@ async function resolveManagedCalendarClient(): Promise<ManagedCalendarClient | n
74
74
  return { email: credential.accountId, accessToken: credential.accessToken };
75
75
  }
76
76
 
77
+ /**
78
+ * A workspace connection can be registered and marked "connected" while its
79
+ * token still can't be resolved (revoked, mid-authorization, misconfigured
80
+ * credential). Callers that only need a yes/no read of connection status must
81
+ * see that as "not connected", not as a thrown error — otherwise a single
82
+ * flaky managed-token resolution turns every read action (list-events
83
+ * included) into a 500 instead of the same not-connected state the UI already
84
+ * shows.
85
+ */
86
+ async function resolveManagedCalendarClientOrNull(): Promise<ManagedCalendarClient | null> {
87
+ try {
88
+ return await resolveManagedCalendarClient();
89
+ } catch {
90
+ // coercion-ok: null is the same typed "not connected" result callers
91
+ // already get for "no managed connection configured" - isConnected and
92
+ // getConnectedAccounts never distinguish it from a genuine read success.
93
+ return null;
94
+ }
95
+ }
96
+
77
97
  const SCOPES = [
78
98
  "https://www.googleapis.com/auth/calendar.readonly",
79
99
  "https://www.googleapis.com/auth/calendar.events",
@@ -975,7 +995,20 @@ export async function getClientsForAccountsWithErrors(
975
995
  (account) => hasCalendarScope(account.tokens),
976
996
  );
977
997
  if (accounts.length === 0) {
978
- const managed = await resolveManagedCalendarClient();
998
+ // Unlike isConnected()/getAuthStatus() - plain status reads with nowhere
999
+ // to put a reason - this function already has an `errors` channel its
1000
+ // callers (listEvents, listGoogleCalendars) use to distinguish an empty
1001
+ // calendar from a read failure. Preserve the failure there instead of
1002
+ // coercing it into the same silent-empty shape as "no managed connection
1003
+ // configured", matching the sibling fallback in getClientsWithErrors.
1004
+ let managed: ManagedCalendarClient | null = null;
1005
+ let managedError: string | undefined;
1006
+ try {
1007
+ managed = await resolveManagedCalendarClient();
1008
+ } catch (err: any) {
1009
+ managedError =
1010
+ err?.message || "Workspace Google Calendar connection failed";
1011
+ }
979
1012
  if (!managed) {
980
1013
  if (accountEmails?.length) {
981
1014
  throw new Error(
@@ -984,7 +1017,9 @@ export async function getClientsForAccountsWithErrors(
984
1017
  }
985
1018
  return {
986
1019
  clients: [],
987
- errors: [],
1020
+ errors: managedError
1021
+ ? [{ email: "workspace", error: managedError }]
1022
+ : [],
988
1023
  requestedAccounts: [],
989
1024
  resolvedAccounts: [],
990
1025
  };
@@ -1225,7 +1260,7 @@ export async function isConnected(forEmail?: string): Promise<boolean> {
1225
1260
  if (!forEmail) return false;
1226
1261
  const accounts = await listOAuthAccountsByOwner("google", forEmail);
1227
1262
  if (accounts.some((account) => hasCalendarScope(account.tokens))) return true;
1228
- return Boolean(await resolveManagedCalendarClient());
1263
+ return Boolean(await resolveManagedCalendarClientOrNull());
1229
1264
  }
1230
1265
 
1231
1266
  export async function getConnectedAccounts(
@@ -1236,7 +1271,7 @@ export async function getConnectedAccounts(
1236
1271
  (account) => hasCalendarScope(account.tokens),
1237
1272
  );
1238
1273
  if (accounts.length > 0) return accounts.map((a) => a.accountId);
1239
- const managed = await resolveManagedCalendarClient();
1274
+ const managed = await resolveManagedCalendarClientOrNull();
1240
1275
  return managed ? [managed.email] : [];
1241
1276
  }
1242
1277
 
@@ -1276,7 +1311,7 @@ export async function getAuthStatus(
1276
1311
  );
1277
1312
 
1278
1313
  if (oauthAccounts.length === 0) {
1279
- const managed = await resolveManagedCalendarClient();
1314
+ const managed = await resolveManagedCalendarClientOrNull();
1280
1315
  return managed
1281
1316
  ? { connected: true, accounts: [{ email: managed.email, shared: true }] }
1282
1317
  : { connected: false, accounts: [] };
@@ -3,6 +3,24 @@
3
3
  All notable user-facing changes to Clips are documented here. Open it any time
4
4
  from the command menu (Cmd+K → "What's new") or from Settings.
5
5
 
6
+ ## 2026-09-25
7
+
8
+ ### Improved
9
+
10
+ - Shared clips remember your sidebar choice and help new viewers understand why to sign up.
11
+
12
+ ## 2026-09-24
13
+
14
+ ### Improved
15
+
16
+ - Recording actions are easier to find and align clearly.
17
+ - Share links appear after uploads finish, when clips are ready to view.
18
+
19
+ ### Fixed
20
+
21
+ - Signed-in viewers see shared recordings in the Clips app shell with library breadcrumbs.
22
+ - The camera bubble stays visible while you reposition it before recording, then the popover resumes its inactive auto-hide after the drag ends.
23
+
6
24
  ## 2026-09-23
7
25
 
8
26
  ### Improved
@@ -359,6 +359,10 @@ const messages = {
359
359
  downloadForWindows: "Download for Windows",
360
360
  downloadForLinux: "Download for Linux",
361
361
  downloadDesktopApp: "Download desktop app",
362
+ commentSignupTitle: "Give your AI agent the full picture",
363
+ commentSignupContext: "Share transcript and timestamped frames in one link",
364
+ commentSignupFeedback: "Turn recorded feedback into clear next steps",
365
+ commentSignupDebug: "Capture console errors and failed requests",
362
366
  agentEmptyTitle: "Join the conversation",
363
367
  agentEmptyDescription:
364
368
  "Create a free Clips account to comment, react, and ask about this clip.",
@@ -17,6 +17,7 @@ import {
17
17
  DefaultSpinner,
18
18
  EnvironmentBadge,
19
19
  } from "@agent-native/core/client/ui";
20
+ import { usePersistentSidebarCollapsed } from "@agent-native/toolkit/app-shell";
20
21
  import {
21
22
  IconAlertTriangle,
22
23
  IconDeviceDesktop,
@@ -543,7 +544,10 @@ export default function ShareRoute() {
543
544
  // viewer. Its own tab strip is the only panel navigation; the page toolbar
544
545
  // stays focused on recording actions.
545
546
  const [panel, setPanel] = useState<SharePanel>("comments");
546
- const [sidePanelCollapsed, setSidePanelCollapsed] = useState(false);
547
+ const { collapsed: sidePanelCollapsed, setCollapsed: setSidePanelCollapsed } =
548
+ usePersistentSidebarCollapsed({
549
+ storageKey: "clips:share-sidebar-collapsed",
550
+ });
547
551
  const [descriptionExpanded, setDescriptionExpanded] = useState(false);
548
552
  const commentsSectionRef = useRef<HTMLElement | null>(null);
549
553
  const selectCommentsPanel = useCallback(() => {
@@ -555,7 +559,7 @@ export default function ShareRoute() {
555
559
  block: "start",
556
560
  });
557
561
  });
558
- }, []);
562
+ }, [setSidePanelCollapsed]);
559
563
  const [downloading, setDownloading] = useState(false);
560
564
  const [accessRequestSent, setAccessRequestSent] = useState(false);
561
565
  const [accessRequestError, setAccessRequestError] = useState<string | null>(
@@ -1837,32 +1841,49 @@ export default function ShareRoute() {
1837
1841
  ref={commentsSectionRef}
1838
1842
  className="flex min-h-0 flex-1 flex-col overflow-hidden px-3 pb-3 pt-2"
1839
1843
  >
1840
- <CommentsPanel
1841
- recordingId={recording.id}
1842
- comments={comments}
1843
- currentMs={playbackMs}
1844
- getCurrentMs={resolvePlaybackMs}
1845
- currentUserEmail={session?.email}
1846
- currentUserName={session?.name}
1847
- enableComments={recording.enableComments}
1848
- canComment={viewerCanComment}
1849
- onSeek={(ms) => playerRef.current?.seek(ms)}
1850
- onUnauthenticated={requireSignIn}
1851
- queryKey={[
1852
- "public-recording",
1853
- shareId,
1854
- password,
1855
- agentAccessToken,
1856
- session?.email ?? null,
1857
- ]}
1858
- selectComments={(d: any) => d?.data?.comments}
1859
- applyComments={(d: any, next) =>
1860
- d
1861
- ? { ...d, data: { ...(d.data ?? {}), comments: next } }
1862
- : d
1863
- }
1864
- presentation="inline"
1865
- />
1844
+ {!session &&
1845
+ sessionStatus !== "loading" &&
1846
+ sessionStatus !== "signing-out" &&
1847
+ comments.length === 0 ? (
1848
+ <PublicCommentsEmptyState
1849
+ signInHref={signInHref}
1850
+ onSignUp={() => {
1851
+ fireShareCtaClick("signup");
1852
+ openCreateAccount("comment");
1853
+ }}
1854
+ onSignIn={() => fireShareCtaClick("signin")}
1855
+ />
1856
+ ) : (
1857
+ <CommentsPanel
1858
+ recordingId={recording.id}
1859
+ comments={comments}
1860
+ currentMs={playbackMs}
1861
+ getCurrentMs={resolvePlaybackMs}
1862
+ currentUserEmail={session?.email}
1863
+ currentUserName={session?.name}
1864
+ enableComments={recording.enableComments}
1865
+ canComment={viewerCanComment}
1866
+ onSeek={(ms) => playerRef.current?.seek(ms)}
1867
+ onUnauthenticated={requireSignIn}
1868
+ queryKey={[
1869
+ "public-recording",
1870
+ shareId,
1871
+ password,
1872
+ agentAccessToken,
1873
+ session?.email ?? null,
1874
+ ]}
1875
+ selectComments={(d: any) => d?.data?.comments}
1876
+ applyComments={(d: any, next) =>
1877
+ d
1878
+ ? {
1879
+ ...d,
1880
+ data: { ...(d.data ?? {}), comments: next },
1881
+ }
1882
+ : d
1883
+ }
1884
+ presentation="inline"
1885
+ />
1886
+ )}
1866
1887
  </section>
1867
1888
  </TabsContent>
1868
1889
  ) : null}
@@ -2010,6 +2031,67 @@ function formatRecordedOn(
2010
2031
  }).format(date);
2011
2032
  }
2012
2033
 
2034
+ function PublicCommentsEmptyState({
2035
+ signInHref,
2036
+ onSignUp,
2037
+ onSignIn,
2038
+ }: {
2039
+ signInHref: string;
2040
+ onSignUp: () => void;
2041
+ onSignIn: () => void;
2042
+ }) {
2043
+ const t = useT();
2044
+
2045
+ return (
2046
+ <div className="mx-auto flex min-h-0 w-full max-w-md flex-1 flex-col justify-center gap-5 overflow-y-auto px-5 py-6">
2047
+ <div className="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
2048
+ <IconDeviceDesktop aria-hidden="true" className="size-5" />
2049
+ </div>
2050
+ <h2 className="text-base font-semibold tracking-tight">
2051
+ {t("sharePage.commentSignupTitle")}
2052
+ </h2>
2053
+ <ul className="space-y-3 text-sm leading-5 text-muted-foreground">
2054
+ <li className="flex items-start gap-3">
2055
+ <span
2056
+ aria-hidden="true"
2057
+ className="mt-2 size-1.5 shrink-0 rounded-full bg-primary"
2058
+ />
2059
+ <span>{t("sharePage.commentSignupContext")}</span>
2060
+ </li>
2061
+ <li className="flex items-start gap-3">
2062
+ <span
2063
+ aria-hidden="true"
2064
+ className="mt-2 size-1.5 shrink-0 rounded-full bg-primary"
2065
+ />
2066
+ <span>{t("sharePage.commentSignupFeedback")}</span>
2067
+ </li>
2068
+ <li className="flex items-start gap-3">
2069
+ <span
2070
+ aria-hidden="true"
2071
+ className="mt-2 size-1.5 shrink-0 rounded-full bg-primary"
2072
+ />
2073
+ <span>{t("sharePage.commentSignupDebug")}</span>
2074
+ </li>
2075
+ </ul>
2076
+ <div className="space-y-3">
2077
+ <Button type="button" className="w-full" onClick={onSignUp}>
2078
+ {t("signInPrompt.createAccount")}
2079
+ </Button>
2080
+ <p className="text-center text-xs text-muted-foreground">
2081
+ {t("sharePage.agentEmptySignInPrompt")}{" "}
2082
+ <a
2083
+ href={signInHref}
2084
+ onClick={onSignIn}
2085
+ className="font-medium text-foreground underline underline-offset-4 hover:no-underline"
2086
+ >
2087
+ {t("signInPrompt.signIn")}
2088
+ </a>
2089
+ </p>
2090
+ </div>
2091
+ </div>
2092
+ );
2093
+ }
2094
+
2013
2095
  function PublicAgentEmptyState({
2014
2096
  signupHref,
2015
2097
  signInHref,
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-09-25
4
+ ---
5
+
6
+ Shared clips remember your sidebar choice and help new viewers understand why to sign up.
@@ -27,6 +27,10 @@ time from the command menu (Cmd+K → "What's new").
27
27
 
28
28
  ## 2026-09-21
29
29
 
30
+ ### Added
31
+
32
+ - Choose Tabler icons, any emoji, or uploaded images for pages, collections, properties, views, callouts, and workspaces.
33
+
30
34
  ### Improved
31
35
 
32
36
  - Search now ranks exact and partial title matches above incidental matches in document bodies.
@@ -3,14 +3,44 @@
3
3
  All notable user-facing changes to Design are documented here. Open it any time
4
4
  from the command menu (Cmd+K → "What's new") or from Settings.
5
5
 
6
+ ## 2026-09-25
7
+
8
+ ### Fixed
9
+
10
+ - Cross-screen moves preserve Flex and Grid sizing through undo and redo.
11
+ - Moving a layer from one live app screen into another now lands it instead of failing with "Could not move that layer", and a failed move no longer leaves the layer stuck to the cursor.
12
+
6
13
  ## 2026-09-24
7
14
 
15
+ ### Added
16
+
17
+ - CSS margins can be adjusted on the canvas and in the layout inspector.
18
+ - Organization admins can review agent runs and summarize linked designs, slide decks, and analytics from Settings.
19
+ - Resized vectors can be point-edited, and the K scale tool shows a Scale section with a live factor.
20
+ - Share editable live canvases and apply collaborators' visual changes
21
+
8
22
  ### Improved
9
23
 
24
+ - A vector's stroke row shows Position beside Weight instead of a lone Weight field.
25
+ - Design reviews show the real canvas beside its conversation.
26
+ - Local visual edits stay scoped to the right design and recover cleanly when a screen reloads.
10
27
  - Pasted images now expose their sizing and adjustment controls in the inspector
11
28
 
12
29
  ### Fixed
13
30
 
31
+ - Double-clicking text or a vector on the board starts editing it, and clicking empty canvas deselects it.
32
+ - Interact opens a focused responsive preview without reloading the running design.
33
+ - Interrupted cross-screen moves now restore the layer and leave the canvas ready for the next edit.
34
+ - Keep gradient values visible and limit canvas paints to supported backgrounds
35
+ - Open pen paths no longer paint a filled triangle after Shift+X, matching Figma.
36
+ - Open Pen paths preserve their fill opacity when you finish editing them.
37
+ - Pen path edits save without being blocked by delayed local retry storage.
38
+ - Re-clicking the active responsive preview returns to the canvas even with pending edits
39
+ - SVG paste handles mixed-case markup safely and preserves opacity edits on open Pen paths
40
+ - SVG pastes keep valid namespace and fill data, and closing open paths preserves authored transparency.
41
+ - Text selection remains available during line-height editing, and pasted SVGs have unsafe external styles removed.
42
+ - Visual edit handoffs now accept current edit revisions reliably
43
+ - While editing text, click to place the caret and double-click a word to restyle just that word.
14
44
  - Editing a vector path preserves its fill and stroke when reopened or closed.
15
45
  - Pasted images, SVGs, and Design layers preserve more of their original content and placement.
16
46
 
@@ -7,8 +7,20 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
7
7
 
8
8
  ### Improved
9
9
 
10
+ - Add an All inbox tab that shows every inbox thread and can be hidden in tab settings.
11
+ - Priority sort keeps results when switching inbox tabs, and loading tabs show a skeleton
12
+ - The composer keeps its taller layout in a narrower window.
10
13
  - The compose window opens larger, leaving more room to write with quieter toolbar icons.
11
14
 
15
+ ### Fixed
16
+
17
+ - Gmail inboxes refresh reliably when push notifications are delayed.
18
+ - Priority sorting stays in place when you return to the inbox
19
+
20
+ ### Security
21
+
22
+ - Mail automations no longer fall back to shared deployment LLM keys; connect a provider in Settings to enable them.
23
+
12
24
  ## 2026-09-23
13
25
 
14
26
  ### Improved
@@ -3,6 +3,30 @@
3
3
  All notable user-facing changes to Agent-Native Slides are documented here. Open it any
4
4
  time from the command menu (Cmd+K → "What's new").
5
5
 
6
+ ## 2026-09-25
7
+
8
+ ### Fixed
9
+
10
+ - Word document imports now complete successfully.
11
+
12
+ ## 2026-09-24
13
+
14
+ ### Improved
15
+
16
+ - Slides completes multi-slide edits with fewer rounds of work
17
+
18
+ ### Fixed
19
+
20
+ - Answering or skipping setup questions keeps deck generation in its original chat.
21
+ - Deck generation progress follows its own run instead of unrelated chats.
22
+ - Editing text on a slide no longer rewrites or restyles the rest of the slide, and clicking into text without typing no longer saves anything. Clicking text on a Markdown-layout slide no longer converts the slide to HTML; ask the agent to edit that text for now.
23
+ - Guided questions stay available when a deck continuation cannot be delivered.
24
+ - Restyling slides preserves their text and layout
25
+
26
+ ### Security
27
+
28
+ - Slides image generation uses credentials saved for your account or workspace, not a shared deployment key.
29
+
6
30
  ## 2026-09-23
7
31
 
8
32
  ### Improved
@@ -11,6 +35,8 @@ time from the command menu (Cmd+K → "What's new").
11
35
 
12
36
  ### Fixed
13
37
 
38
+ - Decks opened from a link or reload no longer get stuck on the generating screen when no generation is running
39
+ - Decks with no design system now follow a theme chosen for their subject instead of a fixed default palette
14
40
  - Deck access errors leave the loading state and explain that the deck is unavailable.
15
41
  - Slide chat follows the current selection, and loaded slides remain visible when a background refresh fails
16
42
  - Slides home retries failed deck-list reads sooner.
@@ -186,8 +186,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
186
186
  duplicate?: undefined;
187
187
  eventId?: undefined;
188
188
  } | {
189
+ error?: undefined;
189
190
  accepted: boolean;
190
191
  duplicate: boolean;
191
192
  eventId: string;
192
- error?: undefined;
193
193
  }>;
@@ -33,6 +33,7 @@ export interface AuthPageProps {
33
33
  initialView: AuthView;
34
34
  appBasePath: string;
35
35
  homePath: string;
36
+ initialResumeHref?: string;
36
37
  workspaceRuntime: boolean;
37
38
  trackingApp: string;
38
39
  defaultLocale: string;
@@ -483,9 +483,11 @@ export function shouldStartWithLocalDev(pathname, search) {
483
483
  !path.endsWith("/sign-in"));
484
484
  }
485
485
  export function AuthPage(props) {
486
- const { authMode, googleOnly, initialPrompt, appBasePath, homePath, workspaceRuntime, trackingApp, defaultLocale, localeStorageKey, locales, localeMetadata, localeOptions, marketing, marketingLocales, brandMarkSrc, brandMarkLightSrc, githubUrl, showGoogle, organizationSsoEnabled = false, googleViaIdentitySso = false, signupLegalNotice, signupLocalModeNote, docsAuthUrl, identitySsoEnabled = false, identitySsoAuto = false, publicOAuthOrigin, workspaceGatewayReturnOrigin, googleAuthMode, builderPreviewLocalDevEnabled, } = props;
486
+ const { authMode, googleOnly, initialPrompt, appBasePath, homePath, initialResumeHref, workspaceRuntime, trackingApp, defaultLocale, localeStorageKey, locales, localeMetadata, localeOptions, marketing, marketingLocales, brandMarkSrc, brandMarkLightSrc, githubUrl, showGoogle, organizationSsoEnabled = false, googleViaIdentitySso = false, signupLegalNotice, signupLocalModeNote, docsAuthUrl, identitySsoEnabled = false, identitySsoAuto = false, publicOAuthOrigin, workspaceGatewayReturnOrigin, googleAuthMode, builderPreviewLocalDevEnabled, } = props;
487
487
  const [localePreference, setLocalePreference] = React.useState("system");
488
488
  const [locale, setLocale] = React.useState(defaultLocale);
489
+ const [browserLocationReady, setBrowserLocationReady] = React.useState(false);
490
+ React.useEffect(() => setBrowserLocationReady(true), []);
489
491
  const [localeMenuOpen, setLocaleMenuOpen] = React.useState(false);
490
492
  const [view, setView] = React.useState(props.initialView);
491
493
  const [messages, setMessages] = React.useState({});
@@ -544,9 +546,9 @@ export function AuthPage(props) {
544
546
  const apiPath = React.useCallback((path) => `${runtimeAppBasePath}${toPublicFrameworkPath(path, { publicPrefix: frameworkRoutePrefix() })}`, [runtimeAppBasePath]);
545
547
  const identityHref = React.useMemo(() => apiPath("/_agent-native/identity/login"), [apiPath]);
546
548
  const journey = React.useCallback(() => {
547
- if (typeof window === "undefined") {
549
+ if (typeof window === "undefined" || !browserLocationReady) {
548
550
  return signInJourney({
549
- at: `${runtimeAppBasePath}/`,
551
+ at: initialResumeHref ?? `${runtimeAppBasePath}/`,
550
552
  basePath: runtimeAppBasePath,
551
553
  homePath,
552
554
  });
@@ -560,8 +562,9 @@ export function AuthPage(props) {
560
562
  basePath: runtimeAppBasePath,
561
563
  homePath,
562
564
  });
563
- }, [homePath, runtimeAppBasePath]);
565
+ }, [browserLocationReady, homePath, initialResumeHref, runtimeAppBasePath]);
564
566
  const resumeHref = React.useCallback(() => journey().resumeHref, [journey]);
567
+ const identityLoginHref = React.useMemo(() => `${identityHref}?${new URLSearchParams({ return: resumeHref() }).toString()}`, [identityHref, resumeHref]);
565
568
  const identityBootstrapHref = React.useCallback((target) => {
566
569
  const safeTarget = target || resumeHref();
567
570
  if (!identitySsoEnabled || isAgentNativeDesktop())
@@ -1976,6 +1979,9 @@ export function AuthPage(props) {
1976
1979
  ? false
1977
1980
  : shouldHideAuthSubtitle(view, localDevAvailable) }, usesMarketingWelcome ? t("welcomeSubtitle") : t(keys.subtitle)),
1978
1981
  React.createElement("p", { className: `upgrade-note ${upgradeVisible ? "show" : ""}`, id: "upgrade-note", "data-upgrade-copy": t("upgradeCopy"), "data-i18n-data-upgrade-copy": "upgradeCopy" }, upgradeVisible ? t("upgradeCopy") : null),
1982
+ identitySsoEnabled && !googleOnly ? (React.createElement("div", { className: "identity-sso-entry", id: "identity-sso-entry" },
1983
+ React.createElement("a", { className: "btn-primary btn-identity-sso", id: "identity-sso-btn", href: identityLoginHref, "aria-describedby": "identity-sso-hint", "data-i18n": "continueWithAgentNative" }, t("continueWithAgentNative")),
1984
+ React.createElement("p", { className: "identity-sso-hint", id: "identity-sso-hint", "data-i18n": "identitySsoHint" }, t("identitySsoHint")))) : null,
1979
1985
  React.createElement("div", { className: "local-dev-signin", id: "local-dev-signin", hidden: !localDevAvailable },
1980
1986
  React.createElement("button", { type: "button", className: "btn-local-dev btn-primary", id: "local-dev-btn", title: t("localDevDescription"), "data-i18n": "localDevButton", "data-i18n-title": "localDevDescription", "aria-describedby": "local-dev-description", disabled: localDevBusy, onClick: () => void handleLocalDev() }, localDevBusy ? t("localDevSigningIn") : t("localDevButton")),
1981
1987
  React.createElement("p", { className: "local-dev-description", id: "local-dev-description" },
@@ -10,7 +10,7 @@ export interface OrgSwitcherProps {
10
10
  className?: string;
11
11
  /** Hide entirely when the user only belongs to one org. Default: false. */
12
12
  hideWhenSingle?: boolean;
13
- /** Keep the switcher's button height reserved while org state is loading. */
13
+ /** Keep the switcher's slot reserved when there is no organization to show. */
14
14
  reserveSpace?: boolean;
15
15
  /**
16
16
  * Icon-only trigger for collapsed sidebar rails. The popover — and with it
@@ -42,8 +42,8 @@ function organizationSettingsPath(path) {
42
42
  function ReservedOrgSwitcherSpace({ className }) {
43
43
  return _jsx("div", { "aria-hidden": "true", className: `h-8 ${className ?? ""}` });
44
44
  }
45
- function OrgSwitcherLoadingPlaceholder({ className }) {
46
- return (_jsxs("button", { type: "button", disabled: true, "aria-label": "Loading organization", className: `${SWITCHER_BUTTON_CLASS} animate-pulse ${className ?? ""}`, children: [_jsx(IconBriefcase, { className: "h-3.5 w-3.5 shrink-0 opacity-60" }), _jsx("span", { className: "h-3 min-w-0 flex-1 rounded-sm bg-muted-foreground/20" }), _jsx(IconSelector, { className: "h-3 w-3 shrink-0 opacity-30" })] }));
45
+ function OrgSwitcherLoadingPlaceholder({ className, compact, }) {
46
+ return (_jsx("button", { type: "button", disabled: true, "aria-label": "Loading organization", className: `${compact ? COMPACT_SWITCHER_BUTTON_CLASS : SWITCHER_BUTTON_CLASS} animate-pulse ${className ?? ""}`, children: compact ? (_jsx("span", { className: "size-3.5 rounded-sm bg-muted-foreground/20" })) : (_jsxs(_Fragment, { children: [_jsx(IconBriefcase, { className: "h-3.5 w-3.5 shrink-0 opacity-60" }), _jsx("span", { className: "h-3 min-w-0 flex-1 rounded-sm bg-muted-foreground/20" }), _jsx(IconSelector, { className: "h-3 w-3 shrink-0 opacity-30" })] })) }));
47
47
  }
48
48
  /**
49
49
  * Compact org switcher button. Shows the active org (or "Personal" when the
@@ -83,7 +83,7 @@ export function OrgSwitcher({ className, hideWhenSingle, reserveSpace, compact,
83
83
  await signOut();
84
84
  };
85
85
  if (!org) {
86
- return reserveSpace && isLoading ? (_jsx(OrgSwitcherLoadingPlaceholder, { className: className })) : null;
86
+ return isLoading ? (_jsx(OrgSwitcherLoadingPlaceholder, { className: className, compact: compact })) : null;
87
87
  }
88
88
  const orgs = org.orgs ?? [];
89
89
  const pendingInvitations = org.pendingInvitations ?? [];
@@ -44,9 +44,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
44
44
  url: string;
45
45
  id: string;
46
46
  provider: string;
47
- error?: undefined;
48
47
  configured?: undefined;
49
48
  connectPath?: undefined;
49
+ error?: undefined;
50
50
  } | {
51
51
  url?: undefined;
52
52
  id?: undefined;
@@ -11,14 +11,14 @@
11
11
  * DELETE /_agent-native/notifications/:id — delete
12
12
  */
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
- error?: undefined;
15
14
  count: number;
16
15
  updated?: undefined;
16
+ error?: undefined;
17
17
  ok?: undefined;
18
18
  } | {
19
- error?: undefined;
20
19
  count?: undefined;
21
20
  updated: number;
21
+ error?: undefined;
22
22
  ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
26
26
  error: string;
27
27
  ok?: undefined;
28
28
  } | {
29
- error?: undefined;
30
29
  count?: undefined;
31
30
  updated?: undefined;
31
+ error?: undefined;
32
32
  ok: boolean;
33
33
  }>>;
@@ -60,17 +60,17 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
60
60
  ok?: undefined;
61
61
  status?: undefined;
62
62
  } | {
63
+ error?: undefined;
63
64
  ok: boolean;
64
65
  status: string;
65
- error?: undefined;
66
66
  } | {
67
67
  ok?: undefined;
68
68
  error: string;
69
69
  removed?: undefined;
70
70
  } | {
71
+ error?: undefined;
71
72
  ok: boolean;
72
73
  removed: boolean;
73
- error?: undefined;
74
74
  }>>;
75
75
  /**
76
76
  * POST /_agent-native/secrets/:key/test — validate an optional candidate value
@@ -81,13 +81,13 @@ export declare function createTestSecretHandler(): import("h3").EventHandlerWith
81
81
  error: string;
82
82
  note?: undefined;
83
83
  } | {
84
+ error?: undefined;
84
85
  ok: boolean;
85
86
  note?: undefined;
86
- error?: undefined;
87
87
  } | {
88
+ error?: undefined;
88
89
  ok: boolean;
89
90
  note: string;
90
- error?: undefined;
91
91
  } | {
92
92
  note?: undefined;
93
93
  ok: boolean;
@@ -119,11 +119,11 @@ export interface AdHocSecretPayload {
119
119
  export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
120
120
  error: string;
121
121
  } | {
122
+ error?: undefined;
122
123
  ok: boolean;
123
124
  key: string;
124
- error?: undefined;
125
125
  } | {
126
+ error?: undefined;
126
127
  ok: boolean;
127
128
  removed: boolean;
128
- error?: undefined;
129
129
  }>>;
@@ -9,7 +9,7 @@ export declare function createAgentEngineOllamaModelsHandler(): import("h3").Eve
9
9
  error: string;
10
10
  models?: undefined;
11
11
  } | {
12
+ error?: undefined;
12
13
  ok: boolean;
13
14
  models: string[];
14
- error?: undefined;
15
15
  }>>;
@@ -17,6 +17,7 @@ import { NATIVE_AUTH_COPY } from "../shared/auth-copy.js";
17
17
  import { docsUrl } from "../shared/docs-url.js";
18
18
  import { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, ENVIRONMENT_BETA_HOSTS, } from "../shared/environment-lanes.js";
19
19
  import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, } from "../shared/password-policy.js";
20
+ import { signInJourney } from "../shared/sign-in-journey.js";
20
21
  import { AGENT_NATIVE_SOCIAL_IMAGE_ALT, AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT, AGENT_NATIVE_SOCIAL_IMAGE_PATH, AGENT_NATIVE_SOCIAL_IMAGE_TYPE, AGENT_NATIVE_SOCIAL_IMAGE_WIDTH, withAgentNativeSocialImageCacheBuster, } from "../shared/social-meta.js";
21
22
  import { getAppBasePathFromViteEnv, normalizeAppBasePath, } from "./app-base-path.js";
22
23
  import { AUTH_MARKETING_LOCALE_COPY, } from "./auth-marketing-locales.js";
@@ -119,6 +120,8 @@ const EN_AUTH_COPY = {
119
120
  localDevSigningIn: "Signing in locally…",
120
121
  localDevFailed: "Local development sign-in is unavailable.",
121
122
  localDevFullOptions: "Show full sign in options",
123
+ continueWithAgentNative: "Continue with Agent-Native",
124
+ identitySsoHint: "Use the same verified email you use in your other Agent-Native apps.",
122
125
  openSource: "FREE & OPEN SOURCE",
123
126
  learnMore: "Learn more",
124
127
  useOwnGoogleClient: "Use your own Google OAuth client:",
@@ -192,6 +195,8 @@ const AUTH_LOCALE_COPY = {
192
195
  localDevSigningIn: "正在本地登录…",
193
196
  localDevFailed: "本地开发登录不可用。",
194
197
  localDevFullOptions: "显示完整登录选项",
198
+ continueWithAgentNative: "使用 Agent-Native 继续",
199
+ identitySsoHint: "使用你在其他 Agent-Native 应用中验证过的相同邮箱。",
195
200
  openSource: "100% 免费且开源",
196
201
  learnMore: "了解更多",
197
202
  useOwnGoogleClient: "使用你自己的 Google OAuth 客户端:",
@@ -263,6 +268,8 @@ const AUTH_LOCALE_COPY = {
263
268
  localDevSigningIn: "正在本機登入…",
264
269
  localDevFailed: "本機開發登入無法使用。",
265
270
  localDevFullOptions: "顯示完整登入選項",
271
+ continueWithAgentNative: "使用 Agent-Native 繼續",
272
+ identitySsoHint: "請使用你在其他 Agent-Native 應用中驗證過的相同電子郵件。",
266
273
  openSource: "100% 免費且開源",
267
274
  learnMore: "深入瞭解",
268
275
  useOwnGoogleClient: "使用你自己的 Google OAuth 用戶端:",
@@ -334,6 +341,8 @@ const AUTH_LOCALE_COPY = {
334
341
  localDevSigningIn: "Iniciando sesión localmente…",
335
342
  localDevFailed: "El inicio de sesión de desarrollo local no está disponible.",
336
343
  localDevFullOptions: "Mostrar todas las opciones de inicio de sesión",
344
+ continueWithAgentNative: "Continuar con Agent-Native",
345
+ identitySsoHint: "Usa el mismo correo verificado que en tus otras apps de Agent-Native.",
337
346
  openSource: "100% gratis y de código abierto",
338
347
  learnMore: "Más información",
339
348
  useOwnGoogleClient: "Usa tu propio cliente de Google OAuth:",
@@ -405,6 +414,8 @@ const AUTH_LOCALE_COPY = {
405
414
  localDevSigningIn: "Connexion locale…",
406
415
  localDevFailed: "La connexion de développement local est indisponible.",
407
416
  localDevFullOptions: "Afficher toutes les options de connexion",
417
+ continueWithAgentNative: "Continuer avec Agent-Native",
418
+ identitySsoHint: "Utilisez la même adresse e-mail vérifiée que dans vos autres applications Agent-Native.",
408
419
  openSource: "100 % gratuit et open source",
409
420
  learnMore: "En savoir plus",
410
421
  useOwnGoogleClient: "Utilisez votre propre client Google OAuth :",
@@ -476,6 +487,8 @@ const AUTH_LOCALE_COPY = {
476
487
  localDevSigningIn: "Lokale Anmeldung…",
477
488
  localDevFailed: "Die lokale Entwicklungsanmeldung ist nicht verfügbar.",
478
489
  localDevFullOptions: "Alle Anmeldeoptionen anzeigen",
490
+ continueWithAgentNative: "Mit Agent-Native fortfahren",
491
+ identitySsoHint: "Verwende dieselbe bestätigte E-Mail-Adresse wie in deinen anderen Agent-Native-Apps.",
479
492
  openSource: "100 % kostenlos und Open Source",
480
493
  learnMore: "Mehr erfahren",
481
494
  useOwnGoogleClient: "Eigenen Google-OAuth-Client verwenden:",
@@ -547,6 +560,8 @@ const AUTH_LOCALE_COPY = {
547
560
  localDevSigningIn: "ローカルでサインイン中…",
548
561
  localDevFailed: "ローカル開発のサインインは利用できません。",
549
562
  localDevFullOptions: "完全なサインイン オプションを表示",
563
+ continueWithAgentNative: "Agent-Native で続行",
564
+ identitySsoHint: "他の Agent-Native アプリで確認済みの同じメールアドレスを使用してください。",
550
565
  openSource: "100% 無料でオープンソース",
551
566
  learnMore: "詳細を見る",
552
567
  useOwnGoogleClient: "自分の Google OAuth クライアントを使用:",
@@ -618,6 +633,8 @@ const AUTH_LOCALE_COPY = {
618
633
  localDevSigningIn: "로컬로 로그인하는 중…",
619
634
  localDevFailed: "로컬 개발 로그인을 사용할 수 없습니다.",
620
635
  localDevFullOptions: "전체 로그인 옵션 보기",
636
+ continueWithAgentNative: "Agent-Native로 계속",
637
+ identitySsoHint: "다른 Agent-Native 앱에서 인증한 것과 같은 이메일 주소를 사용하세요.",
621
638
  openSource: "100% 무료 오픈 소스",
622
639
  learnMore: "자세히 알아보기",
623
640
  useOwnGoogleClient: "내 Google OAuth 클라이언트 사용:",
@@ -689,6 +706,8 @@ const AUTH_LOCALE_COPY = {
689
706
  localDevSigningIn: "Entrando localmente…",
690
707
  localDevFailed: "O login de desenvolvimento local não está disponível.",
691
708
  localDevFullOptions: "Mostrar todas as opções de login",
709
+ continueWithAgentNative: "Continuar com Agent-Native",
710
+ identitySsoHint: "Use o mesmo email verificado nos outros apps Agent-Native.",
692
711
  openSource: "100% grátis e open source",
693
712
  learnMore: "Saiba mais",
694
713
  useOwnGoogleClient: "Use seu próprio cliente Google OAuth:",
@@ -760,6 +779,8 @@ const AUTH_LOCALE_COPY = {
760
779
  localDevSigningIn: "स्थानीय रूप से साइन इन हो रहा है…",
761
780
  localDevFailed: "स्थानीय विकास साइन-इन उपलब्ध नहीं है।",
762
781
  localDevFullOptions: "साइन-इन के सभी विकल्प दिखाएं",
782
+ continueWithAgentNative: "Agent-Native के साथ जारी रखें",
783
+ identitySsoHint: "दूसरे Agent-Native ऐप्स में सत्यापित किया गया वही ईमेल इस्तेमाल करें।",
763
784
  openSource: "100% मुफ्त और open source",
764
785
  learnMore: "और जानें",
765
786
  useOwnGoogleClient: "अपना Google OAuth client उपयोग करें:",
@@ -831,6 +852,8 @@ const AUTH_LOCALE_COPY = {
831
852
  localDevSigningIn: "جارٍ تسجيل الدخول محليًا…",
832
853
  localDevFailed: "تسجيل دخول التطوير المحلي غير متاح.",
833
854
  localDevFullOptions: "عرض خيارات تسجيل الدخول الكاملة",
855
+ continueWithAgentNative: "المتابعة باستخدام Agent-Native",
856
+ identitySsoHint: "استخدم عنوان البريد الإلكتروني نفسه الذي تم التحقق منه في تطبيقات Agent-Native الأخرى.",
834
857
  openSource: "مجاني ومفتوح المصدر 100%",
835
858
  learnMore: "معرفة المزيد",
836
859
  useOwnGoogleClient: "استخدم عميل Google OAuth الخاص بك:",
@@ -948,6 +971,18 @@ export function getOnboardingHtml(opts = {}) {
948
971
  const configuredAppBasePath = getAppBasePathFromViteEnv();
949
972
  const appBasePath = configuredAppBasePath || workspaceBasePathFromRequest(opts.requestPath);
950
973
  const appHomePath = resolveAppHomePath(getAppConfig().app, getAppConfig().workspace);
974
+ const requestUrl = new URL(opts.requestPath || `${appBasePath}/`, "https://agent-native.local");
975
+ const requestPathname = requestUrl.pathname;
976
+ const isRootRequest = requestPathname === appBasePath || requestPathname === `${appBasePath}/`;
977
+ const initialResumeHref = signInJourney({
978
+ at: isRootRequest
979
+ ? `${appBasePath}/`
980
+ : `${requestPathname}${requestUrl.search}${requestUrl.hash}`,
981
+ continuation: isRootRequest ? null : requestUrl.searchParams.get("c"),
982
+ legacyReturn: isRootRequest ? null : requestUrl.searchParams.get("return"),
983
+ basePath: appBasePath,
984
+ homePath: appHomePath,
985
+ }).resumeHref;
951
986
  const workspaceRuntime = isWorkspaceRuntime();
952
987
  const trackingApp = getAppConfig().app.slug ??
953
988
  getAppConfig().app.template ??
@@ -1399,6 +1434,7 @@ export function getOnboardingHtml(opts = {}) {
1399
1434
  initialView: initialAuthView(opts, authMode, googleOnly),
1400
1435
  appBasePath,
1401
1436
  homePath: appHomePath,
1437
+ initialResumeHref,
1402
1438
  workspaceRuntime,
1403
1439
  trackingApp,
1404
1440
  defaultLocale: DEFAULT_LOCALE,
@@ -1694,6 +1730,7 @@ export function getOnboardingHtml(opts = {}) {
1694
1730
  .card.verifying #google-btn,
1695
1731
  .card.verifying #google-err,
1696
1732
  .card.verifying #auth-divider,
1733
+ .card.verifying #identity-sso-entry,
1697
1734
  .card.verifying #upgrade-note {
1698
1735
  display: none;
1699
1736
  }
@@ -1800,6 +1837,24 @@ export function getOnboardingHtml(opts = {}) {
1800
1837
  .local-dev-full-options[hidden] { display: none; }
1801
1838
  .full-auth-options { margin-top: 1rem; }
1802
1839
  .full-auth-options[hidden] { display: none; }
1840
+ .identity-sso-entry { margin: 1rem 0; }
1841
+ .btn-identity-sso {
1842
+ display: flex;
1843
+ align-items: center;
1844
+ justify-content: center;
1845
+ min-height: 2.75rem;
1846
+ padding: 0.75rem;
1847
+ text-align: center;
1848
+ text-decoration: none;
1849
+ }
1850
+ .identity-sso-hint {
1851
+ margin: 0.5rem 0 0;
1852
+ color: color-mix(in srgb, currentColor 50%, transparent);
1853
+ font-size: 0.75rem;
1854
+ line-height: 1.45;
1855
+ text-align: center;
1856
+ }
1857
+ html[data-agent-native-embedded="1"] #identity-sso-entry { display: none; }
1803
1858
  .sso-signin { margin-top: 0.75rem; }
1804
1859
  .legal-note {
1805
1860
  margin-top: 0.375rem;
@@ -1979,6 +2034,7 @@ export function getOnboardingHtml(opts = {}) {
1979
2034
  .card.magic-link-complete #google-signin,
1980
2035
  .card.magic-link-complete #auth-divider,
1981
2036
  .card.magic-link-complete #auth-tabs,
2037
+ .card.magic-link-complete #identity-sso-entry,
1982
2038
  .card.magic-link-complete #upgrade-note,
1983
2039
  .card.magic-link-complete .form {
1984
2040
  display: none;
@@ -49,8 +49,13 @@ eligibility check is unavailable, ordinary per-app sign-in remains available.
49
49
 
50
50
  ### Server-side federation
51
51
 
52
- The remaining broker and server-side callers use a short-lived,
53
- server-verified protocol. It is not exposed as a browser login control.
52
+ Canonical Agent-Native sign-in pages expose an explicit **Continue with
53
+ Agent-Native** action. It starts the same short-lived, server-verified protocol
54
+ used by broker and server-side callers. A person with an active Dispatch
55
+ session can continue; otherwise, Dispatch asks them to authenticate there. On
56
+ first use, they may need to establish a Dispatch identity. The target app then
57
+ links or creates its local user using the verified email and issues its own
58
+ session.
54
59
 
55
60
  1. The caller requests an exact registered app binding.
56
61
  2. Dispatch authenticates the workspace identity and returns a short-lived,
@@ -100,10 +105,11 @@ credentials.
100
105
 
101
106
  <code>AGENT_NATIVE_IDENTITY_HUB_URL</code> identifies the hub for the
102
107
  server-side and browser paths when a self-hosted app needs an explicit Dispatch
103
- deployment. The <code>browser.identity-sso</code> rollout flag keeps browser
104
- federation opt-in. When enabled, a signed-in Dispatch user gets a silent
105
- <code>prompt=none</code> probe on app load and is provisioned without seeing a
106
- second login wall; the ordinary local sign-in form remains the fallback.
108
+ deployment. Exact registered first-party apps can show the explicit browser
109
+ action. The <code>browser.identity-sso</code> rollout flag controls silent
110
+ <code>prompt=none</code> sign-in and post-local-login bootstrap; keep that
111
+ automatic path limited to the selected rollout cohort. The local sign-in form
112
+ remains available as a fallback.
107
113
 
108
114
  For a self-hosted Dispatch deployment, register every custom app with its
109
115
  exact app ID, client ID, HTTPS origin, callback path, and
@@ -138,22 +144,27 @@ After deployment:
138
144
 
139
145
  1. Verify that normal email, password, magic-link, and configured provider
140
146
  sign-in continue to work in a browser.
141
- 2. With <code>browser.identity-sso</code> off, verify ordinary local sign-in.
142
- With it on, verify the silent <code>prompt=none</code> probe and local-form
143
- fallback without introducing a wildcard cookie.
147
+ 2. With <code>browser.identity-sso</code> off, verify the explicit Continue with
148
+ Agent-Native action and ordinary local sign-in. With it on, verify the silent
149
+ <code>prompt=none</code> probe and local-form fallback without introducing a
150
+ wildcard cookie.
144
151
  3. If Desktop workspace SSO is enabled, verify one exact registered app from
145
152
  the explicit Desktop Settings action.
146
153
  4. If organization federation is enabled, verify the exact organization ID
147
154
  and membership role in the target app.
148
155
 
149
- To roll back the remaining server-side or Desktop path, disable the relevant
150
- feature flag and remove <code>AGENT_NATIVE_IDENTITY_HUB_URL</code> from a
151
- self-hosted deployment, then redeploy. The additive identity and organization
152
- records remain available for ordinary local auth.
156
+ To roll back the silent browser or Desktop path, disable its feature flag. To
157
+ remove the explicit browser action from a self-hosted app, remove
158
+
159
+ <code>AGENT_NATIVE_IDENTITY_HUB_URL</code> or its exact app registration, then
160
+ redeploy. Local sign-in remains available, and additive identity and
161
+ organization records remain intact.
153
162
 
154
163
  ## Browser identity SSO rollout runbook
155
164
 
156
- The browser path is intentionally a rollout, not a default switch in core.
165
+ The silent browser path is intentionally a rollout, not a default switch in
166
+ core. The explicit Continue with Agent-Native action is available to exact
167
+ registered first-party clients and does not enable silent sign-in.
157
168
  For one canonical `*.agent-native.com` app:
158
169
 
159
170
  1. Keep `browser.identity-sso` disabled for the general population and enable
@@ -162,7 +173,8 @@ For one canonical `*.agent-native.com` app:
162
173
  Confirm the silent `prompt=none` probe returns the same verified account and
163
174
  that the app lands in its normal local session.
164
175
  3. Repeat with no active hub session. Confirm `login_required` returns to the
165
- ordinary local sign-in form, with no Agent-Native sign-in button.
176
+ ordinary local sign-in form and the explicit action still lets a person
177
+ authenticate with Dispatch when they choose it.
166
178
  4. Widen the cohort only after checking that logout and revocation behave as
167
179
  expected. The first enablement may require one-time revocation of existing
168
180
  sessions so stale local sessions do not mask the probe result.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.188.0-nightly-20260925170533",
3
+ "version": "0.188.0",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -508,9 +508,9 @@
508
508
  "yaml": "^2.9.0",
509
509
  "yjs": "^13.6.32",
510
510
  "zod": "^4.3.6",
511
- "@agent-native/toolkit": "^0.21.0-nightly-20260925170533",
512
- "@agent-native/agentkit": "^0.2.10-nightly-20260925170533",
513
- "@agent-native/recap-cli": "0.5.39"
511
+ "@agent-native/agentkit": "^0.2.10",
512
+ "@agent-native/recap-cli": "0.5.40",
513
+ "@agent-native/toolkit": "^0.21.0"
514
514
  },
515
515
  "devDependencies": {
516
516
  "@ai-sdk/anthropic": "^3.0.71",