@agent-native/core 0.90.8 → 0.90.10

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 (114) hide show
  1. package/README.md +7 -28
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +16 -0
  4. package/corpus/core/docs/content/agent-native-toolkit.mdx +1 -1
  5. package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +1 -1
  6. package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +3 -3
  7. package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +1 -1
  8. package/corpus/core/docs/content/locales/de-DE/tracking.mdx +3 -3
  9. package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +1 -1
  10. package/corpus/core/docs/content/locales/es-ES/tracking.mdx +3 -3
  11. package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +1 -1
  12. package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +3 -3
  13. package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +1 -1
  14. package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +3 -3
  15. package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +1 -1
  16. package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +3 -3
  17. package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +1 -1
  18. package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +3 -3
  19. package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +1 -1
  20. package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +3 -3
  21. package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +1 -1
  22. package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +3 -3
  23. package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +1 -1
  24. package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +3 -3
  25. package/corpus/core/docs/content/tracking.mdx +3 -3
  26. package/corpus/core/package.json +1 -1
  27. package/corpus/core/src/agent/production-agent.ts +15 -4
  28. package/corpus/core/src/cli/deploy-build.ts +50 -0
  29. package/corpus/core/src/cli/index.ts +9 -4
  30. package/corpus/core/src/client/analytics.ts +17 -3
  31. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +3 -1
  32. package/corpus/core/src/client/blocks/library/wireframe.tsx +6 -1
  33. package/corpus/core/src/deploy/build.ts +64 -27
  34. package/corpus/core/src/file-upload/builder.ts +10 -23
  35. package/corpus/core/src/file-upload/types.ts +3 -3
  36. package/corpus/core/src/styles/blocks.css +7 -0
  37. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +9 -4
  38. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +21 -7
  39. package/corpus/templates/analytics/app/entry.client.tsx +6 -1
  40. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -1
  41. package/corpus/templates/analytics/changelog/2026-07-07-analytics-now-recovers-from-stale-route-chunks-after-deploys.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-07-07-metric-dashboard-cards-now-show-warehouse-backed-numeric-val.md +6 -0
  43. package/corpus/templates/clips/actions/create-recording.ts +1 -1
  44. package/corpus/templates/clips/actions/finalize-recording.ts +2 -2
  45. package/corpus/templates/clips/actions/import-loom-recording.ts +1 -1
  46. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +1 -1
  47. package/corpus/templates/clips/changelog/2026-07-07-clip-video-uploads-now-compress-in-the-background-while-keeping.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-07-desktop-settings-and-fn-dictation-are-more-reliable.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-07-desktop-settings-now-includes-a-manual-update-check-with-res.md +6 -0
  50. package/corpus/templates/clips/desktop/src/app.tsx +95 -8
  51. package/corpus/templates/clips/desktop/src/lib/updater.ts +60 -43
  52. package/corpus/templates/clips/desktop/src/styles.css +5 -0
  53. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +93 -23
  54. package/corpus/templates/dispatch/changelog/2026-07-07-overview-is-now-just-ask-dispatch-and-a-list-of-apps.md +6 -0
  55. package/dist/agent/production-agent.d.ts.map +1 -1
  56. package/dist/agent/production-agent.js +16 -3
  57. package/dist/agent/production-agent.js.map +1 -1
  58. package/dist/cli/deploy-build.d.ts +12 -0
  59. package/dist/cli/deploy-build.d.ts.map +1 -0
  60. package/dist/cli/deploy-build.js +30 -0
  61. package/dist/cli/deploy-build.js.map +1 -0
  62. package/dist/cli/index.js +8 -4
  63. package/dist/cli/index.js.map +1 -1
  64. package/dist/client/analytics.d.ts.map +1 -1
  65. package/dist/client/analytics.js +14 -3
  66. package/dist/client/analytics.js.map +1 -1
  67. package/dist/client/blocks/library/wireframe-kit.d.ts +1 -0
  68. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  69. package/dist/client/blocks/library/wireframe-kit.js +2 -1
  70. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  71. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  72. package/dist/client/blocks/library/wireframe.js +6 -1
  73. package/dist/client/blocks/library/wireframe.js.map +1 -1
  74. package/dist/collab/awareness.d.ts +2 -2
  75. package/dist/collab/awareness.d.ts.map +1 -1
  76. package/dist/collab/struct-routes.d.ts +1 -1
  77. package/dist/deploy/build.d.ts +6 -0
  78. package/dist/deploy/build.d.ts.map +1 -1
  79. package/dist/deploy/build.js +54 -25
  80. package/dist/deploy/build.js.map +1 -1
  81. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  82. package/dist/file-upload/builder.d.ts.map +1 -1
  83. package/dist/file-upload/builder.js +9 -19
  84. package/dist/file-upload/builder.js.map +1 -1
  85. package/dist/file-upload/types.d.ts +3 -3
  86. package/dist/file-upload/types.d.ts.map +1 -1
  87. package/dist/file-upload/types.js.map +1 -1
  88. package/dist/notifications/routes.d.ts +1 -1
  89. package/dist/resources/handlers.d.ts +3 -3
  90. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  91. package/dist/styles/blocks.css +7 -0
  92. package/docs/content/agent-native-toolkit.mdx +1 -1
  93. package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +1 -1
  94. package/docs/content/locales/ar-SA/tracking.mdx +3 -3
  95. package/docs/content/locales/de-DE/agent-native-toolkit.mdx +1 -1
  96. package/docs/content/locales/de-DE/tracking.mdx +3 -3
  97. package/docs/content/locales/es-ES/agent-native-toolkit.mdx +1 -1
  98. package/docs/content/locales/es-ES/tracking.mdx +3 -3
  99. package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +1 -1
  100. package/docs/content/locales/fr-FR/tracking.mdx +3 -3
  101. package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +1 -1
  102. package/docs/content/locales/hi-IN/tracking.mdx +3 -3
  103. package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +1 -1
  104. package/docs/content/locales/ja-JP/tracking.mdx +3 -3
  105. package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +1 -1
  106. package/docs/content/locales/ko-KR/tracking.mdx +3 -3
  107. package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +1 -1
  108. package/docs/content/locales/pt-BR/tracking.mdx +3 -3
  109. package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +1 -1
  110. package/docs/content/locales/zh-CN/tracking.mdx +3 -3
  111. package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +1 -1
  112. package/docs/content/locales/zh-TW/tracking.mdx +3 -3
  113. package/docs/content/tracking.mdx +3 -3
  114. package/package.json +1 -1
