@agent-native/core 0.136.5 → 0.137.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 (157) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/clips/app/components/import-menu.tsx +109 -0
  3. package/corpus/templates/clips/app/components/library/library-grid.tsx +8 -18
  4. package/corpus/templates/clips/app/components/library/library-layout.tsx +14 -50
  5. package/corpus/templates/clips/app/components/library/sort-menu.tsx +20 -6
  6. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +10 -34
  7. package/corpus/templates/clips/app/routes/_app.library._index.tsx +0 -2
  8. package/corpus/templates/clips/changelog/2026-08-04-cleaner-library-controls-with-an-icon-only-sort-button.md +6 -0
  9. package/corpus/templates/clips/changelog/2026-08-04-upload-and-loom-imports-now-live-under-one-import-menu.md +6 -0
  10. package/corpus/templates/content/actions/_database-utils.ts +6 -0
  11. package/corpus/templates/content/actions/bind-content-database-source-field.ts +273 -97
  12. package/corpus/templates/content/actions/change-content-database-source-role.ts +65 -29
  13. package/corpus/templates/content/actions/configure-document-property.ts +113 -32
  14. package/corpus/templates/content/actions/delete-content-database.ts +5 -7
  15. package/corpus/templates/content/actions/delete-document-property.ts +109 -62
  16. package/corpus/templates/content/actions/delete-document.ts +178 -36
  17. package/corpus/templates/content/actions/duplicate-database-item.ts +27 -0
  18. package/corpus/templates/content/actions/duplicate-database-items.ts +31 -1
  19. package/corpus/templates/content/actions/permanently-delete-document.ts +4 -6
  20. package/corpus/templates/content/actions/remove-database-items.ts +13 -0
  21. package/corpus/templates/content/actions/set-document-property.ts +69 -1
  22. package/corpus/templates/content/actions/upsert-database-item-by-key.ts +814 -0
  23. package/corpus/templates/content/parity/matrix.md +1 -1
  24. package/corpus/templates/content/parity/matrix.ts +1 -0
  25. package/corpus/templates/content/server/db/schema.ts +27 -0
  26. package/corpus/templates/content/server/plugins/db.ts +24 -0
  27. package/corpus/templates/design/app/components/design/BreakpointBar.tsx +122 -114
  28. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  29. package/corpus/templates/design/app/components/design/EditPanel.tsx +68 -34
  30. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +78 -4
  31. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +8 -0
  32. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +5 -2
  33. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +10 -9
  34. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +31 -69
  35. package/corpus/templates/design/app/components/design/inspector/design-icons.tsx +80 -106
  36. package/corpus/templates/design/app/components/design/multi-screen/types.ts +4 -0
  37. package/corpus/templates/design/app/hooks/use-editor-preferences.ts +75 -0
  38. package/corpus/templates/design/app/i18n-data.ts +8 -0
  39. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +61 -0
  40. package/corpus/templates/design/app/pages/design-editor/editor-preferences.ts +98 -0
  41. package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +491 -0
  42. package/corpus/templates/design/app/pages/design-editor/paste-placement.ts +133 -0
  43. package/corpus/templates/design/app/pages/design-editor/png-export-render.ts +8 -0
  44. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -12
  45. package/corpus/templates/design/changelog/2026-08-03-arrow-keys-now-reorder-a-layer-inside-an-auto-layout-frame.md +6 -0
  46. package/corpus/templates/design/changelog/2026-08-03-auto-layout-flow-icons-show-the-direction-at-a-glance-instea.md +6 -0
  47. package/corpus/templates/design/changelog/2026-08-03-auto-layout-icons-share-one-outlined-square-style-so-flow-ga.md +6 -0
  48. package/corpus/templates/design/changelog/2026-08-03-export-has-a-named-preview-section-you-can-expand-replacing-.md +6 -0
  49. package/corpus/templates/design/changelog/2026-08-03-export-preview-shows-the-selected-element-instead-of-the-who.md +6 -0
  50. package/corpus/templates/design/changelog/2026-08-03-inspector-sections-show-an-expand-indicator-so-it-is-clear-w.md +6 -0
  51. package/corpus/templates/design/changelog/2026-08-03-paste-lands-inside-a-selected-frame-instead-of-beside-it.md +6 -0
  52. package/corpus/templates/design/changelog/2026-08-03-the-add-breakpoint-button-sits-outside-the-breakpoint-select.md +6 -0
  53. package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-keep-space-below-the-continu.md +6 -0
  54. package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-share-the-chat-panel-s-backg.md +6 -0
  55. package/corpus/templates/design/changelog/2026-08-03-the-export-panel-no-longer-stacks-a-duplicate-export-block-o.md +6 -0
  56. package/corpus/templates/design/changelog/2026-08-03-the-text-tool-uses-a-t-icon-matching-the-letter-it-inserts.md +6 -0
  57. package/corpus/templates/factory/.env.example +17 -0
  58. package/corpus/templates/factory/AGENTS.md +27 -17
  59. package/corpus/templates/factory/README.md +19 -9
  60. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +90 -0
  61. package/corpus/templates/factory/actions/get-triage-config.ts +14 -3
  62. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +386 -0
  63. package/corpus/templates/factory/actions/list-factory-automations.ts +53 -0
  64. package/corpus/templates/factory/actions/list-triage-items.ts +7 -2
  65. package/corpus/templates/factory/actions/poll-github-sources.ts +203 -0
  66. package/corpus/templates/factory/actions/poll-sentry-errors.ts +161 -0
  67. package/corpus/templates/factory/actions/poll-slack-channel.ts +4 -2
  68. package/corpus/templates/factory/actions/run-factory-automation.ts +42 -0
  69. package/corpus/templates/factory/actions/save-factory-automation.ts +69 -0
  70. package/corpus/templates/factory/actions/save-triage-config.ts +28 -2
  71. package/corpus/templates/factory/actions/start-builder-for-item.ts +411 -0
  72. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +14 -3
  73. package/corpus/templates/factory/app/components/ui/checkbox.tsx +1 -0
  74. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +7 -0
  75. package/corpus/templates/factory/app/i18n/en-US.ts +35 -1
  76. package/corpus/templates/factory/app/routes/factory.tsx +462 -9
  77. package/corpus/templates/factory/changelog/2026-08-04-added-organization-owned-factory-automations-for-slack-bug-t.md +6 -0
  78. package/corpus/templates/factory/changelog/2026-08-04-automation-editor-controls-and-model-picker.md +6 -0
  79. package/corpus/templates/factory/netlify.toml +2 -2
  80. package/corpus/templates/factory/server/connectors/credentials.ts +6 -5
  81. package/corpus/templates/factory/server/connectors/slack.ts +133 -5
  82. package/corpus/templates/factory/server/db/schema.ts +6 -0
  83. package/corpus/templates/factory/server/lib/require-factory-automation.ts +65 -0
  84. package/corpus/templates/factory/server/plugins/agent-chat.ts +29 -11
  85. package/corpus/templates/factory/server/plugins/auth.ts +1 -0
  86. package/corpus/templates/factory/server/plugins/factory-migrations.ts +12 -0
  87. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +218 -67
  88. package/corpus/templates/factory/server/triage/builder-executor.ts +4 -0
  89. package/corpus/templates/factory/server/triage/contracts.ts +14 -1
  90. package/corpus/templates/factory/server/triage/github-client.ts +404 -0
  91. package/corpus/templates/factory/server/triage/metadata.ts +44 -0
  92. package/corpus/templates/factory/server/triage/pr-babysit.ts +5 -5
  93. package/corpus/templates/factory/server/triage/pr-policy.ts +144 -0
  94. package/corpus/templates/factory/server/triage/sentry-client.ts +180 -0
  95. package/corpus/templates/factory/server/triage/slack-client.ts +43 -0
  96. package/corpus/templates/factory/server/triage/slack-poller.ts +9 -2
  97. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +27 -0
  98. package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +14 -4
  99. package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -0
  100. package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -0
  101. package/corpus/templates/plan/actions/create-plan-design.ts +8 -6
  102. package/corpus/templates/plan/actions/create-prototype-plan.ts +8 -6
  103. package/corpus/templates/plan/actions/create-ui-plan.ts +7 -7
  104. package/corpus/templates/plan/actions/get-plan-blocks.ts +7 -1
  105. package/corpus/templates/plan/actions/validate-plan-input.ts +39 -0
  106. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +167 -11
  107. package/corpus/templates/plan/app/i18n/en-US.ts +1 -0
  108. package/corpus/templates/plan/changelog/2026-08-04-canvas-zoom-controls-now-explain-the-command-ctrl-plus-scrol.md +6 -0
  109. package/corpus/templates/plan/changelog/2026-08-04-pinch-to-zoom-is-now-supported-on-touchscreens-in-visual-pla.md +6 -0
  110. package/corpus/templates/plan/shared/plan-content.ts +17 -2
  111. package/dist/cli/skills-content/canvas.d.ts +3 -3
  112. package/dist/cli/skills-content/canvas.js +16 -5
  113. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  114. package/dist/cli/skills-content/visual-plan-skill.js +27 -0
  115. package/dist/cli/skills-content/wireframe.d.ts +2 -2
  116. package/dist/cli/skills-content/wireframe.js +7 -0
  117. package/dist/client/AgentPanel.js +23 -8
  118. package/dist/client/api-path.d.ts +6 -0
  119. package/dist/client/api-path.js +55 -0
  120. package/dist/client/onboarding/FirstRunOnboarding.d.ts +3 -0
  121. package/dist/client/onboarding/FirstRunOnboarding.js +93 -0
  122. package/dist/client/onboarding/first-run-enabled.d.ts +2 -0
  123. package/dist/client/onboarding/first-run-enabled.js +9 -0
  124. package/dist/client/onboarding/index.d.ts +2 -1
  125. package/dist/client/onboarding/index.js +1 -0
  126. package/dist/client/onboarding/use-onboarding.d.ts +6 -1
  127. package/dist/client/onboarding/use-onboarding.js +32 -1
  128. package/dist/client/route-state.js +10 -0
  129. package/dist/connections/catalog.d.ts +4 -4
  130. package/dist/connections/catalog.js +3 -3
  131. package/dist/db/index.d.ts +1 -1
  132. package/dist/db/index.js +1 -1
  133. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  134. package/dist/notifications/routes.d.ts +5 -5
  135. package/dist/observability/routes.d.ts +3 -3
  136. package/dist/onboarding/app-profile.d.ts +4 -0
  137. package/dist/onboarding/app-profile.js +378 -0
  138. package/dist/onboarding/index.d.ts +2 -1
  139. package/dist/onboarding/index.js +1 -0
  140. package/dist/onboarding/plugin.d.ts +2 -0
  141. package/dist/onboarding/plugin.js +21 -1
  142. package/dist/onboarding/types.d.ts +19 -0
  143. package/dist/progress/routes.d.ts +1 -1
  144. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  145. package/dist/provider-api/actions/provider-api.d.ts +12 -12
  146. package/dist/resources/handlers.d.ts +1 -1
  147. package/dist/server/auth.js +21 -1
  148. package/dist/server/better-auth-instance.d.ts +3 -2
  149. package/dist/server/better-auth-instance.js +9 -7
  150. package/dist/server/email-markdown.js +3 -8
  151. package/dist/server/transcribe-voice.d.ts +1 -1
  152. package/dist/shared/first-run-onboarding.d.ts +3 -0
  153. package/dist/shared/first-run-onboarding.js +3 -0
  154. package/dist/triggers/index.d.ts +3 -0
  155. package/dist/triggers/index.js +5 -0
  156. package/docs/content/cross-app-sso.mdx +26 -0
  157. package/package.json +1 -1
