@agent-native/core 0.0.0-beta-20260820220930 → 0.0.0-beta-20260820222620

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 (39) hide show
  1. package/corpus/templates/analytics/.agents/skills/agent-native-docs/SKILL.md +11 -0
  2. package/corpus/templates/assets/.agents/skills/agent-native-docs/SKILL.md +11 -0
  3. package/corpus/templates/brain/.agents/skills/agent-native-docs/SKILL.md +11 -0
  4. package/corpus/templates/calendar/.agents/skills/agent-native-docs/SKILL.md +11 -0
  5. package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +11 -0
  6. package/corpus/templates/clips/.agents/skills/agent-native-docs/SKILL.md +11 -0
  7. package/corpus/templates/clips/app/routes/download.tsx +78 -131
  8. package/corpus/templates/content/.agents/skills/agent-native-docs/SKILL.md +11 -0
  9. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +11 -0
  10. package/corpus/templates/design/.agents/skills/agent-native-docs/SKILL.md +11 -0
  11. package/corpus/templates/dispatch/.agents/skills/agent-native-docs/SKILL.md +11 -0
  12. package/corpus/templates/factory/.agents/skills/agent-native-docs/SKILL.md +11 -0
  13. package/corpus/templates/forms/.agents/skills/agent-native-docs/SKILL.md +11 -0
  14. package/corpus/templates/macros/.agents/skills/agent-native-docs/SKILL.md +11 -0
  15. package/corpus/templates/mail/.agents/skills/agent-native-docs/SKILL.md +11 -0
  16. package/corpus/templates/plan/.agents/skills/agent-native-docs/SKILL.md +11 -0
  17. package/corpus/templates/slides/.agents/skills/agent-native-docs/SKILL.md +11 -0
  18. package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +11 -0
  19. package/dist/client/blocks/library/diagram.config.d.ts +4 -1
  20. package/dist/client/blocks/library/diagram.js +1 -1
  21. package/dist/client/chat/run-recovery.js +15 -7
  22. package/dist/notifications/routes.d.ts +3 -3
  23. package/dist/server/attribution.d.ts +29 -0
  24. package/dist/server/attribution.js +68 -0
  25. package/dist/server/auth.js +58 -15
  26. package/dist/server/better-auth-instance.js +16 -4
  27. package/dist/server/identity-sso.js +14 -2
  28. package/dist/server/request-context.d.ts +8 -0
  29. package/dist/server/request-context.js +4 -0
  30. package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +11 -0
  31. package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +11 -0
  32. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +11 -0
  33. package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +11 -0
  34. package/docs/content/deployment.mdx +3 -1
  35. package/package.json +2 -2
  36. package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +11 -0
  37. package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +11 -0
  38. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +11 -0
  39. package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +11 -0
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -4,20 +4,12 @@ import {
4
4
  IconBrandChrome,
5
5
  IconBrandApple,
6
6
  IconBrandWindows,
7
- IconChevronDown,
8
- IconDeviceDesktop,
9
7
  IconExternalLink,
8
+ IconTerminal2,
10
9
  } from "@tabler/icons-react";
11
10
  import { useEffect, useState } from "react";
12
11
 
13
12
  import { Button } from "@/components/ui/button";
14
- import {
15
- DropdownMenu,
16
- DropdownMenuContent,
17
- DropdownMenuRadioGroup,
18
- DropdownMenuRadioItem,
19
- DropdownMenuTrigger,
20
- } from "@/components/ui/dropdown-menu";
21
13
  import { Skeleton } from "@/components/ui/skeleton";
22
14
  import enMessages from "@/i18n/en-US";