@@ -23,7 +23,7 @@ being rebuilt in slightly different ways every time.
23
23
  <Screen
24
24
  surface="desktop"
25
25
  html={
26
- "<div style='min-height:560px;box-sizing:border-box;padding:24px;display:grid;grid-template-columns:200px 1fr 280px;gap:16px'><aside class='wf-card' style='display:flex;flex-direction:column;gap:8px;padding:14px'><div style='display:flex;align-items:center;gap:8px'><div style='width:26px;height:26px;border-radius:8px;background:var(--wf-accent-soft);display:flex;align-items:center;justify-content:center;font-size:12px;flex:none'>A</div><strong style='flex:1'>Acme</strong><span data-icon='chevronDown'></span></div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;background:var(--wf-accent-soft)'><span data-icon='search'></span> Home</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='edit'></span> Documents</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='user'></span> Team</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='settings'></span> Settings</div><div style='flex:1'></div><div style='display:flex;align-items:center;gap:8px;padding-top:10px;border-top:1.4px solid var(--wf-line)'><div style='width:24px;height:24px;border-radius:999px;background:var(--wf-accent-soft);flex:none'></div><small style='flex:1'>Steve</small><span data-icon='dots'></span></div></aside><main class='wf-card' style='display:flex;flex-direction:column;gap:14px'><div style='display:flex;align-items:center;gap:10px'><h2 style='margin:0'>Q3 Forecast</h2><span class='wf-pill'>Org</span><div style='flex:1'></div><div style='display:flex'><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div></div><button class='primary'><span data-icon='user'></span> Share</button></div><div class='wf-box' style='display:flex;flex-direction:column;gap:10px'><small class='wf-muted'>Revenue by month</small><div style='display:flex;align-items:flex-end;gap:10px;height:120px'><div style='flex:1;height:45%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:65%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:55%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:80%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:95%;background:var(--wf-accent);border-radius:4px'></div></div></div><div style='display:flex;gap:10px'><div class='wf-box' style='flex:1'><small class='wf-muted'>ARR</small><br/><strong>$4.2M</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Win rate</small><br/><strong>28%</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Pipeline</small><br/><strong>3.2x</strong></div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Agent</strong><div class='wf-box'>Summarize Q3 versus Q2 and flag risks.</div><div class='wf-box' style='background:var(--wf-accent-soft)'>Revenue up 12%. Two pipeline risks flagged.</div><div style='flex:1'></div><div class='wf-card' style='padding:10px;display:flex;flex-direction:column;gap:8px'><span class='wf-muted'>Ask about this dashboard...</span><div style='display:flex;align-items:center;gap:6px'><span class='wf-pill'>Q3 Forecast</span><span class='wf-pill'>GPT-5</span><div style='flex:1'></div><button class='primary'><span data-icon='send'></span></button></div></div></aside></div>"
26
+ "<div style='min-height:560px;box-sizing:border-box;display:grid;grid-template-columns:200px 1fr 280px;gap:16px'><aside class='wf-card' style='display:flex;flex-direction:column;gap:8px;padding:14px'><div style='display:flex;align-items:center;gap:8px'><div style='width:26px;height:26px;border-radius:8px;background:var(--wf-accent-soft);display:flex;align-items:center;justify-content:center;font-size:12px;flex:none'>A</div><strong style='flex:1'>Acme</strong><span data-icon='chevronDown'></span></div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;background:var(--wf-accent-soft)'><span data-icon='search'></span> Home</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='edit'></span> Documents</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='user'></span> Team</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='settings'></span> Settings</div><div style='flex:1'></div><div style='display:flex;align-items:center;gap:8px;padding-top:10px;border-top:1.4px solid var(--wf-line)'><div style='width:24px;height:24px;border-radius:999px;background:var(--wf-accent-soft);flex:none'></div><small style='flex:1'>Steve</small><span data-icon='dots'></span></div></aside><main class='wf-card' style='display:flex;flex-direction:column;gap:14px'><div style='display:flex;align-items:center;gap:10px'><h2 style='margin:0'>Q3 Forecast</h2><div style='flex:1'></div><div style='display:flex'><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div></div><button class='primary'><span data-icon='user'></span> Share</button></div><div class='wf-box' style='display:flex;flex-direction:column;gap:10px'><small class='wf-muted'>Revenue by month</small><div style='display:flex;align-items:flex-end;gap:10px;height:120px'><div style='flex:1;height:45%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:65%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:55%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:80%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:95%;background:var(--wf-accent);border-radius:4px'></div></div></div><div style='display:flex;gap:10px'><div class='wf-box' style='flex:1'><small class='wf-muted'>ARR</small><br/><strong>$4.2M</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Win rate</small><br/><strong>28%</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Pipeline</small><br/><strong>3.2x</strong></div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Agent</strong><div class='wf-box'>Summarize Q3 versus Q2 and flag risks.</div><div class='wf-box' style='background:var(--wf-accent-soft)'>Revenue up 12%. Two pipeline risks flagged.</div><div style='flex:1'></div><div class='wf-card' style='padding:10px;display:flex;flex-direction:column;gap:8px'><span class='wf-muted'>Ask about this dashboard...</span><div style='display:flex;align-items:center;gap:6px'><span class='wf-pill'>Q3 Forecast</span><span class='wf-pill'>GPT-5</span><div style='flex:1'></div><button class='primary'><span data-icon='send'></span></button></div></div></aside></div>"
27
27
  }
28
28
  />
29
29
  </WireframeBlock>