@@ -7,11 +7,13 @@
7
7
  * POST /_agent-native/onboarding/dismiss — dismiss the banner
8
8
  * GET /_agent-native/onboarding/dismissed — dismissed flag + allComplete
9
9
  */
10
- import { defineEventHandler, getMethod, getQuery, setResponseStatus, } from "h3";
10
+ import { deleteCookie, defineEventHandler, getMethod, getQuery, setResponseStatus, } from "h3";
11
11
  import { appStateGet, appStatePut } from "../application-state/store.js";
12
12
  import { getSession } from "../server/auth.js";
13
13
  import { awaitBootstrap, getH3App, markDefaultPluginProvided, } from "../server/framework-request-handler.js";
14
14
  import { runWithRequestContext } from "../server/request-context.js";
15
+ import { FIRST_RUN_ONBOARDING_COOKIE } from "../shared/first-run-onboarding.js";
16
+ import { getOnboardingAppProfile } from "./app-profile.js";
15
17
  import { registerDefaultOnboardingSteps } from "./default-steps.js";
16
18
  import { listOnboardingSteps } from "./registry.js";
17
19
  const ONBOARDING_PREFIX = "/_agent-native/onboarding";
@@ -89,6 +91,7 @@ export function createOnboardingPlugin(options = {}) {
89
91
  return async (nitroApp) => {
90
92
  markDefaultPluginProvided(nitroApp, "onboarding");
91
93
  await awaitBootstrap(nitroApp);
94
+ const appProfile = getOnboardingAppProfile(options.appId);
92
95
  if (!options.skipDefaultSteps) {
93
96
  registerDefaultOnboardingSteps();
94
97
  }
@@ -178,6 +181,23 @@ export function createOnboardingPlugin(options = {}) {
178
181
  return { dismissed: false, allComplete: false };
179
182
  }
180
183
  }));