23
15
  import {
@@ -45,7 +37,6 @@ type PlatformId = "mac" | "windows" | "linux";
45
37
  interface PlatformVariant {
46
38
  id: PlatformId;
47
39
  label: string;
48
- sublabel: string;
49
40
  assetKinds: readonly (
50
41
  | "mac-universal"
51
42
  | "mac-arm64"
@@ -64,23 +55,20 @@ const VARIANTS: PlatformVariant[] = [
64
55
  {
65
56
  id: "mac",
66
57
  label: "macOS",
67
- sublabel: "Universal (Apple Silicon + Intel)",
68
58
  assetKinds: ["mac-universal", "mac-arm64", "mac-x64"],
69
59
  icon: IconBrandApple,
70
60
  },
71
61
  {
72
62
  id: "windows",
73
63
  label: "Windows",
74
- sublabel: "64-bit MSI installer",
75
64
  assetKinds: ["windows-msi"],
76
65
  icon: IconBrandWindows,
77
66
  },
78
67
  {
79
68
  id: "linux",
80
69
  label: "Linux",
81
- sublabel: "AppImage, Debian, and RPM packages",
82
70
  assetKinds: ["linux-appimage", "linux-deb", "linux-rpm"],
83
- icon: IconDeviceDesktop,
71
+ icon: IconTerminal2,
84
72
  },
85
73
  ];
86
74
 
@@ -171,47 +159,6 @@ function primaryDownloadButton(
171
159
  );
172
160
  }
173
161
 
174
- function secondaryDownloadButton(
175
- variant: PlatformVariant,
176
- manifest: Manifest | null,
177
- manifestError: boolean,
178
- downloadLabel: string,
179
- retryLabel: string,
180
- onRetry: () => void,
181
- ) {
182
- const asset = pickAsset(manifest, variant);
183
- const Icon = variant.icon;
184
- const className =
185
- "h-auto gap-1.5 px-2 py-1 text-sm font-normal text-muted-foreground hover:bg-transparent hover:text-foreground";
186
- if (asset) {
187
- return (
188
- <Button asChild variant="ghost" className={className}>
189
- <a href={asset.url} download onClick={markDesktopAppDownloaded}>
190
- <Icon className="h-4 w-4" />
191
- {downloadLabel}
192
- </a>
193
- </Button>
194
- );
195
- }
196
- if (manifest === null && !manifestError) {
197
- return <Skeleton className="h-7 w-[208px] rounded-md" />;
198
- }
199
- if (manifestError) {
200
- return (
201
- <Button variant="ghost" className={className} onClick={onRetry}>
202
- <Icon className="h-4 w-4" />
203
- {retryLabel}
204
- </Button>
205
- );
206
- }
207
- return (
208
- <Button variant="ghost" className={className} disabled>
209
- <Icon className="h-4 w-4" />
210
- {downloadLabel}
211
- </Button>
212
- );
213
- }
214
-
215
162
  export default function DownloadPage() {
216
163
  const chromeExtensionEnabled = useClipsChromeExtensionEnabled();
217
164
  const t = useT();
@@ -267,7 +214,11 @@ export default function DownloadPage() {
267
214
  };
268
215
 
269
216
  const primary = VARIANTS.find((v) => v.id === detected) ?? VARIANTS[0];
270
- const secondary = VARIANTS.filter((v) => v.id !== primary.id);
217
+
218
+ const handlePlatformChange = (nextPlatform: PlatformId) => {
219
+ if (nextPlatform === detected) return;
220
+ setDetected(nextPlatform);
221
+ };
271
222
 
272
223
  return (
273
224
  <div className="min-h-screen bg-background text-foreground">
@@ -317,87 +268,83 @@ export default function DownloadPage() {
317
268
  {t("downloadRoute.heroDescription")}
318
269
  </p>
319
270
 
320
- <div className="mt-10 flex flex-col items-center gap-3">
321
- {primaryDownloadButton(
322
- primary,
323
- manifest,
324
- manifestError,
325
- t("downloadRoute.downloadFor", { platform: primary.label }),
326
- t("downloadRoute.retry"),
327
- retryManifest,
328
- )}
329
- {secondary.map((variant) => (
330
- <div key={variant.id}>
331
- {secondaryDownloadButton(
332
- variant,
333
- manifest,
334
- manifestError,
335
- t("downloadRoute.alsoFor", { platform: variant.label }),
336
- t("downloadRoute.retry"),
337
- retryManifest,
338
- )}
339
- </div>
340
- ))}
341
- <div className="text-xs text-muted-foreground">
342
- {manifest ? (
343
- <>
344
- {manifest.pub_date
345
- ? t("downloadRoute.versionReleased", {
346
- version: manifest.version,
347
- date: new Date(manifest.pub_date).toLocaleDateString(),
348
- })
349
- : t("downloadRoute.version", {
350
- version: manifest.version,
351
- })}
352
- </>
353
- ) : manifestError ? (
354
- <>{t("downloadRoute.manifestError")}</>
355
- ) : (
356
- <>{t("downloadRoute.loadingRelease")}</>
357
- )}
271
+ <div className="mt-10 flex flex-col items-center">
272
+ <div className="mb-2 flex justify-center gap-2">
273
+ {VARIANTS.map((variant) => {
274
+ const Icon = variant.icon;
275
+ const active = primary.id === variant.id;
276
+ return (
277
+ <button
278
+ key={variant.id}
279
+ type="button"
280
+ aria-label={variant.label}
281
+ aria-pressed={active}
282
+ onClick={() => handlePlatformChange(variant.id)}
283
+ className={`group flex items-center justify-center rounded-lg p-4 transition-opacity focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${
284
+ active
285
+ ? "text-foreground"
286
+ : "text-muted-foreground opacity-40 hover:opacity-65"
287
+ }`}
288
+ >
289
+ <Icon className="size-6" aria-hidden="true" />
290
+ </button>
291
+ );
292
+ })}
358
293
  </div>
359
- <div className="mt-2 flex items-center justify-center text-xs text-muted-foreground">
360
- <DropdownMenu>
361
- <DropdownMenuTrigger asChild>
362
- <Button
294
+
295
+ <div className="mx-auto mt-8 max-w-2xl text-center">
296
+ {primaryDownloadButton(
297
+ primary,
298
+ manifest,
299
+ manifestError,
300
+ t("downloadRoute.downloadFor", { platform: primary.label }),
301
+ t("downloadRoute.retry"),
302
+ retryManifest,
303
+ )}
304
+
305
+ <div className="mt-5 flex justify-center">
306
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
307
+ <span>{t("downloadRoute.stable")}</span>
308
+ <button
363
309
  type="button"
364
- variant="ghost"
365
- size="sm"
366
- className="h-7 gap-1.5 px-2 font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground"
367
- data-release-channel={channel}
310
+ role="switch"
311
+ aria-checked={channel === "nightly"}
312
+ aria-label={t(
313
+ channel === "nightly"
314
+ ? "downloadRoute.switchToStable"
315
+ : "downloadRoute.switchToNightly",
316
+ )}
317
+ onClick={() =>
318
+ handleChannelChange(
319
+ channel === "nightly" ? "production" : "nightly",
320
+ )
321
+ }
322
+ className={`relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${
323
+ channel === "nightly"
324
+ ? "bg-foreground"
325
+ : "bg-muted-foreground/20"
326
+ }`}
368
327
  >
369
328
  <span
370
- className={
329
+ aria-hidden="true"
330
+ className={`block size-3.5 rounded-full bg-primary-foreground shadow-sm transition-transform ${
371
331
  channel === "nightly"
372
- ? "font-medium text-foreground"
373
- : undefined
374
- }
375
- >
376
- {channel === "nightly"
377
- ? t("downloadRoute.nightly")
378
- : t("downloadRoute.stable")}
379
- </span>
380
- <IconChevronDown className="size-3.5" aria-hidden="true" />
381
- </Button>
382
- </DropdownMenuTrigger>
383
- <DropdownMenuContent align="center" className="w-44">
384
- <DropdownMenuRadioGroup
385
- value={channel}
386
- onValueChange={(value) => {
387
- if (value === "production" || value === "nightly") {
388
- handleChannelChange(value);
389
- }
390
- }}
332
+ ? "translate-x-[18px]"
333
+ : "translate-x-[2px]"
334
+ }`}
335
+ />
336
+ </button>
337
+ <span
338
+ className={
339
+ channel === "nightly"
340
+ ? "font-medium text-foreground"
341
+ : "text-muted-foreground"
342
+ }
391
343
  >
392
- <DropdownMenuRadioItem value="production">
393
- {t("downloadRoute.stable")}
394
- </DropdownMenuRadioItem>
395
- <DropdownMenuRadioItem value="nightly">
396
- {t("downloadRoute.nightly")}
397
- </DropdownMenuRadioItem>
398
- </DropdownMenuRadioGroup>
399
- </DropdownMenuContent>
400
- </DropdownMenu>
344
+ {t("downloadRoute.nightly")}
345
+ </span>
346
+ </div>
347
+ </div>
401
348
  </div>
402
349
  </div>
403
350
 
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -37,7 +37,10 @@ export interface DiagramData {
37
37
  */
38
38
  html?: string;
39
39
  css?: string;
40
- /** `design` forces clean HTML/CSS rendering without the sketch overlay. */
40
+ /**
41
+ * `design` forces clean HTML/CSS rendering without the sketch overlay. Use it
42
+ * for UI-like blocks such as cards, logo walls, tables, and controls.
43
+ */
41
44
  renderMode?: "wireframe" | "design";
42
45
  caption?: string;
43
46
  /** Outer surface frame. `auto` lets the host choose the right default. */
@@ -393,7 +393,7 @@ export const diagramBlock = defineBlock({
393
393
  // edit from a corner button + panel rather than inline.
394
394
  editSurface: "panel",
395
395
  label: "Diagram",
396
- description: "A flexible inline architecture/code diagram. Prefer html/css with SVG or semantic HTML for polished two-dimensional layouts; use renderMode design for clean presentation or .diagram-* primitives and --wf-* tokens for theme/sketch compatibility. Legacy nodes/edges are only for simple previews.",
396
+ description: "A flexible inline architecture/code diagram. Prefer html/css with SVG or semantic HTML for polished two-dimensional layouts; use renderMode design for UI-like blocks such as cards, logo walls, tables, and controls, or .diagram-* primitives and --wf-* tokens for actual diagrams that need theme/sketch compatibility. Legacy nodes/edges are only for simple previews.",
397
397
  // Seed the legacy fallback shape so a fresh block validates while agents can
398
398
  // replace it with html/css when layout quality matters.
399
399
  empty: () => ({ nodes: [{ id: "n1", label: "Module" }], edges: [] }),
@@ -298,17 +298,25 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
298
298
  return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: runErrorHeadline(info, {
299
299
  recoverable: t("agentChat.error.stopped"),
300
300
  terminal: t("agentChat.error.failed"),
301
- }) }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: localizeKnownChatErrorText(info.message, t) }), isProviderAuthError && (_jsx("div", { className: "mt-3 rounded-md border border-border/70 bg-background/60 p-2.5", children: _jsx(BuilderSetupContent, { layout: "sidebar", onConnected: handleProviderConnected }) })), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: t("agentChat.recovery.credentialRejected") })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: t("agentChat.recovery.newChatHint") })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), t("agentChat.common.details")] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": t("agentChat.common.dismiss"), className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : null, builderReconnect.connecting
301
+ }) }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: localizeKnownChatErrorText(info.message, t) }), isProviderAuthError && (_jsx("div", { className: "mt-3 rounded-md border border-border/70 bg-background/60 p-2.5", children: _jsx(BuilderSetupContent, { layout: "sidebar", onConnected: handleProviderConnected }) })), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: t("agentChat.recovery.credentialRejected") })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: t("agentChat.recovery.newChatHint") })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), t("agentChat.common.details")] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": t("agentChat.common.dismiss"), className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex min-w-0 items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : null, builderReconnect.connecting
302
302
  ? t("agentChat.recovery.connectingBuilder")
