@agent-native/core 0.90.9 → 0.90.11

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 (132) hide show
  1. package/README.md +7 -28
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +18 -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/durable-background.ts +5 -5
  28. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +40 -8
  29. package/corpus/core/src/agent/engine/anthropic-engine.ts +25 -7
  30. package/corpus/core/src/agent/engine/output-tokens.ts +94 -0
  31. package/corpus/core/src/agent/production-agent.ts +46 -7
  32. package/corpus/core/src/agent/types.ts +3 -2
  33. package/corpus/core/src/client/analytics.ts +17 -3
  34. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +3 -1
  35. package/corpus/core/src/client/blocks/library/wireframe.tsx +6 -1
  36. package/corpus/core/src/db/client.ts +12 -9
  37. package/corpus/core/src/file-upload/builder.ts +10 -23
  38. package/corpus/core/src/file-upload/types.ts +3 -3
  39. package/corpus/core/src/shared/reasoning-effort.ts +23 -0
  40. package/corpus/core/src/styles/blocks.css +7 -0
  41. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +9 -4
  42. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +21 -7
  43. package/corpus/templates/analytics/app/entry.client.tsx +6 -1
  44. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -1
  45. package/corpus/templates/analytics/changelog/2026-07-07-analytics-now-recovers-from-stale-route-chunks-after-deploys.md +6 -0
  46. package/corpus/templates/analytics/changelog/2026-07-07-metric-dashboard-cards-now-show-warehouse-backed-numeric-val.md +6 -0
  47. package/corpus/templates/clips/actions/create-recording.ts +1 -1
  48. package/corpus/templates/clips/actions/finalize-recording.ts +2 -2
  49. package/corpus/templates/clips/actions/import-loom-recording.ts +1 -1
  50. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +1 -1
  51. package/corpus/templates/clips/actions/regenerate-chapters.ts +7 -0
  52. package/corpus/templates/clips/app/hooks/use-auto-title.ts +15 -5
  53. package/corpus/templates/clips/app/routes/download.tsx +12 -4
  54. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -0
  55. package/corpus/templates/clips/changelog/2026-07-07-auto-generated-chapter-requests-now-open-the-matching-agent-.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-07-clip-video-uploads-now-compress-in-the-background-while-keeping.md +6 -0
  57. package/corpus/templates/clips/changelog/2026-07-07-desktop-settings-and-fn-dictation-are-more-reliable.md +6 -0
  58. package/corpus/templates/clips/changelog/2026-07-07-desktop-settings-now-includes-a-manual-update-check-with-res.md +6 -0
  59. package/corpus/templates/clips/changelog/2026-07-07-the-clips-desktop-download-page-now-uses-the-agent-native-ap.md +6 -0
  60. package/corpus/templates/clips/desktop/src/app.tsx +95 -8
  61. package/corpus/templates/clips/desktop/src/lib/updater.ts +60 -43
  62. package/corpus/templates/clips/desktop/src/styles.css +5 -0
  63. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +93 -23
  64. package/corpus/templates/dispatch/changelog/2026-07-07-overview-is-now-just-ask-dispatch-and-a-list-of-apps.md +6 -0
  65. package/dist/agent/durable-background.d.ts +1 -1
  66. package/dist/agent/durable-background.d.ts.map +1 -1
  67. package/dist/agent/durable-background.js +5 -3
  68. package/dist/agent/durable-background.js.map +1 -1
  69. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  70. package/dist/agent/engine/ai-sdk-engine.js +24 -4
  71. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  72. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  73. package/dist/agent/engine/anthropic-engine.js +13 -3
  74. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  75. package/dist/agent/engine/output-tokens.d.ts +27 -0
  76. package/dist/agent/engine/output-tokens.d.ts.map +1 -1
  77. package/dist/agent/engine/output-tokens.js +70 -0
  78. package/dist/agent/engine/output-tokens.js.map +1 -1
  79. package/dist/agent/production-agent.d.ts.map +1 -1
  80. package/dist/agent/production-agent.js +41 -7
  81. package/dist/agent/production-agent.js.map +1 -1
  82. package/dist/agent/types.d.ts +3 -2
  83. package/dist/agent/types.d.ts.map +1 -1
  84. package/dist/agent/types.js.map +1 -1
  85. package/dist/client/analytics.d.ts.map +1 -1
  86. package/dist/client/analytics.js +14 -3
  87. package/dist/client/analytics.js.map +1 -1
  88. package/dist/client/blocks/library/wireframe-kit.d.ts +1 -0
  89. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  90. package/dist/client/blocks/library/wireframe-kit.js +2 -1
  91. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  92. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  93. package/dist/client/blocks/library/wireframe.js +6 -1
  94. package/dist/client/blocks/library/wireframe.js.map +1 -1
  95. package/dist/db/client.d.ts.map +1 -1
  96. package/dist/db/client.js +12 -7
  97. package/dist/db/client.js.map +1 -1
  98. package/dist/file-upload/builder.d.ts.map +1 -1
  99. package/dist/file-upload/builder.js +9 -19
  100. package/dist/file-upload/builder.js.map +1 -1
  101. package/dist/file-upload/types.d.ts +3 -3
  102. package/dist/file-upload/types.d.ts.map +1 -1
  103. package/dist/file-upload/types.js.map +1 -1
  104. package/dist/secrets/routes.d.ts +6 -6
  105. package/dist/shared/reasoning-effort.d.ts +1 -0
  106. package/dist/shared/reasoning-effort.d.ts.map +1 -1
  107. package/dist/shared/reasoning-effort.js +18 -0
  108. package/dist/shared/reasoning-effort.js.map +1 -1
  109. package/dist/styles/blocks.css +7 -0
  110. package/docs/content/agent-native-toolkit.mdx +1 -1
  111. package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +1 -1
  112. package/docs/content/locales/ar-SA/tracking.mdx +3 -3
  113. package/docs/content/locales/de-DE/agent-native-toolkit.mdx +1 -1
  114. package/docs/content/locales/de-DE/tracking.mdx +3 -3
  115. package/docs/content/locales/es-ES/agent-native-toolkit.mdx +1 -1
  116. package/docs/content/locales/es-ES/tracking.mdx +3 -3
  117. package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +1 -1
  118. package/docs/content/locales/fr-FR/tracking.mdx +3 -3
  119. package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +1 -1
  120. package/docs/content/locales/hi-IN/tracking.mdx +3 -3
  121. package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +1 -1
  122. package/docs/content/locales/ja-JP/tracking.mdx +3 -3
  123. package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +1 -1
  124. package/docs/content/locales/ko-KR/tracking.mdx +3 -3
  125. package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +1 -1
  126. package/docs/content/locales/pt-BR/tracking.mdx +3 -3
  127. package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +1 -1
  128. package/docs/content/locales/zh-CN/tracking.mdx +3 -3
  129. package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +1 -1
  130. package/docs/content/locales/zh-TW/tracking.mdx +3 -3
  131. package/docs/content/tracking.mdx +3 -3
  132. package/package.json +1 -1