184
+ // GET /_agent-native/onboarding/profile
185
+ getH3App(nitroApp).use(`${ONBOARDING_PREFIX}/profile`, defineEventHandler(async (event) => {
186
+ if (getMethod(event) !== "GET") {
187
+ setResponseStatus(event, 405);
188
+ return { error: "Method not allowed" };
189
+ }
190
+ return appProfile;
191
+ }));
192
+ // POST /_agent-native/onboarding/first-run/complete
193
+ getH3App(nitroApp).use(`${ONBOARDING_PREFIX}/first-run/complete`, defineEventHandler(async (event) => {
194
+ if (getMethod(event) !== "POST") {
195
+ setResponseStatus(event, 405);
196
+ return { error: "Method not allowed" };
197
+ }
198
+ deleteCookie(event, FIRST_RUN_ONBOARDING_COOKIE, { path: "/" });
199
+ return { ok: true };
200
+ }));
181
201
  };
182
202
  }
183
203
  /** Default plugin instance — mounted automatically when a template doesn't override. */
@@ -84,4 +84,23 @@ export interface OnboardingStepStatus {
84
84
  complete: boolean;
85
85
  methods: OnboardingMethod[];
86
86
  }
87
+ export interface OnboardingCapability {
88
+ /** Stable capability id used by the profile and analytics. */
89
+ id: string;
90
+ /** Short label shown in the setup choice and BYOK list. */
91
+ label: string;
92
+ /** Whether this capability blocks the app's normal setup. */
93
+ required: boolean;
94
+ /** Whether Builder's managed connection covers this capability. */
95
+ builderIncluded: boolean;
96
+ /** Compact description of the key or connection the BYOK path needs. */
97
+ keySummary: string;
98
+ /** Hover/focus explanation for why the capability exists. */
99
+ why: string;
100
+ }
101
+ export interface OnboardingAppProfile {
102
+ appId: string;
103
+ appName: string;
104
+ capabilities: OnboardingCapability[];
105
+ }
87
106
  //# sourceMappingURL=types.d.ts.map