303
- : t("agentChat.recovery.reconnectBuilder")] })), canRecover && (_jsx(_Fragment, { children: _jsxs("button", { type: "button", onClick: onContinue, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), t("agentChat.common.continue")] }) })), canRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { size: 13 }), isQueryError
304
- ? t("agentChat.recovery.diagnoseRetry")
305
- : t("agentChat.common.retry")] })), canRecover && isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: startNewChat, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconPlus, { size: 13 }), t("agentChat.tabs.newChat")] })), canRecover && onFork && !isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: handleFork, disabled: forking, title: t("agentChat.recovery.forkDescription"), "aria-label": t("agentChat.recovery.forkDescription"), className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent disabled:cursor-wait disabled:opacity-70", children: [forking ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconGitFork, { size: 13 })), forking
306
- ? t("agentChat.recovery.forking")
307
- : t("agentChat.message.forkChat")] })), _jsxs("button", { type: "button", onClick: copyDetails, className: "ms-auto inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground hover:bg-background/80 hover:text-foreground", children: [copyState === "copied" ? (_jsx(IconCheck, { size: 13 })) : copyState === "failed" ? (_jsx(IconX, { size: 13 })) : (_jsx(IconCopy, { size: 13 })), _jsx("span", { "aria-live": "polite", children: copyState === "copied"
303
+ : t("agentChat.recovery.reconnectBuilder")] })), canRecover && (_jsxs("button", { type: "button", onClick: onContinue, className: "inline-flex min-w-0 flex-1 items-center justify-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), _jsx("span", { className: "truncate", children: t("agentChat.common.continue") })] })), _jsxs("div", { className: "flex shrink-0 items-center gap-0.5 rounded-md border border-border/70 bg-background/60 p-0.5", children: [canRetry && (_jsx("button", { type: "button", onClick: onRetry, title: isQueryError
304
+ ? t("agentChat.recovery.diagnoseRetry")
305
+ : t("agentChat.common.retry"), "aria-label": isQueryError
306
+ ? t("agentChat.recovery.diagnoseRetry")
307
+ : t("agentChat.common.retry"), className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconRefresh, { size: 14 }) })), canRecover && isConnectionRecoveryError && (_jsx("button", { type: "button", onClick: startNewChat, title: t("agentChat.tabs.newChat"), "aria-label": t("agentChat.tabs.newChat"), className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconPlus, { size: 14 }) })), canRecover && onFork && !isConnectionRecoveryError && (_jsx("button", { type: "button", onClick: handleFork, disabled: forking, title: t("agentChat.recovery.forkDescription"), "aria-label": t("agentChat.recovery.forkDescription"), className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground disabled:cursor-wait disabled:opacity-70", children: forking ? (_jsx(IconLoader2, { size: 14, className: "animate-spin" })) : (_jsx(IconGitFork, { size: 14 })) })), _jsxs("button", { type: "button", onClick: copyDetails, title: copyState === "copied"
308
308
  ? t("agentChat.common.copied")
309
309
  : copyState === "failed"
310
310
  ? t("agentChat.recovery.copyFailed")
311
- : copyLabel })] })] }), shouldShowBuilderReconnect && builderReconnect.error && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: builderReconnect.error })), forkError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: forkError }))] }));
311
+ : copyLabel, "aria-label": copyState === "copied"
312
+ ? t("agentChat.common.copied")
313
+ : copyState === "failed"
314
+ ? t("agentChat.recovery.copyFailed")
315
+ : copyLabel, className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: [copyState === "copied" ? (_jsx(IconCheck, { size: 14 })) : copyState === "failed" ? (_jsx(IconX, { size: 14 })) : (_jsx(IconCopy, { size: 14 })), _jsx("span", { className: "sr-only", "aria-live": "polite", children: copyState === "copied"
316
+ ? t("agentChat.common.copied")
317
+ : copyState === "failed"
318
+ ? t("agentChat.recovery.copyFailed")
319
+ : copyLabel })] })] })] }), shouldShowBuilderReconnect && builderReconnect.error && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: builderReconnect.error })), forkError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: forkError }))] }));
312
320
  }