@@ -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>
@@ -856,15 +856,18 @@ export function isBackgroundFunctionPoolContext(): boolean {
856
856
  ) {
857
857
  return true;
858
858
  }
859
- // Set by the HMAC-authenticated agent-chat `_process-run` route before it
860
- // re-enters the normal chat handler. This mirrors the marker-only runtime
861
- // proof used by durable-background.ts without importing agent code into db/.
862
- if (
863
- (globalThis as Record<string, unknown>)
864
- .__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__ === true
865
- ) {
866
- return true;
867
- }
859
+ // NOTE: we deliberately do NOT trust `__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__`
860
+ // here. That flag is set from the dispatch MARKER (which URL the foreground
861
+ // targeted), not from proof the request actually LANDED on a background
862
+ // function. A worker dispatched toward `-background` but routed onto the ~60s
863
+ // synchronous function would otherwise take the 8-connection background pool
864
+ // while running as one of MANY warm sync-function instances — multiplying
865
+ // Neon connections and exhausting the pooled endpoint ("connection
866
+ // terminated" / statement timeouts / failed heartbeat writes → stale runs).
867
+ // The genuine `-background` function sets `__AGENT_NATIVE_BACKGROUND_RUNTIME__`
868
+ // as its first cold-start statement, so a real background worker still gets
869
+ // the larger pool via the check above. Mirrors the same proof-of-landing
870
+ // tightening applied to `shouldUseBackgroundFunctionTimeoutForWorker`.
868
871
  const lambdaName = process.env.AWS_LAMBDA_FUNCTION_NAME;