@@ -15,7 +15,7 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- error?: undefined;
19
18
  ok: boolean;
19
+ error?: undefined;
20
20
  }>>;
21
21
  //# sourceMappingURL=routes.d.ts.map
@@ -75,6 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
+ id?: undefined;
79
+ provider?: undefined;
78
80
  deleted?: undefined;
79
81
  found?: undefined;
80
82
  providers: {
@@ -87,26 +89,24 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
87
89
  updatedAt: number;
88
90
  }[];
89
91
  count: number;
90
- provider?: undefined;
91
92
  registered?: undefined;
92
- id?: undefined;
93
93
  label?: undefined;
94
94
  message?: undefined;
95
95
  } | {
96
- deleted?: undefined;
97
96
  count?: undefined;
97
+ id?: undefined;
98
+ deleted?: undefined;
98
99
  providers?: undefined;
99
100
  found: boolean;
100
101
  provider: import("../custom-registry.js").CustomProviderConfig;
101
102
  registered?: undefined;
102
- id?: undefined;
103
103
  label?: undefined;
104
104
  message?: undefined;
105
105
  } | {
106
- deleted?: undefined;
107
106
  count?: undefined;
108
- providers?: undefined;
109
107
  provider?: undefined;
108
+ deleted?: undefined;
109
+ providers?: undefined;
110
110
  found: boolean;
111
111
  id: string;
112
112
  registered?: undefined;
@@ -114,20 +114,20 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
114
114
  message?: undefined;
115
115
  } | {
116
116
  count?: undefined;
117
+ provider?: undefined;
117
118
  found?: undefined;
118
119
  providers?: undefined;
119
- provider?: undefined;
120
120
  deleted: boolean;
121
121
  id: string;
122
122
  registered?: undefined;
123
123
  label?: undefined;
124
124
  message?: undefined;
125
125
  } | {
126
- deleted?: undefined;
127
126
  count?: undefined;
127
+ provider?: undefined;
128
+ deleted?: undefined;
128
129
  found?: undefined;
129
130
  providers?: undefined;
130
- provider?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -311,9 +311,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
+ message?: undefined;
314
315
  id?: undefined;
315
- provider?: undefined;
316
- deleted?: undefined;
317
316
  found?: undefined;
318
317
  providers: {
319
318
  id: string;
@@ -325,45 +324,46 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
325
324
  updatedAt: number;
326
325
  }[];
327
326
  count: number;
327
+ provider?: undefined;
328
+ deleted?: undefined;
328
329
  registered?: undefined;
329
330
  label?: undefined;
330
- message?: undefined;
331
331
  } | {
332
+ message?: undefined;
332
333
  id?: undefined;
333
- deleted?: undefined;
334
334
  count?: undefined;
335
335
  providers?: undefined;
336
336
  found: boolean;
337
337
  provider: import("../custom-registry.js").CustomProviderConfig;
338
+ deleted?: undefined;
338
339
  registered?: undefined;
339
340
  label?: undefined;
340
- message?: undefined;
341
341
  } | {
342
- provider?: undefined;
343
- deleted?: undefined;
342
+ message?: undefined;
344
343
  count?: undefined;
345
344
  providers?: undefined;
345
+ provider?: undefined;
346
346
  found: boolean;
347
347
  id: string;
348
+ deleted?: undefined;
348
349
  registered?: undefined;
349
350
  label?: undefined;
350
- message?: undefined;
351
351
  } | {
352
- provider?: undefined;
352
+ message?: undefined;
353
353
  found?: undefined;
354
354
  count?: undefined;
355
355
  providers?: undefined;
356
+ provider?: undefined;
356
357
  deleted: boolean;
357
358
  id: string;
358
359
  registered?: undefined;
359
360
  label?: undefined;
360
- message?: undefined;
361
361
  } | {
362
- provider?: undefined;
363
- deleted?: undefined;
364
362
  found?: undefined;
365
363
  count?: undefined;
366
364
  providers?: undefined;
365
+ provider?: undefined;
366
+ deleted?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -48,8 +48,8 @@ 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
54
  error?: undefined;
55
55
  ok: boolean;
@@ -2,6 +2,7 @@ import crypto from "node:crypto";
2
2
  import { defineEventHandler, getMethod, getQuery, getRequestIP, setResponseHeader, setResponseStatus, getCookie, setCookie, deleteCookie, getHeader, } from "h3";
3
3
  import { EMBED_START_PATH } from "../shared/embed-auth.js";
4
4
  import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
5
+ import { FIRST_RUN_ONBOARDING_COOKIE, FIRST_RUN_ONBOARDING_MAX_AGE, } from "../shared/first-run-onboarding.js";
5
6
  import { EMBED_TRANSPLANT_HEADER, isMcpEmbedCorsOrigin, MCP_EMBED_CORS_ALLOW_HEADERS, shouldAllowMcpEmbedCredentials, } from "../shared/mcp-embed-headers.js";
6
7
  import { isEmbedCapabilityScope, requestHasEmbedAuthMarker, resolveEmbedSessionFromRequest, } from "./embed-session.js";
7
8
  // In h3 v2, `event.req` IS the web Request — but in Nitro's dev server (srvx
@@ -1880,6 +1881,14 @@ function crossSiteCookieAttrs(event) {
1880
1881
  ? { sameSite: "none", secure: true, partitioned: true }
1881
1882
  : { sameSite: "lax", secure: false };
1882
1883
  }
1884
+ function setFirstRunOnboardingCookie(event) {
1885
+ setCookie(event, FIRST_RUN_ONBOARDING_COOKIE, "1", {
1886
+ ...crossSiteCookieAttrs(event),
1887
+ httpOnly: false,
1888
+ path: "/",
1889
+ maxAge: FIRST_RUN_ONBOARDING_MAX_AGE,
1890
+ });
1891
+ }
1883
1892
  export function setFrameworkSessionCookie(event, token) {
1884
1893
  clearFrameworkSessionCookies(event);
1885
1894
  setCookie(event, COOKIE_NAME, token, {
@@ -2293,11 +2302,14 @@ async function mountBetterAuthRoutes(app, options) {
2293
2302
  if (!googleAccountId) {
2294
2303
  throw new Error("Could not get Google account id");
2295
2304
  }
2296
- await ensureGoogleAuthIdentity({
2305
+ const isNewGoogleUser = await ensureGoogleAuthIdentity({
2297
2306
  email,
2298
2307
  accountId: googleAccountId,
2299
2308
  name: typeof user.name === "string" ? user.name : undefined,
2300
2309
  });
2310
+ if (isNewGoogleUser === true) {
2311
+ setFirstRunOnboardingCookie(event);
2312
+ }
2301
2313
  if (typeof user.picture === "string" && user.picture.trim()) {
2302
2314
  await putSetting(`avatar:${email}`, {
2303
2315
  image: user.picture,
@@ -2633,6 +2645,12 @@ async function mountBetterAuthRoutes(app, options) {
2633
2645
  // Best-effort — don't block the response
2634
2646
  }
2635
2647
  }
2648
+ if (reqPath.includes("/sign-up/email") &&
2649
+ isResponse &&
2650
+ response.status >= 200 &&
2651
+ response.status < 300) {
2652
+ setFirstRunOnboardingCookie(event);
2653
+ }
2636
2654
  return response;
2637
2655
  }));
2638
2656
  // Backward-compat: POST /_agent-native/auth/login
@@ -2721,6 +2739,7 @@ async function mountBetterAuthRoutes(app, options) {
2721
2739
  body: { email, password, name: email.split("@")[0], callbackURL },
2722
2740
  headers: event.headers,
2723
2741
  });
2742
+ setFirstRunOnboardingCookie(event);
2724
2743
  return { ok: true };
2725
2744
  }
2726
2745
  catch (e) {
@@ -2939,6 +2958,7 @@ function mountAuthFallbackRoutes(app) {
2939
2958
  body: { email, password, name: email.split("@")[0] },
2940
2959
  headers: event.headers,
2941
2960
  });
2961
+ setFirstRunOnboardingCookie(event);
2942
2962
  return { ok: true };
2943
2963
  }
2944
2964
  catch (e) {
@@ -193,9 +193,10 @@ export interface GoogleAuthIdentity {
193
193
  * Ensure a verified Google identity has a canonical Better Auth user/account
194
194
  * before the legacy email-keyed session is issued. This prevents a later
195
195
  * password signup from becoming the first canonical identity for that email.
196
+ * Returns whether this call created the canonical user.
196
197
  */
197
- export declare function ensureGoogleAuthIdentity(identity: GoogleAuthIdentity): Promise<void>;
198
- export declare function ensureGoogleAuthIdentityWithAdapter(adapter: BetterAuthInternalAdapter, identity: GoogleAuthIdentity): Promise<void>;
198
+ export declare function ensureGoogleAuthIdentity(identity: GoogleAuthIdentity): Promise<boolean>;
199
+ export declare function ensureGoogleAuthIdentityWithAdapter(adapter: BetterAuthInternalAdapter, identity: GoogleAuthIdentity): Promise<boolean>;
199
200
  /** Reset for testing */
200
201
  export declare function resetBetterAuth(): Promise<void>;
201
202
  /**
@@ -709,13 +709,14 @@ export async function getBetterAuthInternalAdapter(config) {
709
709
  * Ensure a verified Google identity has a canonical Better Auth user/account
710
710
  * before the legacy email-keyed session is issued. This prevents a later
711
711
  * password signup from becoming the first canonical identity for that email.
712
+ * Returns whether this call created the canonical user.
712
713
  */
713
714
  export async function ensureGoogleAuthIdentity(identity) {
714
715
  const adapter = await getBetterAuthInternalAdapter();
715
716
  if (!adapter) {
716
717
  throw new Error("Better Auth internal adapter is unavailable");
717
718
  }
718
- await ensureGoogleAuthIdentityWithAdapter(adapter, identity);
719
+ return ensureGoogleAuthIdentityWithAdapter(adapter, identity);
719
720
  }
720
721
  export async function ensureGoogleAuthIdentityWithAdapter(adapter, identity) {
721
722
  const email = identity.email.trim().toLowerCase();
@@ -731,13 +732,13 @@ export async function ensureGoogleAuthIdentityWithAdapter(adapter, identity) {
731
732
  if (!existing || linkedAccount.userId !== existing.user.id) {
732
733
  throw new Error("Google account is already linked to another user");
733
734
  }
734
- return;
735
+ return false;
735
736
  }
736
737
  if (!existing) {
737
738
  if (adapter.createOAuthUser) {
738
739
  try {
739
740
  await adapter.createOAuthUser({ email, name, emailVerified: true }, { providerId: "google", accountId });
740
- return;
741
+ return true;
741
742
  }
742
743
  catch (error) {
743
744
  // A concurrent first sign-in may have won the unique-email race. Only
@@ -754,7 +755,7 @@ export async function ensureGoogleAuthIdentityWithAdapter(adapter, identity) {
754
755
  if (linkedAccount.userId !== existing.user.id) {
755
756
  throw new Error("Google account is already linked to another user");
756
757
  }
757
- return;
758
+ return false;
758
759
  }
759
760
  }
760
761
  }
@@ -769,7 +770,7 @@ export async function ensureGoogleAuthIdentityWithAdapter(adapter, identity) {
769
770
  providerId: "google",
770
771
  accountId,
771
772
  });
772
- return;
773
+ return true;
773
774
  }
774
775
  }
775
776
  if (!existing) {
@@ -777,7 +778,7 @@ export async function ensureGoogleAuthIdentityWithAdapter(adapter, identity) {
777
778
  }
778
779
  const alreadyLinked = existing.accounts.some((account) => account.providerId === "google" && account.accountId === accountId);
779
780
  if (alreadyLinked)
780
- return;
781
+ return false;
781
782
  // A password signup reserves the email before verification. If that row is
782
783
  // credential-only, remove the unverified credential and promote the same
783
784
  // canonical user to the verified Google identity. Any other linked account
@@ -793,13 +794,14 @@ export async function ensureGoogleAuthIdentityWithAdapter(adapter, identity) {
793
794
  email,
794
795
  accountId,
795
796
  });
796
- return;
797
+ return false;
797
798
  }
798
799
  await adapter.linkAccount({
799
800
  userId: existing.user.id,
800
801
  providerId: "google",
801
802
  accountId,
802
803
  });
804
+ return false;
803
805
  }
804
806
  /** Reset for testing */
805
807
  export async function resetBetterAuth() {
@@ -25,14 +25,9 @@ function inlineMarkdown(value) {
25
25
  trailing = url.slice(-1) + trailing;
26
26
  url = url.slice(0, -1);
27
27
  }
28
- let label = "Open link";
29
- const normalizedUrl = url.replace(/&amp;/g, "&");
30
- if (URL.canParse(normalizedUrl)) {
31
- const host = new URL(normalizedUrl).hostname.replace(/^www\./, "");
32
- if (host)
33
- label = `Open ${host}`;
34
- }
35
- return `${prefix}<a href="${url}" style="color:${EMAIL_TEXT};text-decoration:underline;">${label}</a>${trailing}`;
28
+ // A bare URL stays its own link text: a recipient who cannot read the
29
+ // destination cannot tell a digest link from a phishing one.
30
+ return `${prefix}<a href="${url}" style="color:${EMAIL_TEXT};text-decoration:underline;">${url}</a>${trailing}`;
36
31
  });
37
32
  html = html
38
33
  .replace(/`([^`]+)`/g, `<code style="background:${EMAIL_CODE};padding:2px 5px;border-radius:4px;font-size:0.92em;">$1</code>`)
@@ -20,7 +20,7 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- text: string;
24
23
  error?: undefined;
24
+ text: string;
25
25
  }>>;
26
26
  //# sourceMappingURL=transcribe-voice.d.ts.map
@@ -0,0 +1,3 @@
1
+ export declare const FIRST_RUN_ONBOARDING_COOKIE = "agent-native-first-run";
2
+ export declare const FIRST_RUN_ONBOARDING_MAX_AGE: number;
3
+ //# sourceMappingURL=first-run-onboarding.d.ts.map
@@ -0,0 +1,3 @@
1
+ export const FIRST_RUN_ONBOARDING_COOKIE = "agent-native-first-run";
2
+ export const FIRST_RUN_ONBOARDING_MAX_AGE = 24 * 60 * 60;
3
+ //# sourceMappingURL=first-run-onboarding.js.map
@@ -2,4 +2,7 @@ export type { TriggerFrontmatter, TriggerDispatchContext } from "./types.js";
2
2
  export { initTriggerDispatcher, refreshEventSubscriptions, parseTriggerFrontmatter, buildTriggerContent, type TriggerDispatcherDeps, } from "./dispatcher.js";
3
3
  export { evaluateCondition, __clearConditionCache, } from "./condition-evaluator.js";
4
4
  export { createAutomationToolEntries } from "./actions.js";
5
+ export { defineAutomation, listAutomationDefinitions, updateAutomation, type AutomationActor, type AutomationDefinition, } from "../automations/service.js";
6
+ export { queueAutomationRunNow } from "../jobs/run-now.js";
7
+ export { listAutomationRuns } from "../jobs/run-history.js";
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,9 @@
1
1
  export { initTriggerDispatcher, refreshEventSubscriptions, parseTriggerFrontmatter, buildTriggerContent, } from "./dispatcher.js";
2
2
  export { evaluateCondition, __clearConditionCache, } from "./condition-evaluator.js";
3
3
  export { createAutomationToolEntries } from "./actions.js";
4
+ // Template-native automation surfaces use the same organization-scoped
5
+ // service and run history as the framework Agent page.
6
+ export { defineAutomation, listAutomationDefinitions, updateAutomation, } from "../automations/service.js";
7
+ export { queueAutomationRunNow } from "../jobs/run-now.js";
8
+ export { listAutomationRuns } from "../jobs/run-history.js";
4
9
  //# sourceMappingURL=index.js.map
@@ -15,6 +15,32 @@ This is the same trust primitive [A2A](/docs/a2a-protocol) and [External Agents]
15
15
 
16
16
  </Callout>
17
17
 
18
+ ## What the user sees {#user-flow}
19
+
20
+ Cross-App SSO should feel like moving between products in one workspace, not
21
+ creating a second account. For example:
22
+
23
+ 1. Sign in to [Dispatch](https://dispatch.agent-native.com), or to any app
24
+ that already uses the workspace identity.
25
+ 2. Open [Mail](https://mail.agent-native.com), [Calendar](https://calendar.agent-native.com),
26
+ or another enabled app in a new tab.
27
+ 3. The app sends the browser to Dispatch only if it does not already have a
28
+ Dispatch session. If the session exists, the redirect is effectively
29
+ invisible.
30
+ 4. Return to the app already signed in, with the same email, records, and org
31
+ context as before.
32
+
33
+ The important distinction is that this is **one sign-in, not one shared
34
+ cookie**. Each separately hosted app still creates and owns its own local
35
+ session. Dispatch proves the user's identity at the app's front door, and the
36
+ app then continues with its normal local auth and access checks.
37
+
38
+ For a product walkthrough, record this exact sequence: sign in once in
39
+ Dispatch, open two other hosted apps, show that neither asks for credentials,
40
+ then sign out of one app and show that the other app's session is independent.
41
+ That last step makes the security boundary visible: SSO removes repeated
42
+ logins without turning every app into one shared session store.
43
+
18
44
  ## What & why {#what-why}
19
45
 
20
46
  Per-app user stores mean there is no single place a browser cookie could live that every app trusts. The federation model instead names one app — **Dispatch** — as the identity authority. Any other app can delegate "who is this person?" to Dispatch, get back a short-lived signed assertion of the user's verified email, and then **link that to its own local account by email**.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.136.5",
3
+ "version": "0.137.0",
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": {