313
321
  // ─── LoopLimitContinueCard ────────────────────────────────────────────────────
314
322
  export function LoopLimitContinueCard({ info, onContinue, }) {
@@ -13,13 +13,13 @@
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
14
  count: number;
15
15
  updated?: undefined;
16
- ok?: undefined;
17
16
  error?: undefined;
17
+ ok?: undefined;
18
18
  } | {
19
19
  count?: undefined;
20
20
  updated: number;
21
- ok?: undefined;
22
21
  error?: undefined;
22
+ ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
25
25
  updated?: undefined;
@@ -28,6 +28,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
28
28
  } | {
29
29
  count?: undefined;
30
30
  updated?: undefined;
31
- ok: boolean;
32
31
  error?: undefined;
32
+ ok: boolean;
33
33
  }>>;
@@ -35,6 +35,18 @@ export interface FirstTouchAttribution {
35
35
  /** ISO timestamp of when the visitor first landed. */
36
36
  landed_at?: string;
37
37
  }
38
+ /**
39
+ * Request-scoped browser attribution captured at the signup boundary.
40
+ * Better Auth may create the user in a later async callback where the
41
+ * original request headers are no longer available, so the values must be
42
+ * carried explicitly through that boundary.
43
+ */
44
+ export interface SignupAttributionContext {
45
+ attribution: Record<string, string>;
46
+ anonymousId?: string;
47
+ }
48
+ /** Internal Better Auth handoff header; attribution is analytics-only. */
49
+ export declare const SIGNUP_ATTRIBUTION_HEADER_NAME = "x-agent-native-signup-attribution";
38
50
  /** Cookie name written by the client (non-HttpOnly; non-sensitive). */
39
51
  export declare const FIRST_TOUCH_COOKIE_NAME = "an_ft";
40
52
  /**
@@ -87,3 +99,20 @@ export declare function deriveSignupAttribution(ft: FirstTouchAttribution | null
87
99
  * error.
88
100
  */
89
101
  export declare function signupAttributionFromCookieHeader(cookieHeader: string | null | undefined): Record<string, string>;
102
+ /**
103
+ * Capture all browser attribution needed by the server-side signup event.
104
+ * Keep this as one boundary helper so every signup entry point carries the
105
+ * same values into Better Auth's user-create hook.
106
+ */
107
+ export declare function signupAttributionContextFromCookieHeader(cookieHeader: string | null | undefined): SignupAttributionContext;
108
+ /** Serialize the bounded attribution handoff for a Better Auth request. */
109
+ export declare function encodeSignupAttributionContext(context: SignupAttributionContext): string;
110
+ /**
111
+ * Decode the internal handoff header. Invalid input is absent, not a direct
112
+ * attribution, so the caller can safely fall back to the request cookie.
113
+ */
114
+ export declare function decodeSignupAttributionContext(value: string | null | undefined): SignupAttributionContext | undefined;
115
+ /** Add the explicit handoff to a Better Auth request/API header set. */
116
+ export declare function addSignupAttributionHeader(headers: HeadersInit | undefined, context: SignupAttributionContext): Headers;
117
+ /** Read the explicit handoff from Better Auth's request context headers. */
118
+ export declare function signupAttributionContextFromHeaders(headers: Headers | null | undefined): SignupAttributionContext | undefined;
@@ -1,4 +1,7 @@
1
1
  import { ANALYTICS_ANONYMOUS_ID_COOKIE_NAME, normalizeAnalyticsAnonymousId, } from "../shared/analytics-anonymous-id.js";
2
+ /** Internal Better Auth handoff header; attribution is analytics-only. */
3
+ export const SIGNUP_ATTRIBUTION_HEADER_NAME = "x-agent-native-signup-attribution";
4
+ const SIGNUP_ATTRIBUTION_HEADER_MAX_LENGTH = 4096;
2
5
  /** Cookie name written by the client (non-HttpOnly; non-sensitive). */
3
6
  export const FIRST_TOUCH_COOKIE_NAME = "an_ft";
4
7
  const STRING_FIELDS = [
@@ -178,3 +181,68 @@ export function signupAttributionFromCookieHeader(cookieHeader) {
178
181
  return { referral_source: "direct" };
179
182
  }
180
183
  }