869
872
  if (
870
873
  typeof lambdaName === "string" &&
@@ -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),
@@ -16,8 +16,8 @@ export interface FileUploadInput {
16
16
  mimeType?: string;
17
17
  /** Optional owner email for per-user scoping in fallback storage. */
18
18
  ownerEmail?: string;
19
- /** Builder.io upload hint: return after asset registration instead of waiting for server-side compression. */
20
- skipCompressionWait?: boolean;
19
+ /** Builder.io upload hint: return one stable URL while compression finishes asynchronously. */
20
+ stableUrl?: boolean;
21
21
  /** Builder.io upload hint: set false for internal artifacts that should not appear in the asset library. */
22
22
  recordAsset?: boolean;
23
23
  }
@@ -81,7 +81,7 @@ export interface FileUploadProvider {
81
81
  completeSession(
82
82
  session: ResumableUploadSession,
83
83
  filename: string,
84
- options?: { skipCompressionWait?: boolean; recordAsset?: boolean },
84
+ options?: { stableUrl?: boolean; recordAsset?: boolean },
85
85
  ): Promise<string>;
86
86
  };
87
87
  }
@@ -98,6 +98,29 @@ export function reasoningEffortLabel(effort: ReasoningEffort | undefined) {
98
98
  return REASONING_EFFORT_LABELS[effort ?? "auto"];
99
99
  }
100
100
 
101
+ /**
102
+ * One tier down from each effort, stopping at "minimal" — "none"/"auto"
103
+ * (not really "tiers") and "minimal" itself are left unchanged. Used by the
104
+ * empty-final-response retry so a retried turn asks for meaningfully less
105
+ * reasoning instead of repeating the exact request that came back empty.
106
+ */
107
+ const REASONING_EFFORT_STEP_DOWN: Partial<
108
+ Record<ReasoningEffort, ReasoningEffort>
109
+ > = {
110
+ max: "xhigh",
111
+ xhigh: "high",
112
+ high: "medium",
113
+ medium: "low",
114
+ low: "minimal",
115
+ };
116
+
117
+ export function stepDownReasoningEffort(
118
+ effort: ReasoningEffort | undefined,
119
+ ): ReasoningEffort | undefined {
120
+ if (!effort) return effort;
121
+ return REASONING_EFFORT_STEP_DOWN[effort] ?? effort;
122
+ }
123
+
101
124
  function isGPTReasoningModel(model: string) {
102
125
  return /^gpt-5/.test(model) || /^o\d/.test(model);
103
126
  }
@@ -1007,6 +1007,13 @@
1007
1007
  min-height: 100%;
1008
1008
  }
1009
1009
 
1010
+ .plan-html-frame[data-frame="hide"]:not([data-render-mode="design"])
1011
+ > .plan-html-frame-content
1012
+ > :first-child {
1013
+ margin: 0 !important;
1014
+ padding: 0 !important;
1015
+ }
1016
+
1010
1017
  .plan-html-frame[data-prototype-live="true"] {
1011
1018
  overflow: auto;
1012
1019
  }
@@ -106,12 +106,17 @@ agent answers about browser recordings in the Analytics template.
106
106
 
107
107
  ## Capture Defaults
108
108
 