@@ -200,7 +200,7 @@ configureTracking({
200
200
  endpoint: "https://analytics.example.com/api/analytics/track",
201
201
  sessionReplay: {
202
202
  enabled: true,
203
- sampleRate: 0.1,
203
+ sampleRate: 1,
204
204
  },
205
205
  getDefaultProps: (_event, props) => ({
206
206
  ...props,
@@ -218,14 +218,14 @@ As raízes dos modelos do Agent Native já chamam `configureTracking()`. As impl
218
218
  VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY=anpk_...
219
219
  VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT=https://analytics.example.com/api/analytics/track
220
220
  VITE_AGENT_NATIVE_SESSION_REPLAY_ENABLED=true
221
- VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=0.1
221
+ VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=1
222
222
  ```
223
223
 
224
224
  O auxiliar do navegador também realiza uma leitura best-effort e não bloqueante da sessão de autenticação atual do Agent Native. O replay é, por padrão, exclusivo para usuários conectados: quando `sessionReplay` está habilitado, a gravação não começa a menos que a sessão seja resolvida para um endereço de e-mail de usuário. Replays de usuários conectados incluem `userId`/`userEmail` baseados em e-mail, além de `orgId`.
225
225
 
226
226
  Defina `sessionReplay.requireSignedInUser: false` ou `VITE_AGENT_NATIVE_SESSION_REPLAY_REQUIRE_AUTH=false` apenas para uma implantação de replay anônimo intencional. Quando o controle de autenticação está desabilitado, as gravações anônimas permanecem consultáveis por visitante anônimo, sessão, aplicativo/modelo, hostname e caminho.
227
227
 
228
- O replay de sessão é amostrado de forma determinística por sessão do navegador. Um `sampleRate` de `0.1` registra cerca de 10% das sessões elegíveis; use `1` quando a população elegível for intencionalmente pequena, como em dogfooding somente para usuários conectados.
228
+ O replay de sessão é amostrado de forma determinística por sessão do navegador. Um `sampleRate` de `1` registra todas as sessões elegíveis; use valores menores, como `0.1`, quando a população elegível for intencionalmente ampla.
229
229
 
230
230
  Os padrões de privacidade são intencionalmente conservadores, mas ainda úteis para a reprodução:
231
231
 
@@ -23,7 +23,7 @@ being rebuilt in slightly different ways every time.
23
23
  <Screen
24
24
  surface="desktop"
25
25
  html={
26
- "<div style='min-height:560px;box-sizing:border-box;padding:24px;display:grid;grid-template-columns:200px 1fr 280px;gap:16px'><aside class='wf-card' style='display:flex;flex-direction:column;gap:8px;padding:14px'><div style='display:flex;align-items:center;gap:8px'><div style='width:26px;height:26px;border-radius:8px;background:var(--wf-accent-soft);display:flex;align-items:center;justify-content:center;font-size:12px;flex:none'>A</div><strong style='flex:1'>Acme</strong><span data-icon='chevronDown'></span></div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;background:var(--wf-accent-soft)'><span data-icon='search'></span> Home</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='edit'></span> Documents</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='user'></span> Team</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='settings'></span> Settings</div><div style='flex:1'></div><div style='display:flex;align-items:center;gap:8px;padding-top:10px;border-top:1.4px solid var(--wf-line)'><div style='width:24px;height:24px;border-radius:999px;background:var(--wf-accent-soft);flex:none'></div><small style='flex:1'>Steve</small><span data-icon='dots'></span></div></aside><main class='wf-card' style='display:flex;flex-direction:column;gap:14px'><div style='display:flex;align-items:center;gap:10px'><h2 style='margin:0'>Q3 Forecast</h2><span class='wf-pill'>Org</span><div style='flex:1'></div><div style='display:flex'><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div></div><button class='primary'><span data-icon='user'></span> Share</button></div><div class='wf-box' style='display:flex;flex-direction:column;gap:10px'><small class='wf-muted'>Revenue by month</small><div style='display:flex;align-items:flex-end;gap:10px;height:120px'><div style='flex:1;height:45%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:65%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:55%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:80%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:95%;background:var(--wf-accent);border-radius:4px'></div></div></div><div style='display:flex;gap:10px'><div class='wf-box' style='flex:1'><small class='wf-muted'>ARR</small><br/><strong>$4.2M</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Win rate</small><br/><strong>28%</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Pipeline</small><br/><strong>3.2x</strong></div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Agent</strong><div class='wf-box'>Summarize Q3 versus Q2 and flag risks.</div><div class='wf-box' style='background:var(--wf-accent-soft)'>Revenue up 12%. Two pipeline risks flagged.</div><div style='flex:1'></div><div class='wf-card' style='padding:10px;display:flex;flex-direction:column;gap:8px'><span class='wf-muted'>Ask about this dashboard...</span><div style='display:flex;align-items:center;gap:6px'><span class='wf-pill'>Q3 Forecast</span><span class='wf-pill'>GPT-5</span><div style='flex:1'></div><button class='primary'><span data-icon='send'></span></button></div></div></aside></div>"
26
+ "<div style='min-height:560px;box-sizing:border-box;display:grid;grid-template-columns:200px 1fr 280px;gap:16px'><aside class='wf-card' style='display:flex;flex-direction:column;gap:8px;padding:14px'><div style='display:flex;align-items:center;gap:8px'><div style='width:26px;height:26px;border-radius:8px;background:var(--wf-accent-soft);display:flex;align-items:center;justify-content:center;font-size:12px;flex:none'>A</div><strong style='flex:1'>Acme</strong><span data-icon='chevronDown'></span></div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;background:var(--wf-accent-soft)'><span data-icon='search'></span> Home</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='edit'></span> Documents</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='user'></span> Team</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='settings'></span> Settings</div><div style='flex:1'></div><div style='display:flex;align-items:center;gap:8px;padding-top:10px;border-top:1.4px solid var(--wf-line)'><div style='width:24px;height:24px;border-radius:999px;background:var(--wf-accent-soft);flex:none'></div><small style='flex:1'>Steve</small><span data-icon='dots'></span></div></aside><main class='wf-card' style='display:flex;flex-direction:column;gap:14px'><div style='display:flex;align-items:center;gap:10px'><h2 style='margin:0'>Q3 Forecast</h2><div style='flex:1'></div><div style='display:flex'><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div></div><button class='primary'><span data-icon='user'></span> Share</button></div><div class='wf-box' style='display:flex;flex-direction:column;gap:10px'><small class='wf-muted'>Revenue by month</small><div style='display:flex;align-items:flex-end;gap:10px;height:120px'><div style='flex:1;height:45%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:65%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:55%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:80%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:95%;background:var(--wf-accent);border-radius:4px'></div></div></div><div style='display:flex;gap:10px'><div class='wf-box' style='flex:1'><small class='wf-muted'>ARR</small><br/><strong>$4.2M</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Win rate</small><br/><strong>28%</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Pipeline</small><br/><strong>3.2x</strong></div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Agent</strong><div class='wf-box'>Summarize Q3 versus Q2 and flag risks.</div><div class='wf-box' style='background:var(--wf-accent-soft)'>Revenue up 12%. Two pipeline risks flagged.</div><div style='flex:1'></div><div class='wf-card' style='padding:10px;display:flex;flex-direction:column;gap:8px'><span class='wf-muted'>Ask about this dashboard...</span><div style='display:flex;align-items:center;gap:6px'><span class='wf-pill'>Q3 Forecast</span><span class='wf-pill'>GPT-5</span><div style='flex:1'></div><button class='primary'><span data-icon='send'></span></button></div></div></aside></div>"
27
27
  }
28
28
  />
29
29
  </WireframeBlock>
@@ -200,7 +200,7 @@ configureTracking({
200
200
  endpoint: "https://analytics.example.com/api/analytics/track",
201
201
  sessionReplay: {
202
202
  enabled: true,
203
- sampleRate: 0.1,
203
+ sampleRate: 1,
204
204
  },
205
205
  getDefaultProps: (_event, props) => ({
206
206
  ...props,
@@ -218,14 +218,14 @@ Agent Native 模板根已经调用了 `configureTracking()`。托管模板部署
218
218
  VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY=anpk_...
219
219
  VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT=https://analytics.example.com/api/analytics/track
220
220
  VITE_AGENT_NATIVE_SESSION_REPLAY_ENABLED=true
221
- VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=0.1
221
+ VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=1
222
222
  ```
223
223
 
224
224
  浏览器端辅助函数还会尽力地、非阻塞地读取当前 Agent Native 认证会话。默认情况下,重放仅限已登录用户:启用 `sessionReplay` 后,除非会话解析出用户邮箱地址,否则不会开始录制。已登录用户的重放会包含基于邮箱的 `userId`/`userEmail` 以及 `orgId`。
225
225
 
226
226
  仅在有意进行匿名重放部署时,才设置 `sessionReplay.requireSignedInUser: false` 或 `VITE_AGENT_NATIVE_SESSION_REPLAY_REQUIRE_AUTH=false`。禁用认证门控后,匿名录制仍可按匿名访客、会话、应用/模板、主机名和路径进行查询。
227
227
 
228
- 会话重放按浏览器会话确定性采样。`sampleRate` 为 `0.1` 时,会记录约 10% 的符合条件的会话;当符合条件的用户群体本就很小时(例如仅限登录用户的内部测试),可使用 `1`。
228
+ 会话重放按浏览器会话确定性采样。`sampleRate` 为 `1` 时,会记录所有符合条件的会话;当符合条件的用户群体有意较大时,可使用较低值,例如 `0.1`。
229
229
 
230
230
  隐私默认设置刻意保持保守,但仍能满足回放的可用性:
231
231
 
@@ -23,7 +23,7 @@ being rebuilt in slightly different ways every time.
23
23
  <Screen
24
24
  surface="desktop"
25
25
  html={
26
- "<div style='min-height:560px;box-sizing:border-box;padding:24px;display:grid;grid-template-columns:200px 1fr 280px;gap:16px'><aside class='wf-card' style='display:flex;flex-direction:column;gap:8px;padding:14px'><div style='display:flex;align-items:center;gap:8px'><div style='width:26px;height:26px;border-radius:8px;background:var(--wf-accent-soft);display:flex;align-items:center;justify-content:center;font-size:12px;flex:none'>A</div><strong style='flex:1'>Acme</strong><span data-icon='chevronDown'></span></div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;background:var(--wf-accent-soft)'><span data-icon='search'></span> Home</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='edit'></span> Documents</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='user'></span> Team</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='settings'></span> Settings</div><div style='flex:1'></div><div style='display:flex;align-items:center;gap:8px;padding-top:10px;border-top:1.4px solid var(--wf-line)'><div style='width:24px;height:24px;border-radius:999px;background:var(--wf-accent-soft);flex:none'></div><small style='flex:1'>Steve</small><span data-icon='dots'></span></div></aside><main class='wf-card' style='display:flex;flex-direction:column;gap:14px'><div style='display:flex;align-items:center;gap:10px'><h2 style='margin:0'>Q3 Forecast</h2><span class='wf-pill'>Org</span><div style='flex:1'></div><div style='display:flex'><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div></div><button class='primary'><span data-icon='user'></span> Share</button></div><div class='wf-box' style='display:flex;flex-direction:column;gap:10px'><small class='wf-muted'>Revenue by month</small><div style='display:flex;align-items:flex-end;gap:10px;height:120px'><div style='flex:1;height:45%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:65%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:55%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:80%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:95%;background:var(--wf-accent);border-radius:4px'></div></div></div><div style='display:flex;gap:10px'><div class='wf-box' style='flex:1'><small class='wf-muted'>ARR</small><br/><strong>$4.2M</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Win rate</small><br/><strong>28%</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Pipeline</small><br/><strong>3.2x</strong></div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Agent</strong><div class='wf-box'>Summarize Q3 versus Q2 and flag risks.</div><div class='wf-box' style='background:var(--wf-accent-soft)'>Revenue up 12%. Two pipeline risks flagged.</div><div style='flex:1'></div><div class='wf-card' style='padding:10px;display:flex;flex-direction:column;gap:8px'><span class='wf-muted'>Ask about this dashboard...</span><div style='display:flex;align-items:center;gap:6px'><span class='wf-pill'>Q3 Forecast</span><span class='wf-pill'>GPT-5</span><div style='flex:1'></div><button class='primary'><span data-icon='send'></span></button></div></div></aside></div>"
26
+ "<div style='min-height:560px;box-sizing:border-box;display:grid;grid-template-columns:200px 1fr 280px;gap:16px'><aside class='wf-card' style='display:flex;flex-direction:column;gap:8px;padding:14px'><div style='display:flex;align-items:center;gap:8px'><div style='width:26px;height:26px;border-radius:8px;background:var(--wf-accent-soft);display:flex;align-items:center;justify-content:center;font-size:12px;flex:none'>A</div><strong style='flex:1'>Acme</strong><span data-icon='chevronDown'></span></div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;background:var(--wf-accent-soft)'><span data-icon='search'></span> Home</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='edit'></span> Documents</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='user'></span> Team</div><div style='display:flex;align-items:center;gap:8px;padding:7px 9px'><span data-icon='settings'></span> Settings</div><div style='flex:1'></div><div style='display:flex;align-items:center;gap:8px;padding-top:10px;border-top:1.4px solid var(--wf-line)'><div style='width:24px;height:24px;border-radius:999px;background:var(--wf-accent-soft);flex:none'></div><small style='flex:1'>Steve</small><span data-icon='dots'></span></div></aside><main class='wf-card' style='display:flex;flex-direction:column;gap:14px'><div style='display:flex;align-items:center;gap:10px'><h2 style='margin:0'>Q3 Forecast</h2><div style='flex:1'></div><div style='display:flex'><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent-soft);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div><div style='width:26px;height:26px;border-radius:999px;background:var(--wf-accent);border:2px solid var(--wf-card);margin-left:-8px;flex:none'></div></div><button class='primary'><span data-icon='user'></span> Share</button></div><div class='wf-box' style='display:flex;flex-direction:column;gap:10px'><small class='wf-muted'>Revenue by month</small><div style='display:flex;align-items:flex-end;gap:10px;height:120px'><div style='flex:1;height:45%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:65%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:55%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:80%;background:var(--wf-accent-soft);border-radius:4px'></div><div style='flex:1;height:95%;background:var(--wf-accent);border-radius:4px'></div></div></div><div style='display:flex;gap:10px'><div class='wf-box' style='flex:1'><small class='wf-muted'>ARR</small><br/><strong>$4.2M</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Win rate</small><br/><strong>28%</strong></div><div class='wf-box' style='flex:1'><small class='wf-muted'>Pipeline</small><br/><strong>3.2x</strong></div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Agent</strong><div class='wf-box'>Summarize Q3 versus Q2 and flag risks.</div><div class='wf-box' style='background:var(--wf-accent-soft)'>Revenue up 12%. Two pipeline risks flagged.</div><div style='flex:1'></div><div class='wf-card' style='padding:10px;display:flex;flex-direction:column;gap:8px'><span class='wf-muted'>Ask about this dashboard...</span><div style='display:flex;align-items:center;gap:6px'><span class='wf-pill'>Q3 Forecast</span><span class='wf-pill'>GPT-5</span><div style='flex:1'></div><button class='primary'><span data-icon='send'></span></button></div></div></aside></div>"
27
27
  }
28
28
  />
29
29
  </WireframeBlock>
@@ -200,7 +200,7 @@ configureTracking({
200
200
  endpoint: "https://analytics.example.com/api/analytics/track",
201
201
  sessionReplay: {
202
202
  enabled: true,
203
- sampleRate: 0.1,
203
+ sampleRate: 1,
204
204
  },
205
205
  getDefaultProps: (_event, props) => ({
206
206
  ...props,
@@ -218,14 +218,14 @@ Agent Native 範本根目錄已經呼叫了 `configureTracking()`。託管範本
218
218
  VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY=anpk_...
219
219
  VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT=https://analytics.example.com/api/analytics/track
220
220
  VITE_AGENT_NATIVE_SESSION_REPLAY_ENABLED=true
221
- VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=0.1
221
+ VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=1
222
222
  ```
223
223
 
224
224
  瀏覽器輔助程式也會盡力執行、非阻塞式讀取目前的 Agent Native 驗證工作階段。重播預設僅限已登入使用者:當 `sessionReplay` 啟用時,除非工作階段解析出使用者電子郵件地址,否則不會開始錄製。已登入使用者的重播包含以電子郵件為基礎的 `userId`/`userEmail` 以及 `orgId`。
225
225
 
226
226
  僅在刻意進行匿名重播部署時,才設定 `sessionReplay.requireSignedInUser: false` 或 `VITE_AGENT_NATIVE_SESSION_REPLAY_REQUIRE_AUTH=false`。停用驗證閘控後,匿名錄製仍可依匿名訪客、工作階段、應用程式/範本、主機名稱與路徑進行查詢。
227
227
 
228
- 工作階段重播是依瀏覽器工作階段進行確定性取樣的。`sampleRate` 為 `0.1` 表示大約記錄 10% 符合條件的工作階段;當符合條件的母體刻意較小時(例如僅限已登入使用者的內部試用),可使用 `1`。
228
+ 工作階段重播是依瀏覽器工作階段進行確定性取樣的。`sampleRate` 為 `1` 表示記錄所有符合條件的工作階段;當符合條件的母體刻意較大時,可使用較低值,例如 `0.1`。
229
229
 
230
230
  隱私預設值刻意設計得保守,但仍足以支援播放:
231
231
 
@@ -200,7 +200,7 @@ configureTracking({
200
200
  endpoint: "https://analytics.example.com/api/analytics/track",
201
201
  sessionReplay: {
202
202
  enabled: true,
203
- sampleRate: 0.1,
203
+ sampleRate: 1,
204
204
  },
205
205
  getDefaultProps: (_event, props) => ({
206
206
  ...props,
@@ -218,14 +218,14 @@ Agent Native template roots already call `configureTracking()`. Hosted template
218
218
  VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY=anpk_...
219
219
  VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT=https://analytics.example.com/api/analytics/track
220
220
  VITE_AGENT_NATIVE_SESSION_REPLAY_ENABLED=true
221
- VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=0.1
221
+ VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=1
222
222
  ```
223
223
 
224
224
  The browser helper also performs a best-effort, non-blocking read of the current Agent Native auth session. Replay is signed-in-only by default: when `sessionReplay` is enabled, recording does not start unless the session resolves to a user email address. Signed-in replays include email-backed `userId`/`userEmail` plus `orgId`.
225
225
 
226
226
  Set `sessionReplay.requireSignedInUser: false` or `VITE_AGENT_NATIVE_SESSION_REPLAY_REQUIRE_AUTH=false` only for an intentional anonymous replay deployment. When auth gating is disabled, anonymous recordings remain queryable by anonymous visitor, session, app/template, hostname, and path.
227
227
 
228
- Session replay is sampled deterministically per browser session. A `sampleRate` of `0.1` records about 10% of eligible sessions; use `1` when the eligible population is intentionally small, such as logged-in-only dogfooding.
228
+ Session replay is sampled deterministically per browser session. A `sampleRate` of `1` records all eligible sessions; use lower values, such as `0.1`, when the eligible population is intentionally broad.
229
229
 
230
230
  Privacy defaults are intentionally conservative but still useful for playback:
231
231
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.90.8",
3
+ "version": "0.90.10",
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": {
@@ -1015,6 +1015,7 @@ const TOOL_INPUT_ACTIVITY_INTERVAL_MS = 1500;
1015
1015
  const ACTION_PREPARATION_NO_PROGRESS_TIMEOUT_MS = 90_000;
1016
1016
  const ACTION_PREPARATION_ZERO_BYTE_RESTART_LIMIT = 2;
1017
1017
  const MODEL_STREAM_NO_PROGRESS_TIMEOUT_MS = 90_000;
1018
+ const EMPTY_FINAL_RESPONSE_RETRY_LIMIT = 1;
1018
1019
  const MAIN_CHAT_INTERNAL_CONTINUATION_LIMIT = 6;
1019
1020
  const RUN_BUDGET_EXHAUSTED_ERROR_CODE = "run_budget_exhausted";
1020
1021
  const RUN_BUDGET_EXHAUSTED_MESSAGE =
@@ -2733,6 +2734,7 @@ export async function runAgentLoop(opts: {
2733
2734
  }
2734
2735
 
2735
2736
  let finalGuardRetries = 0;
2737
+ let emptyFinalResponseRetries = 0;
2736
2738
  let iterations = 0;
2737
2739
 
2738
2740
  // Set when an in-loop processor aborts via `abort()` / throws a `TripWire`.
@@ -3349,16 +3351,24 @@ export async function runAgentLoop(opts: {
3349
3351
  // text — typically when reasoning consumes the entire output-token
3350
3352
  // budget. Without a final text part the SSE stream still ends with a
3351
3353
  // clean `done`, which renders as a totally empty assistant bubble.
3352
- // Surface a plain-language error so the user knows what happened.
3353
- if (
3354
+ // Retry once so a transient reasoning-budget miss can still finish; if
3355
+ // the retry also has no visible content, surface a plain-language error.
3356
+ const hasEmptyFinalResponse =
3354
3357
  !guardEmittedFallback &&
3355
3358
  collectTextParts(assistantContentForHistory).trim().length === 0 &&
3356
- streamedAssistantText.trim().length === 0
3357
- ) {
3359
+ streamedAssistantText.trim().length === 0;
3360
+ if (hasEmptyFinalResponse) {
3361
+ if (emptyFinalResponseRetries < EMPTY_FINAL_RESPONSE_RETRY_LIMIT) {
3362
+ emptyFinalResponseRetries += 1;
3363
+ appendAgentLoopContinuation(messages, "max_tokens");
3364
+ continue;
3365
+ }
3358
3366
  send({
3359
3367
  type: "text",
3360
3368
  text: "The model returned an empty response. This usually means reasoning used the full output-token budget. Try again, or pick a different model from the model menu.",
3361
3369
  });
3370
+ } else {
3371
+ emptyFinalResponseRetries = 0;
3362
3372
  }
3363
3373
  break;
3364
3374
  }
@@ -3369,6 +3379,7 @@ export async function runAgentLoop(opts: {
3369
3379
  // finalGuardRetries stays at 1 from a prior cycle and the guard is
3370
3380
  // permanently disabled for the rest of a long multi-step run.
3371
3381
  finalGuardRetries = 0;
3382
+ emptyFinalResponseRetries = 0;
3372
3383
 
3373
3384
  flushUnstreamedAssistantText();
3374
3385
 
@@ -0,0 +1,50 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export interface DeployPostBuildInvocation {
5
+ command: string;
6
+ args: string[];
7
+ scriptPath: string;
8
+ }
9
+
10
+ export function hasDeployPreset(env: NodeJS.ProcessEnv = process.env): boolean {
11
+ return Boolean(env.NITRO_PRESET?.trim());
12
+ }
13
+
14
+ export function resolveDeployPostBuildInvocation({
15
+ cliDir,
16
+ env = process.env,
17
+ findTsxBin,
18
+ }: {
19
+ cliDir: string;
20
+ env?: NodeJS.ProcessEnv;
21
+ findTsxBin: () => string;
22
+ }): DeployPostBuildInvocation | undefined {
23
+ const builtDeployBuild = path.resolve(cliDir, "../deploy/build.js");
24
+ if (fs.existsSync(builtDeployBuild)) {
25
+ return {
26
+ command: "node",
27
+ args: [builtDeployBuild],
28
+ scriptPath: builtDeployBuild,
29
+ };
30
+ }
31
+
32
+ const sourceDeployBuild = path.resolve(cliDir, "../deploy/build.ts");
33
+ if (fs.existsSync(sourceDeployBuild)) {
34
+ return {
35
+ command: findTsxBin(),
36
+ args: [sourceDeployBuild],
37
+ scriptPath: sourceDeployBuild,
38
+ };
39
+ }
40
+
41
+ if (hasDeployPreset(env)) {
42
+ throw new Error(
43
+ `[build] Deploy build script not found. Expected ${builtDeployBuild} ` +
44
+ `or ${sourceDeployBuild}; refusing to publish an incomplete ` +
45
+ `NITRO_PRESET=${env.NITRO_PRESET} build.`,
46
+ );
47
+ }
48
+
49
+ return undefined;
50
+ }
@@ -8,6 +8,8 @@ import { fileURLToPath } from "url";
8
8
 
9
9
  import * as Sentry from "@sentry/node";
10
10
 
11
+ import { resolveDeployPostBuildInvocation } from "./deploy-build.js";
12
+
11
13
  // Resolve version once at module scope — used by both --version and --help
12
14
  let _version = "unknown";
13
15
  try {
@@ -577,15 +579,18 @@ switch (command) {
577
579
  // `agent-native start` and for serverless presets.
578
580
  if (isReactRouterFramework()) {
579
581
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
580
- const deployBuild = path.resolve(__dirname, "../deploy/build.js");
581
- if (fs.existsSync(deployBuild)) {
582
- await runBuildStep("node", [deployBuild], {
582
+ const deployBuild = resolveDeployPostBuildInvocation({
583
+ cliDir: __dirname,
584
+ findTsxBin,
585
+ });
586
+ if (deployBuild) {
587
+ await runBuildStep(deployBuild.command, deployBuild.args, {
583
588
  label: "deploy-build",
584
589
  env: process.env,
585
590
  });
586
591
  } else {
587
592
  console.warn(
588
- `[build] Deploy build script not found at ${deployBuild}. Skipping post-build step.`,
593
+ "[build] Deploy build script not found and no deploy preset is configured. Skipping post-build step.",
589
594
  );
590
595
  }
591
596
  }
@@ -1005,8 +1005,16 @@ function configuredSessionReplayOptions(
1005
1005
  config: boolean | SessionReplayOptions | undefined,
1006
1006
  tracking: { endpoint?: string; publicKey?: string } = {},
1007
1007
  ): SessionReplayOptions | null {
1008
- const publicKey = tracking.publicKey || _agentNativeAnalyticsPublicKey;
1009
- const trackingEndpoint = tracking.endpoint || _agentNativeAnalyticsEndpoint;
1008
+ const env = (import.meta.env as Record<string, string | undefined>) ?? {};
1009
+ const publicKey =
1010
+ tracking.publicKey ||
1011
+ _agentNativeAnalyticsPublicKey ||
1012
+ env.VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;
1013
+ const trackingEndpoint =
1014
+ tracking.endpoint ||
1015
+ _agentNativeAnalyticsEndpoint ||
1016
+ env.VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT ||
1017
+ (publicKey ? AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT : undefined);
1010
1018
  const endpoint = trackingEndpoint
1011
1019
  ? replayEndpointFromTrackingEndpoint(trackingEndpoint)
1012
1020
  : undefined;
@@ -1034,7 +1042,13 @@ function configuredSessionReplayOptions(
1034
1042
  if (config.enabled === false) return null;
1035
1043
  return withTrackingDefaults(config);
1036
1044
  }
1037
- return sessionReplayEnabledFromEnv() ? withTrackingDefaults({}) : null;
1045
+ const autoEnabledByAnalyticsKey =
1046
+ !!publicKey &&
1047
+ (typeof window === "undefined" ||
1048
+ !isLocalAnalyticsHostname(window.location.hostname));
1049
+ return sessionReplayEnabledFromEnv() || autoEnabledByAnalyticsKey
1050
+ ? withTrackingDefaults({})
1051
+ : null;
1038
1052
  }
1039
1053
 
1040
1054
  function replayExtraPropertiesWithDefaults(
@@ -121,6 +121,7 @@ export function useIsDark(): boolean {
121
121
  */
122
122
  export const KitConfigContext = createContext<{
123
123
  skeleton?: boolean;
124
+ flushFrame?: boolean;
124
125
  sketch?: number;
125
126
  theme?: "light" | "dark";
126
127
  style?: "sketchy" | "clean";
@@ -195,6 +196,7 @@ export function Screen({
195
196
  const isSkeleton = skeleton || Boolean(cfg.skeleton);
196
197
  const wfStyle = cfg.style ?? "sketchy";
197
198
  const wfTheme = theme ?? cfg.theme ?? "light";
199
+ const effectivePad = cfg.flushFrame ? 0 : pad;
198
200
  void sketch;
199
201
  return (
200
202
  <div
@@ -218,7 +220,7 @@ export function Screen({
218
220
  lineHeight: 1.25,
219
221
  display: "flex",
220
222
  flexDirection: "column",
221
- padding: pad,
223
+ padding: effectivePad,
222
224
  boxSizing: "border-box",
223
225
  overflow: "hidden",
224
226
  ...style,
@@ -414,7 +414,12 @@ function KitArtboard({
414
414
  caption={data.caption}
415
415
  render={({ theme, style }) => (
416
416
  <KitConfigContext.Provider
417
- value={{ skeleton: data.skeleton, theme, style }}
417
+ value={{
418
+ skeleton: data.skeleton,
419
+ flushFrame: !showFrame,
420
+ theme,
421
+ style,
422
+ }}
418
423
  >
419
424
  {renderKitScreen(data.screen ?? [])}
420
425
  </KitConfigContext.Provider>
@@ -2427,6 +2427,16 @@ export const config = {
2427
2427
  );
2428
2428
  }
2429
2429
 
2430
+ /**
2431
+ * Nitro's Netlify preset can emit a harmful fallback rewrite to
2432
+ * `/.netlify/functions/server`. With `config.path: "/*"`, that default URL is
2433
+ * removed, so the rewrite publishes platform 404s. Single-template deploys keep
2434
+ * Nitro's `preferStatic: true` so hashed `/assets/*` files in dist win before
2435
+ * the SSR catch-all runs.
2436
+ */
2437
+ const NETLIFY_DEFAULT_FUNCTION_URL_REDIRECT =
2438
+ "/* /.netlify/functions/server 200";
2439
+
2430
2440
  export function assertSingleTemplateNetlifyBuildOutput(
2431
2441
  projectCwd: string,
2432
2442
  ): void {
@@ -2440,21 +2450,31 @@ export function assertSingleTemplateNetlifyBuildOutput(
2440
2450
 
2441
2451
  if (!fs.existsSync(publishDir)) {
2442
2452
  failures.push("missing publish directory: dist");
2443
- } else if (!fs.existsSync(redirectsPath)) {
2444
- failures.push("missing Netlify fallback redirects file: dist/_redirects");
2445
2453
  } else {
2454
+ const assetsDir = path.join(publishDir, "assets");
2455
+ if (
2456
+ !fs.existsSync(assetsDir) ||
2457
+ fs.readdirSync(assetsDir).every((name) => name.startsWith("."))
2458
+ ) {
2459
+ failures.push(
2460
+ "dist/assets is missing hashed client assets — the publish dir would load an infinite spinner",
2461
+ );
2462
+ }
2463
+ }
2464
+
2465
+ if (fs.existsSync(publishDir) && fs.existsSync(redirectsPath)) {
2446
2466
  const redirects = fs.readFileSync(redirectsPath, "utf-8");
2447
2467
  if (
2448
- !redirects
2468
+ redirects
2449
2469
  .split(/\r?\n/)
2450
2470
  .some(
2451
2471
  (line) =>
2452
2472
  line.trim().replace(/\s+/g, " ") ===
2453
- "/* /.netlify/functions/server 200",
2473
+ NETLIFY_DEFAULT_FUNCTION_URL_REDIRECT,
2454
2474
  )
2455
2475
  ) {
2456
2476
  failures.push(
2457
- 'dist/_redirects is missing "/* /.netlify/functions/server 200"',
2477
+ 'dist/_redirects must not contain "/* /.netlify/functions/server 200" — Nitro\'s custom config.path: "/*" removes that default function URL',
2458
2478
  );
2459
2479
  }
2460
2480
  }
@@ -2492,6 +2512,11 @@ export function assertSingleTemplateNetlifyBuildOutput(
2492
2512
  "Netlify server entry does not reference the generated main.mjs bundle",
2493
2513
  );
2494
2514
  }
2515
+ if (!/\bpreferStatic:\s*true\b/.test(serverEntry)) {
2516
+ failures.push(
2517
+ "Netlify server entry must keep preferStatic: true so /assets/* is served from dist before the SSR catch-all",
2518
+ );
2519
+ }
2495
2520
  }
2496
2521
 
2497
2522
  if (isDurableBackgroundDeployEnabled()) {
@@ -2544,37 +2569,49 @@ export function assertSingleTemplateNetlifyBuildOutput(
2544
2569
  );
2545
2570
  }
2546
2571
 
2572
+ /**
2573
+ * Strip the harmful single-template catch-all rewrite that points at
2574
+ * `/.netlify/functions/server`. Nitro declares `config.path: "/*"`, which
2575
+ * removes the default function URL, so rewriting to that URL publishes
2576
+ * Netlify platform 404s. Preserve any real redirects from `public/_redirects`.
2577
+ */
2547
2578
  export function writeSingleTemplateNetlifyRedirects(projectCwd: string): void {
2548
2579
  const publishDir = path.join(projectCwd, "dist");
2549
- if (!fs.existsSync(publishDir)) return;
2550
-
2551
2580
  const redirectsPath = path.join(publishDir, "_redirects");
2552
- const existing = fs.existsSync(redirectsPath)
2553
- ? fs.readFileSync(redirectsPath, "utf-8").trimEnd()
2554
- : "";
2555
- const lines = existing ? existing.split(/\r?\n/) : [];
2556
-
2557
- function hasRedirect(line: string): boolean {
2558
- return lines.some(
2559
- (existingLine) => existingLine.trim().replace(/\s+/g, " ") === line,
2560
- );
2561
- }
2581
+ if (!fs.existsSync(redirectsPath)) return;
2562
2582
 
2563
- const fallbackRules = [
2564
- "# Generated by agent-native build for Netlify single-template deploys",
2565
- "# Static files are served first; dynamic routes fall through to the server function.",
2566
- "/* /.netlify/functions/server 200",
2567
- ];
2583
+ const existing = fs.readFileSync(redirectsPath, "utf-8");
2584
+ const kept: string[] = [];
2585
+ let removed = 0;
2568
2586
 
2569
- for (const line of fallbackRules) {
2570
- if (line.startsWith("#") || !hasRedirect(line)) {
2571
- lines.push(line);
2587
+ for (const line of existing.split(/\r?\n/)) {
2588
+ const normalized = line.trim().replace(/\s+/g, " ");
2589
+ if (
2590
+ normalized === NETLIFY_DEFAULT_FUNCTION_URL_REDIRECT ||
2591
+ normalized ===
2592
+ "# Generated by agent-native build for Netlify single-template deploys" ||
2593
+ normalized ===
2594
+ "# Static files are served first; dynamic routes fall through to the server function."
2595
+ ) {
2596
+ removed += 1;
2597
+ continue;
2572
2598
  }
2599
+ kept.push(line);
2600
+ }
2601
+
2602
+ while (kept.length > 0 && kept[kept.length - 1].trim() === "") {
2603
+ kept.pop();
2573
2604
  }
2574
2605
 
2575
- fs.writeFileSync(redirectsPath, lines.join("\n") + "\n");
2606
+ if (removed === 0) return;
2607
+
2608
+ if (kept.every((line) => line.trim() === "")) {
2609
+ fs.rmSync(redirectsPath, { force: true });
2610
+ } else {
2611
+ fs.writeFileSync(redirectsPath, kept.join("\n").trimEnd() + "\n");
2612
+ }
2576
2613
  console.log(
2577
- "[deploy] Wrote Netlify fallback redirect /* -> /.netlify/functions/server.",
2614
+ '[deploy] Removed Netlify fallback rewrite to /.netlify/functions/server (incompatible with Nitro config.path: "/*").',
2578
2615
  );
2579
2616
  }
2580
2617
 
@@ -13,14 +13,6 @@ const LARGE_FILE_THRESHOLD_BYTES = 30 * 1024 * 1024;
13
13
  const UPLOAD_TIMEOUT_MS = 120_000;
14
14
  const SMALL_FILE_RETRY_DELAYS_MS = [600, 1800];
15
15
 
16
- function enabledFlag(value: unknown): boolean {
17
- return /^(true|1|yes|on)$/i.test(String(value || "").trim());
18
- }
19
-
20
- function stableUrlOptInEnabled(): boolean {
21
- return enabledFlag(process.env.CLIPS_STABLE_URL_OPTIN);
22
- }
23
-
24
16
  function builderUploadHost(): string {
25
17
  return (
26
18
  process.env.BUILDER_APP_HOST ||
@@ -52,12 +44,9 @@ function fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {
52
44
  );
53
45
  }
54
46
 
55
- function setSkipCompressionQueryParams(url: URL): void {
56
- url.searchParams.set("skipCompressionWait", "true");
57
- url.searchParams.set("skipCompression", "true");
58
- if (stableUrlOptInEnabled()) {
59
- url.searchParams.set("stableUrl", "true");
60
- }
47
+ function setStableUrlQueryParam(url: URL): void {
48
+ // Stable URLs let Builder compress asynchronously without changing the media URL.
49
+ url.searchParams.set("stableUrl", "true");
61
50
  }
62
51
 
63
52
  function setRecordAssetQueryParam(
@@ -121,7 +110,7 @@ async function uploadLargeFileViaSignedUrl(
121
110
  assetId,
122
111
  input.filename,
123
112
  {
124
- skipCompressionWait: input.skipCompressionWait,
113
+ stableUrl: input.stableUrl,
125
114
  recordAsset: input.recordAsset,
126
115
  },
127
116
  );
@@ -177,12 +166,12 @@ async function completeBuilderUpload(
177
166
  privateKey: string,
178
167
  assetId: string,
179
168
  filename: string | undefined,
180
- options?: { skipCompressionWait?: boolean; recordAsset?: boolean },
169
+ options?: { stableUrl?: boolean; recordAsset?: boolean },
181
170
  ): Promise<{ url: string; id?: string }> {
182
171
  const host = builderUploadHost();
183
172
  const url = new URL("/api/v1/upload/complete", host);
184
- if (options?.skipCompressionWait) {
185
- setSkipCompressionQueryParams(url);
173
+ if (options?.stableUrl) {
174
+ setStableUrlQueryParam(url);
186
175
  }
187
176
  setRecordAssetQueryParam(url, options?.recordAsset);
188
177
  const res = await fetchWithTimeout(url.toString(), {
@@ -287,8 +276,8 @@ export const builderFileUploadProvider: FileUploadProvider = {
287
276
 
288
277
  const url = new URL("/api/v1/upload", builderUploadHost());
289
278
  if (filename) url.searchParams.set("name", filename);
290
- if (input.skipCompressionWait) {
291
- setSkipCompressionQueryParams(url);
279
+ if (input.stableUrl) {
280
+ setStableUrlQueryParam(url);
292
281
  }
293
282
  setRecordAssetQueryParam(url, input.recordAsset);
294
283
 
@@ -429,9 +418,7 @@ export const builderFileUploadProvider: FileUploadProvider = {
429
418
  assetId,
430
419
  filename,
431
420
  {
432
- skipCompressionWait:
433
- options?.skipCompressionWait ||
434
- session.meta.skipCompressionWait === true,
421
+ stableUrl: options?.stableUrl || session.meta.stableUrl === true,
435
422
  recordAsset:
436
423
  options?.recordAsset ??
437
424
  (session.meta.recordAsset === false ? false : undefined),