184
+ /**
185
+ * Capture all browser attribution needed by the server-side signup event.
186
+ * Keep this as one boundary helper so every signup entry point carries the
187
+ * same values into Better Auth's user-create hook.
188
+ */
189
+ export function signupAttributionContextFromCookieHeader(cookieHeader) {
190
+ const anonymousId = readAnalyticsAnonymousId(cookieHeader);
191
+ return {
192
+ attribution: signupAttributionFromCookieHeader(cookieHeader),
193
+ ...(anonymousId ? { anonymousId } : {}),
194
+ };
195
+ }
196
+ /** Serialize the bounded attribution handoff for a Better Auth request. */
197
+ export function encodeSignupAttributionContext(context) {
198
+ return encodeURIComponent(JSON.stringify(context));
199
+ }
200
+ /**
201
+ * Decode the internal handoff header. Invalid input is absent, not a direct
202
+ * attribution, so the caller can safely fall back to the request cookie.
203
+ */
204
+ export function decodeSignupAttributionContext(value) {
205
+ if (!value || value.length > SIGNUP_ATTRIBUTION_HEADER_MAX_LENGTH) {
206
+ return undefined;
207
+ }
208
+ try {
209
+ const parsed = JSON.parse(decodeURIComponent(value));
210
+ const rawAttribution = parsed?.attribution;
211
+ if (!rawAttribution ||
212
+ typeof rawAttribution !== "object" ||
213
+ Array.isArray(rawAttribution)) {
214
+ return undefined;
215
+ }
216
+ const attribution = {};
217
+ for (const [key, rawValue] of Object.entries(rawAttribution)) {
218
+ if (/^[A-Za-z0-9_]+$/.test(key) &&
219
+ key.length <= 64 &&
220
+ typeof rawValue === "string" &&
221
+ rawValue.length > 0) {
222
+ attribution[key] = rawValue.slice(0, 120);
223
+ }
224
+ }
225
+ if (Object.keys(attribution).length === 0)
226
+ return undefined;
227
+ const anonymousId = normalizeAnalyticsAnonymousId(parsed?.anonymousId);
228
+ return {
229
+ attribution,
230
+ ...(anonymousId ? { anonymousId } : {}),
231
+ };
232
+ }
233
+ catch (error) {
234
+ // `undefined` distinguishes an unreadable handoff from direct attribution.
235
+ void error;
236
+ return undefined;
237
+ }
238
+ }
239
+ /** Add the explicit handoff to a Better Auth request/API header set. */
240
+ export function addSignupAttributionHeader(headers, context) {
241
+ const result = new Headers(headers);
242
+ result.set(SIGNUP_ATTRIBUTION_HEADER_NAME, encodeSignupAttributionContext(context));
243
+ return result;
244
+ }
245
+ /** Read the explicit handoff from Better Auth's request context headers. */
246
+ export function signupAttributionContextFromHeaders(headers) {
247
+ return decodeSignupAttributionContext(headers?.get(SIGNUP_ATTRIBUTION_HEADER_NAME));
248
+ }
@@ -58,7 +58,7 @@ import { isValidWorkspaceAppIdFormat } from "../shared/workspace-app-id.js";
58
58
  import { injectAnalyticsIntoHtml } from "./analytics.js";
59
59
  import { getConfiguredAppBasePath } from "./app-base-path.js";
60
60
  import { getAppProductionUrl } from "./app-url.js";
61
- import { readAnalyticsAnonymousId, readFirstTouchAttribution, signupAttributionFromCookieHeader, } from "./attribution.js";
61
+ import { addSignupAttributionHeader, readAnalyticsAnonymousId, readFirstTouchAttribution, signupAttributionContextFromCookieHeader, signupAttributionFromCookieHeader, } from "./attribution.js";
62
62
  import { getAuthLoginMode } from "./auth-login-mode.js";
63
63
  import { injectBetaOptOutPersistence } from "./beta-opt-out-html.js";
64
64
  import { createBetterAuthSessionForEmail, ensureGoogleAuthIdentity, getAuthSecret, getBetterAuth, getBetterAuthSync, isDeployPreview, } from "./better-auth-instance.js";
@@ -73,6 +73,7 @@ import { ensureCanonicalUserForLegacySession } from "./legacy-auth-migration.js"
73
73
  import { encodeMagicLinkSignupAttribution, MAGIC_LINK_ATTRIBUTION_PARAM, } from "./magic-link-attribution.js";
74
74
  import { safeOAuthReturnUrl } from "./oauth-return-url.js";
75
75
  import { getOnboardingHtml, getResetPasswordHtml, } from "./onboarding-html.js";
76
+ import { getRequestContext, hasContinuationLocalRequestContext, runWithRequestContext, } from "./request-context.js";
76
77
  import { captureAuthError } from "./sentry.js";
77
78
  import { forgetCachedSessionEmail, getCachedSessionEmail, invalidateSessionEmailCache, setCachedSessionEmail, } from "./session-email-cache.js";
78
79
  import { isWorkspaceOAuthCallbackRelayEnabled } from "./workspace-oauth.js";
@@ -83,6 +84,23 @@ import { isWorkspaceOAuthCallbackRelayEnabled } from "./workspace-oauth.js";
83
84
  export function getSessionMaxAge() {
84
85
  return sessionMaxAge;
85
86
  }
87
+ function withSignupAttributionContext(cookieHeader, fn) {
88
+ const signupAttribution = signupAttributionContextFromCookieHeader(cookieHeader);
89
+ if (!hasContinuationLocalRequestContext())
90
+ return fn();
91
+ return runWithRequestContext({
92
+ ...(getRequestContext() ?? {}),
93
+ signupAttribution,
94
+ }, fn);
95
+ }
96
+ function requestWithSignupAttribution(request, signupAttribution) {
97
+ return new Request(request, {
98
+ headers: addSignupAttributionHeader(request.headers, signupAttribution),
99
+ });
100
+ }
101
+ function headersWithSignupAttribution(headers, cookieHeader) {
102
+ return addSignupAttributionHeader(headers, signupAttributionContextFromCookieHeader(cookieHeader));
103
+ }
86
104
  // ---------------------------------------------------------------------------