109
- - Replay is off unless enabled by config/env.
110
- - Consumers can enable replay directly with
109
+ - Replay is on by default for signed-in hosted users when
110
+ `VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY` or `configureTracking({ key })` is
111
+ present. The default sample rate is 100% of eligible sessions.
112
+ - Replay remains off when no first-party analytics key is configured, and it is
113
+ not auto-enabled on localhost/local dev. Consumers can still enable replay
114
+ directly with
111
115
  `configureTracking({ key, endpoint, sessionReplay: { enabled: true } })`.
116
+ - Apps can opt out with `configureTracking({ sessionReplay: false })`.
112
117
  - Agent Native templates already call `configureTracking()` in their roots;
113
- hosted template deployments can usually enable replay with Vite/Netlify env
114
- vars on the recorded site.
118
+ hosted template deployments only need the normal Agent Native Analytics
119
+ Vite/Netlify env vars on the recorded site.
115
120
  - Inputs are masked by default. Page text is visible unless marked with
116
121
  `.an-mask` or `data-an-mask`.
117
122
  - Use `.an-block`, `.an-ignore`, `data-an-block`, or `data-an-ignore` for
@@ -155,6 +155,24 @@ export function formatMetricValue(
155
155
  : String(raw ?? "-");
156
156
  }
157
157
 