87
105
  // Constants
88
106
  // ---------------------------------------------------------------------------
@@ -416,6 +434,9 @@ async function readDesktopSsoSafely(event) {
416
434
  return null;
417
435
  return await readDesktopSso();
418
436
  }
437
+ function isDesktopSessionCookieOnlyCheck(event) {
438
+ return getHeader(event, "x-agent-native-session-check") === "cookie-only";
439
+ }
419
440
  /**
420
441
  * Extract the framework session token from a Better Auth response's
421
442
  * Set-Cookie headers, if any. Used by the password-reset path to skip
@@ -2688,6 +2709,7 @@ export async function getSession(event) {
2688
2709
  })());
2689
2710
  }
2690
2711
  async function resolveSessionUncached(event) {
2712
+ const cookieOnlyDesktopCheck = isDesktopSessionCookieOnlyCheck(event);
2691
2713
  // 1. MCP App embed session. This is a short-lived browser session minted
2692
2714
  // from a one-time ticket that was scoped to the authenticated MCP caller.
2693
2715
  // It lets an inline MCP App iframe load the real app without reusing the
@@ -2724,9 +2746,11 @@ async function resolveSessionUncached(event) {
2724
2746
  // templates too. Gated on `readDesktopSsoSafely` so a non-loopback
2725
2747
  // request that spoofs `User-Agent: ... Electron/...` cannot read the
2726
2748
  // home-dir broker file (and so production builds never consult it).
2727
- const sso = await readDesktopSsoSafely(event);
2728
- if (sso?.email)
2729
- return { email: sso.email, token: sso.token };
2749
+ if (!cookieOnlyDesktopCheck) {
2750
+ const sso = await readDesktopSsoSafely(event);
2751
+ if (sso?.email)
2752
+ return { email: sso.email, token: sso.token };
2753
+ }
2730
2754
  // Fall through to mobile _session check
2731
2755
  }
2732
2756
  else {
@@ -2765,9 +2789,11 @@ async function resolveSessionUncached(event) {
2765
2789
  // a loopback (127.0.0.1 / ::1) source IP, and a non-production NODE_ENV
2766
2790
  // — anything else is rejected so a hostile network request cannot
2767
2791
  // impersonate whichever email last signed into the desktop app.
2768
- const sso = await readDesktopSsoSafely(event);
2769
- if (sso?.email) {
2770
- return { email: sso.email, token: sso.token };
2792
+ if (!cookieOnlyDesktopCheck) {
2793
+ const sso = await readDesktopSsoSafely(event);
2794
+ if (sso?.email) {
2795
+ return { email: sso.email, token: sso.token };
2796
+ }
2771
2797
  }
2772
2798
  }
2773
2799
  // 8. Mobile WebView bridge — _session query param
@@ -3699,11 +3725,18 @@ async function mountBetterAuthRoutes(app, options) {
3699
3725
  getMethod(event) === "POST";
3700
3726
  const isMagicLinkRequest = reqPath.includes("/sign-in/magic-link") && getMethod(event) === "POST";
3701
3727
  const isMagicLinkVerification = reqPath.includes("/magic-link/verify") && getMethod(event) === "GET";
3728
+ const isEmailSignup = reqPath.includes("/sign-up/email") && getMethod(event) === "POST";
3702
3729
  const isSignOut = reqPath.includes("sign-out") && getMethod(event) === "POST";
3703
3730
  if (isSignOut)
3704
3731
  optOutOfAuthDisabledSession(event);
3705
3732
  const authRequest = toWebRequest(event);
3706
3733
  let requestForAuth = authRequest;
3734
+ const signupCookieHeader = isEmailSignup
3735
+ ? authRequest.headers.get("cookie")
3736
+ : undefined;
3737
+ const signupAttribution = isEmailSignup
3738
+ ? signupAttributionContextFromCookieHeader(signupCookieHeader)
3739
+ : undefined;
3707
3740
  let magicLinkPreConsume;
3708
3741
  if (isMagicLinkVerification) {
3709
3742
  const verificationURL = new URL(authRequest.url);
@@ -3820,9 +3853,14 @@ async function mountBetterAuthRoutes(app, options) {
3820
3853
  // validation error.
3821
3854
  }
3822
3855
  }
3856
+ if (signupAttribution) {
3857
+ requestForAuth = requestWithSignupAttribution(requestForAuth, signupAttribution);
3858
+ }
3823
3859
  let response;
3824
3860
  try {
3825
- response = await auth.handler(requestForAuth);
3861
+ response = await (isEmailSignup
3862
+ ? withSignupAttributionContext(signupCookieHeader, () => auth.handler(requestForAuth))
3863
+ : auth.handler(requestForAuth));
3826
3864
  }
3827
3865
  catch (error) {
3828
3866
  if (isMagicLinkVerification) {
@@ -4127,10 +4165,15 @@ async function mountBetterAuthRoutes(app, options) {
4127
4165
  return { error: GOOGLE_AUTH_REQUIRED_MESSAGE };
4128
4166
  }
4129
4167
  try {
4130
- await auth.api.signUpEmail({
4131
- body: { email, password, name: email.split("@")[0], callbackURL },
4132
- headers: event.headers,
4133
- });
4168
+ await withSignupAttributionContext(getHeader(event, "cookie") ?? null, () => auth.api.signUpEmail({
4169
+ body: {
4170
+ email,
4171
+ password,
4172
+ name: email.split("@")[0],
4173
+ callbackURL,
4174
+ },
4175
+ headers: headersWithSignupAttribution(event.headers, getHeader(event, "cookie") ?? null),
4176
+ }));
4134
4177
  setFirstRunOnboardingCookie(event);
4135
4178
  return { ok: true };
4136
4179
  }
@@ -4384,10 +4427,10 @@ function mountAuthFallbackRoutes(app) {
4384
4427
  }
4385
4428
  try {
4386
4429
  const auth = await getBetterAuth();
4387
- await auth.api.signUpEmail({
4430
+ await withSignupAttributionContext(getHeader(event, "cookie") ?? null, () => auth.api.signUpEmail({
4388
4431
  body: { email, password, name: email.split("@")[0] },
4389
- headers: event.headers,
4390
- });
4432
+ headers: headersWithSignupAttribution(event.headers, getHeader(event, "cookie") ?? null),
4433
+ }));
4391
4434
  setFirstRunOnboardingCookie(event);
4392
4435
  return { ok: true };
4393
4436
  }
@@ -26,13 +26,14 @@ import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, } from "../shared/password-po
26
26
  import { formatRuntimeConfigReport, getRuntimeConfigReport, } from "../shared/runtime-config.js";
27
27
  import { flushTracking, identify, track } from "../tracking/index.js";
28
28
  import { getAppProductionUrl } from "./app-url.js";
29
- import { readAnalyticsAnonymousId, signupAttributionFromCookieHeader, } from "./attribution.js";
29
+ import { signupAttributionContextFromCookieHeader, signupAttributionContextFromHeaders, } from "./attribution.js";
30
30
  import { resolveAuthCookieNamespace } from "./cookie-namespace.js";
31
31
  import { getWorkspaceA2ADerivedSecret } from "./derived-secret.js";
32
32
  import { renderMagicLinkEmail, renderResetPasswordEmail, renderVerifySignupEmail, } from "./email-templates.js";
33
33
  import { getEmailReadiness, sendEmail } from "./email.js";
34
34
  import { resolveGoogleSignInCredentials } from "./google-oauth-credentials.js";
35
35
  import { readMagicLinkSignupAttribution } from "./magic-link-attribution.js";
36
+ import { getRequestContext, hasContinuationLocalRequestContext, } from "./request-context.js";
36
37
  export { getAuthLoginMode, resolveAuthLoginMode, resolveAuthLoginModeFromReadiness, } from "./auth-login-mode.js";
37
38
  async function flushSignupTracking() {
38
39
  try {
@@ -1045,14 +1046,25 @@ async function createBetterAuthInstance(config) {
1045
1046
  const cookieHeader = context?.headers?.get("cookie") ??
1046
1047
  context?.request?.headers?.get("cookie") ??
1047
1048
  null;
1049
+ const scopedSignupAttribution = hasContinuationLocalRequestContext()
1050
+ ? getRequestContext()?.signupAttribution
1051
+ : undefined;
1052
+ const requestSignupAttribution = signupAttributionContextFromCookieHeader(cookieHeader);
1053
+ const headerSignupAttribution = signupAttributionContextFromHeaders(context?.headers) ??
1054
+ signupAttributionContextFromHeaders(context?.request?.headers);
1048
1055
  const magicLinkAttribution = context?.request?.url?.includes("newUserCallbackURL")
1049
1056
  ? readMagicLinkSignupAttribution(context.request.url, getAuthSecret())
1050
1057
  : undefined;
1051
- attribution = signupAttributionFromCookieHeader(cookieHeader);
1052
- attribution = magicLinkAttribution?.attribution ?? attribution;
1058
+ attribution =
1059
+ magicLinkAttribution?.attribution ??
1060
+ scopedSignupAttribution?.attribution ??
1061
+ headerSignupAttribution?.attribution ??
1062
+ requestSignupAttribution.attribution;
1053
1063
  anonymousId =
1054
1064
  magicLinkAttribution?.anonymousId ??
1055
- readAnalyticsAnonymousId(cookieHeader);
1065
+ scopedSignupAttribution?.anonymousId ??
1066
+ headerSignupAttribution?.anonymousId ??
1067
+ requestSignupAttribution.anonymousId;
1056
1068
  }
1057
1069
  catch (err) {
1058
1070
  console.error("[auth] failed to derive signup attribution", err);
@@ -19,10 +19,12 @@ import { getAppConfig } from "../app-config/index.js";
19
19
  import { GOOGLE_AUTH_REQUIRED_MESSAGE, isGoogleSignInRequiredForEmail, } from "../org/auth-policy.js";
20
20
  import { SIGN_IN_ENTRY_PATH } from "../shared/sign-in-journey.js";
21
21
  import { getAppName } from "./app-name.js";
22
+ import { addSignupAttributionHeader, signupAttributionContextFromCookieHeader, } from "./attribution.js";
22
23
  import { getSession, isExpectedAuthFailure, safeReturnPath } from "./auth.js";
23
24
  import { getBetterAuth, getBetterAuthInternalAdapter, } from "./better-auth-instance.js";
24
25
  import { createOAuthSession, getOrigin } from "./google-oauth.js";
25
26
  import { consumeSsoState, createSsoState, CANONICAL_IDENTITY_SSO_HUB_URL, getIdentityHubUrl, identitySsoLoginButtonHtml, isCanonicalIdentitySsoClientRequest, isDesktopSsoUserAgent, isIdentitySsoExplicitlyEnabled, isIdentitySsoEnabled, isJtiReplayed, SSO_STATE_TTL_MS, } from "./identity-sso-store.js";
27
+ import { getRequestContext, hasContinuationLocalRequestContext, runWithRequestContext, } from "./request-context.js";
26
28
  export { getIdentityHubUrl, identitySsoLoginButtonHtml, isIdentitySsoEnabled };
27
29
  export const IDENTITY_SSO_PROVIDER_ID = "agent-native";
28
30
  export const IDENTITY_SSO_SCOPE = "identity";
@@ -269,7 +271,7 @@ async function exchangeIdentityCode(hub, binding, input) {
269
271
  return null;
270
272
  }
271
273
  }
272
- async function jitLinkIdentity(identity) {
274
+ async function jitLinkIdentity(identity, signupHeaders) {
273
275
  const adapter = await getBetterAuthInternalAdapter();
274
276
  let existing = adapter
275
277
  ? await adapter
@@ -288,6 +290,7 @@ async function jitLinkIdentity(identity) {
288
290
  password: createUnusableSsoCredential(),
289
291
  name: identity.name || identity.email.split("@")[0] || "User",
290
292
  },
293
+ ...(signupHeaders ? { headers: signupHeaders } : {}),
291
294
  });
292
295
  }
293
296
  catch (error) {
@@ -463,7 +466,16 @@ export async function handleIdentitySso(event, subpath) {
463
466
  return errorPage(GOOGLE_AUTH_REQUIRED_MESSAGE, loginPath);
464
467
  }
465
468
  try {
466
- await jitLinkIdentity(identity);
469
+ const signupCookieHeader = getHeader(event, "cookie") ?? null;
470
+ const signupAttribution = signupAttributionContextFromCookieHeader(signupCookieHeader);
471
+ const signupHeaders = addSignupAttributionHeader(event.headers, signupAttribution);
472
+ const linkIdentity = () => jitLinkIdentity(identity, signupHeaders);
473
+ await (hasContinuationLocalRequestContext()
474
+ ? runWithRequestContext({
475
+ ...(getRequestContext() ?? {}),
476
+ signupAttribution,
477
+ }, linkIdentity)
478
+ : linkIdentity());
467
479
  }
468
480
  catch {
469
481
  return errorPage("Could not finish linking your account. Please try again.", loginPath);
@@ -15,6 +15,7 @@
15
15
  * process.env so existing `AGENT_USER_EMAIL=x pnpm action foo` invocations
16
16
  * continue to work.
17
17
  */
18
+ import type { SignupAttributionContext } from "./attribution.js";
18
19
  /**
19
20
  * Per-request agent-run state. Lives on `RequestContext.run` so the
20
21
  * agent-chat plugin can populate fields as the run progresses (owner,
@@ -109,6 +110,11 @@ export interface RequestContext {
109
110
  * replay; never used for authorization.
110
111
  */
111
112
  browserSessionId?: string;
113
+ /**
114
+ * Browser attribution captured before a Better Auth signup crosses into its
115
+ * async user-create hook. Analytics-only; never used for authorization.
116
+ */
117
+ signupAttribution?: SignupAttributionContext;
112
118
  /** Canonical client surface for analytics attribution. */
113
119
  clientPlatform?: import("../shared/analytics-platform.js").AnalyticsClientPlatform;
114
120
  /**
@@ -188,6 +194,8 @@ type RequestContextObserver = (ctx: RequestContext) => void;
188
194
  * overlapping async requests are only isolated by native AsyncLocalStorage.
189
195
  */
190
196
  export declare function assertRequestActionSurfaceIsolation(): void;
197
+ /** Whether request context values are isolated across overlapping promises. */
198
+ export declare function hasContinuationLocalRequestContext(): boolean;
191
199
  /**
192
200
  * Register a callback fired every time `runWithRequestContext` enters a new
193
201
  * scope. The hook runs INSIDE the AsyncLocalStorage scope, so observability
@@ -80,6 +80,10 @@ export function assertRequestActionSurfaceIsolation() {
80
80
  throw new Error("Request-scoped action surfaces require continuation-local request context storage; " +
81
81
  "this runtime only provides the non-isolated fallback.");
82
82
  }
83
+ /** Whether request context values are isolated across overlapping promises. */
84
+ export function hasContinuationLocalRequestContext() {
85
+ return globalRef[CONTINUATION_LOCAL_KEY] === true;
86
+ }
83
87
  /**
84
88
  * Register a callback fired every time `runWithRequestContext` enters a new
85
89
  * scope. The hook runs INSIDE the AsyncLocalStorage scope, so observability
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -65,7 +65,9 @@ In a workspace, every app inherits the root `DATABASE_URL` by default. Set
65
65
  Set a Nitro preset for the target you choose. The app code and database
66
66
  contract stay the same.
67
67
 
68
- <Diagram id="doc-block-deployment-targets" title="Supported deployment targets" summary="Agent-Native apps can deploy to Node.js, Docker, Vercel, Netlify, Cloudflare Pages, Cloudflare Workers, AWS Lambda, Deno Deploy, Azure Static Web Apps, Koyeb, and Render.">
68
+ <!-- i18n-copy-ignore: renderer-only Diagram attributes keep this shared UI block in clean mode. -->
69
+
70
+ <Diagram id="doc-block-deployment-targets" title="Supported deployment targets" summary="Agent-Native apps can deploy to Node.js, Docker, Vercel, Netlify, Cloudflare Pages, Cloudflare Workers, AWS Lambda, Deno Deploy, Azure Static Web Apps, Koyeb, and Render." frame="hide" renderMode="design">
69
71
 
70
72
  ```html
71
73
  <!-- i18n-copy-ignore: deployment target logo markup is visual-only; labels are unchanged. -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.0.0-beta-20260820220930",
3
+ "version": "0.0.0-beta-20260820222620",
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": {
@@ -427,7 +427,7 @@
427
427
  "y-protocols": "^1.0.7",
428
428
  "yjs": "^13.6.32",
429
429
  "zod": "^4.3.6",
430
- "@agent-native/recap-cli": "0.0.0-beta-20260820220930",
430
+ "@agent-native/recap-cli": "0.5.7",
431
431
  "@agent-native/toolkit": "^0.16.9"
432
432
  },
433
433
  "devDependencies": {
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.
@@ -134,3 +134,14 @@ hand-duplicating framework logic.
134
134
  `agent-surfaces`.
135
135
  - Do not copy framework runtime internals when a public API or narrow UI copy
136
136
  will do; read `customizing-agent-native` for the supported override ladder.
137
+
138
+ ## Authoring visual docs blocks
139
+
140
+ Treat the shared `Diagram` block as a renderer, not as a reason to style every
141
+ docs UI as a sketch. Use the hand-drawn font and Rough.js only for actual
142
+ diagrams or wireframes that communicate structure, flow, or a design draft.
143
+ For polished UI-like content - cards, logo walls, tables, controls, or product
144
+ shells — set `renderMode="design"` (and usually `frame="hide"` when the block
145
+ is the content itself). Design mode preserves normal docs typography and
146
+ disables the Rough.js overlay. Keep `data-rough` only on elements intentionally
147
+ sketched inside a real diagram.