158
+ function isNumericLikeValue(value: unknown): boolean {
159
+ if (typeof value === "number") return Number.isFinite(value);
160
+ return (
161
+ typeof value === "string" &&
162
+ value.trim() !== "" &&
163
+ Number.isFinite(Number(value))
164
+ );
165
+ }
166
+
167
+ export function detectMetricValueColumn(
168
+ row: Record<string, unknown>,
169
+ configuredKey?: string,
170
+ ): string {
171
+ const cols = Object.keys(row);
172
+ if (configuredKey && cols.includes(configuredKey)) return configuredKey;
173
+ return cols.find((key) => isNumericLikeValue(row[key])) || cols[0] || "";
174
+ }
175
+
158
176
  function parsePrometheusSeriesLabel(label: string): {
159
177
  metric: string;
160
178
  labels: Record<string, string>;
@@ -804,7 +822,7 @@ function detectKeys(
804
822
  if (yKeys.length === 0) {
805
823
  for (const c of cols) {
806
824
  if (c === xKey) continue;
807
- if (typeof sample[c] === "number") yKeys.push(c);
825
+ if (isNumericLikeValue(sample[c])) yKeys.push(c);
808
826
  }
809
827
  }
810
828
  if (yKeys.length === 0 && cols.length > 1) {
@@ -1120,14 +1138,10 @@ function MetricRenderer({
1120
1138
  panel: SqlPanel;
1121
1139
  }) {
1122
1140
  const row = rows[0];
1123
- const cols = Object.keys(row);
1124
- const valueCol =
1125
- panel.config?.yKey ||
1126
- cols.find((c) => typeof row[c] === "number") ||
1127
- cols[0];
1141
+ const valueCol = detectMetricValueColumn(row, panel.config?.yKey);
1128
1142
 
1129
1143
  let raw: unknown;
1130
- if (rows.length > 1 && typeof row[valueCol] === "number") {
1144
+ if (rows.length > 1 && isNumericLikeValue(row[valueCol])) {
1131
1145
  raw = rows.reduce((sum, r) => sum + (Number(r[valueCol]) || 0), 0);
1132
1146
  } else {
1133
1147
  raw = row[valueCol];
@@ -1,7 +1,12 @@
1
- import { appBasePath } from "@agent-native/core/client/api-path";
1
+ import {
2
+ appBasePath,
3
+ installRouteChunkRecovery,
4
+ } from "@agent-native/core/client";
2
5
  import { hydrateRoot } from "react-dom/client";
3
6
  import { HydratedRouter } from "react-router/dom";
4
7
 
8
+ installRouteChunkRecovery();
9
+
5
10
  const basePath = appBasePath();
6
11
  const pathname = window.location.pathname;
7
12
  const routerBasePath =
@@ -546,7 +546,7 @@ configureTracking({
546
546
  sessionReplay: {
547
547
  enabled: true,
548
548
  requireSignedInUser: true,
549
- sampleRate: 0.1,
549
+ sampleRate: 1,
550
550
  },
551
551
  getDefaultProps: (_event, props) => ({
552
552
  ...props,
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-07
4
+ ---
5
+
6
+ Analytics now recovers from stale route chunks after deploys instead of leaving dashboards stuck on failed module loads.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-07
4
+ ---
5
+
6
+ Metric dashboard cards now show warehouse-backed numeric values correctly when query results return numbers as strings.
@@ -113,7 +113,7 @@ export default defineAction({
113
113
  sessionId: session.sessionId,
114
114
  meta: {
115
115
  ...session.meta,
116
- skipCompressionWait: true,
116
+ stableUrl: true,
117
117
  recordAsset: false,
118
118
  },
119
119
  bytesUploaded: 0,
@@ -596,7 +596,7 @@ export default defineAction({
596
596
  typeof resumableSession.meta.filename === "string"
597
597
  ? resumableSession.meta.filename
598
598
  : "",
599
- { skipCompressionWait: true, recordAsset: false },
599
+ { stableUrl: true, recordAsset: false },
600
600
  );
601
601
  debugLog("[finalize] resumable upload completed", { id, videoUrl });
602
602
  try {
@@ -966,7 +966,7 @@ export default defineAction({
966
966
  filename: `${id}.${videoFormat}`,
967
967
  mimeType,
968
968
  ownerEmail,
969
- skipCompressionWait: true,
969
+ stableUrl: true,
970
970
  recordAsset: false,
971
971
  });
972
972
  } catch (err) {
@@ -296,7 +296,7 @@ export default defineAction({
296
296
  filename: `${id}.mp4`,
297
297
  mimeType: media.mimeType,
298
298
  ownerEmail,
299
- skipCompressionWait: true,
299
+ stableUrl: true,
300
300
  recordAsset: false,
301
301
  });
302
302
 
@@ -218,7 +218,7 @@ export async function ensureRecordingSeekable(params: {
218
218
  filename: `${recordingId}.${videoFormat}`,
219
219
  mimeType,
220
220
  ownerEmail,
221
- skipCompressionWait: true,
221
+ stableUrl: true,
222
222
  recordAsset: false,
223
223
  });
224
224
 
@@ -23,6 +23,12 @@ export default defineAction({
23
23
  "Ask the agent to generate chapters for this recording based on its transcript (and the full video when Include full video is enabled). The agent identifies topic transitions and calls set-chapters.",
24
24
  schema: z.object({
25
25
  recordingId: z.string().describe("Recording ID"),
26
+ openInChat: z
27
+ .boolean()
28
+ .optional()
29
+ .describe(
30
+ "When true, focus the queued generation request in the agent chat instead of keeping it hidden.",
31
+ ),
26
32
  }),
27
33
  run: async (args) => {
28
34
  await assertAccess("recording", args.recordingId, "editor");
@@ -57,6 +63,7 @@ export default defineAction({
57
63
  segmentsJson: transcript?.segmentsJson ?? "[]",
58
64
  transcriptText: transcript?.fullText ?? "",
59
65
  includeFullVideoInAi,
66
+ openInChat: args.openInChat === true,
60
67
  message: withFullVideoAiInstructions(
61
68
  baseMessage,
62
69
  args.recordingId,
@@ -15,6 +15,7 @@ import {
15
15
  agentNativePath,
16
16
  callAction,
17
17
  sendToAgentChat,
18
+ type AgentChatMessage,
18
19
  } from "@agent-native/core/client";
19
20
  import { fullVideoAiModelSelection } from "@shared/clips-ai-prefs";
20
21
  import { useEffect, useRef } from "react";
@@ -55,6 +56,7 @@ interface AiRequest {
55
56
  thresholdMs?: number;
56
57
  message?: string;
57
58
  includeFullVideoInAi?: boolean;
59
+ openInChat?: boolean;
58
60
  }
59
61
 
60
62
  const DISPATCHABLE_REQUESTS = new Set([
@@ -201,25 +203,33 @@ function buildRequestContext(rec: RecordingSummary, request: AiRequest) {
201
203
  };
202
204
  }
203
205
 
204
- function dispatchAiRequest(rec: RecordingSummary, request: AiRequest) {
206
+ export function buildAiRequestChatOptions(
207
+ rec: RecordingSummary,
208
+ request: AiRequest,
209
+ ): AgentChatMessage {
205
210
  const includeFullVideo = request.includeFullVideoInAi === true;
206
211
  const gemini = includeFullVideo ? fullVideoAiModelSelection() : null;
207
- sendToAgentChat({
212
+ const openInChat = request.openInChat === true;
213
+ return {
208
214
  message:
209
215
  request.message ??
210
216
  `Handle queued ${request.kind} work for recording ${rec.id}.`,
211
217
  context: JSON.stringify(buildRequestContext(rec, request)),
212
218
  submit: true,
213
- openSidebar: false,
219
+ openSidebar: openInChat ? true : false,
214
220
  newTab: true,
215
- background: true,
221
+ background: !openInChat,
216
222
  ...(gemini
217
223
  ? {
218
224
  engine: gemini.engine,
219
225
  model: gemini.model,
220
226
  }
221
227
  : {}),
222
- });
228
+ };
229
+ }
230
+
231
+ function dispatchAiRequest(rec: RecordingSummary, request: AiRequest) {
232
+ sendToAgentChat(buildAiRequestChatOptions(rec, request));
223
233
  }
224
234
 
225
235
  function parseJsonArray(raw: string | undefined): unknown[] {
@@ -4,7 +4,6 @@ import {
4
4
  IconBrandApple,
5
5
  IconBrandWindows,
6
6
  IconExternalLink,
7
- IconPlayerRecord,
8
7
  } from "@tabler/icons-react";
9
8
  import { useEffect, useState } from "react";
10
9
 
@@ -201,9 +200,18 @@ export default function DownloadPage() {
201
200
  href={appPath("/")}
202
201
  className="flex items-center gap-2 font-semibold"
203
202
  >
204
- <span className="grid h-7 w-7 place-items-center rounded-md bg-primary text-primary-foreground">
205
- <IconPlayerRecord className="h-4 w-4" />
206
- </span>
203
+ <img
204
+ src={appPath("/agent-native-icon-light.svg")}
205
+ alt=""
206
+ aria-hidden="true"
207
+ className="block h-4 w-auto shrink-0 dark:hidden"
208
+ />
209
+ <img
210
+ src={appPath("/agent-native-icon-dark.svg")}
211
+ alt=""
212
+ aria-hidden="true"
213
+ className="hidden h-4 w-auto shrink-0 dark:block"
214
+ />
207
215
  <span>Clips</span>
208
216
  </a>
209
217
  <a
@@ -1096,6 +1096,7 @@ export default function RecordingPage() {
1096
1096
  onSelect={() =>
1097
1097
  regenerateChapters.mutate({
1098
1098
  recordingId: recording.id,
1099
+ openInChat: true,
1099
1100
  } as any)
1100
1101
  }
1101
1102
  >
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-07
4
+ ---
5
+
6
+ Auto-generated chapter requests now open the matching agent chat tab.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-07
4
+ ---
5
+
6
+ Clip video uploads now compress in the background while keeping a single stable playback URL.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-07
4
+ ---
5
+
6
+ Desktop settings stay out of the server URL field on open, and Fn dictation recovers when macOS disables its Input Monitoring tap.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-07
4
+ ---
5
+
6
+ Desktop settings now include a manual update check with restart status.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-07
4
+ ---
5
+
6
+ The Clips Desktop download page now uses the Agent Native app icon in its header.