@agent-native/core 0.77.4 → 0.77.7

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 (192) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +31 -0
  3. package/corpus/core/package.json +3 -3
  4. package/corpus/core/src/agent/production-agent.ts +35 -0
  5. package/corpus/core/src/agent/run-store.ts +2 -0
  6. package/corpus/core/src/agent/thread-data-builder.ts +21 -2
  7. package/corpus/core/src/cli/create.ts +35 -1
  8. package/corpus/core/src/cli/skills.ts +8 -0
  9. package/corpus/core/src/client/AgentPanel.tsx +57 -25
  10. package/corpus/core/src/client/MultiTabAssistantChat.tsx +31 -1
  11. package/corpus/core/src/client/analytics.ts +24 -2
  12. package/corpus/core/src/client/changelog/Changelog.tsx +22 -10
  13. package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +405 -35
  14. package/corpus/core/src/client/use-agent-engine-configured.ts +29 -15
  15. package/corpus/core/src/client/use-chat-threads.ts +104 -55
  16. package/corpus/core/src/server/analytics.ts +27 -3
  17. package/corpus/core/src/sharing/access.ts +35 -13
  18. package/corpus/core/src/sharing/actions/share-resource.ts +32 -9
  19. package/corpus/core/src/sharing/actions/unshare-resource.ts +25 -2
  20. package/corpus/core/src/sharing/registry.ts +9 -0
  21. package/corpus/core/src/vite/client.ts +3 -0
  22. package/corpus/templates/analytics/actions/data-source-status.ts +25 -14
  23. package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +2 -2
  24. package/corpus/templates/analytics/app/lib/data-source-status.ts +47 -6
  25. package/corpus/templates/analytics/app/lib/data-sources.ts +14 -3
  26. package/corpus/templates/analytics/app/pages/DataSources.tsx +24 -5
  27. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +15 -43
  28. package/corpus/templates/analytics/changelog/2026-06-25-fixed-hubspot-data-source-status-so-private-app-tokens-legac.md +6 -0
  29. package/corpus/templates/analytics/netlify.toml +1 -0
  30. package/corpus/templates/analytics/server/lib/credential-keys.ts +3 -1
  31. package/corpus/templates/analytics/server/routes/api/test-connection.post.ts +12 -3
  32. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +5 -2
  33. package/corpus/templates/assets/changelog/2026-06-25-create-now-returns-from-agent-settings-to-the-asset-chat-eve.md +6 -0
  34. package/corpus/templates/assets/netlify.toml +1 -0
  35. package/corpus/templates/brain/netlify.toml +1 -0
  36. package/corpus/templates/calendar/app/i18n-data.ts +12 -0
  37. package/corpus/templates/calendar/app/lib/clipboard.ts +58 -0
  38. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +15 -9
  39. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +6 -2
  40. package/corpus/templates/calendar/changelog/2026-06-25-booking-link-copy-buttons-no-longer-fail-when-clipboard-perm.md +6 -0
  41. package/corpus/templates/calendar/netlify.toml +1 -0
  42. package/corpus/templates/chat/netlify.toml +1 -0
  43. package/corpus/templates/clips/AGENTS.md +9 -7
  44. package/corpus/templates/clips/actions/add-vocabulary-term.ts +2 -2
  45. package/corpus/templates/clips/actions/archive-recording.ts +5 -2
  46. package/corpus/templates/clips/actions/clear-edits.ts +5 -2
  47. package/corpus/templates/clips/actions/create-folder.ts +7 -2
  48. package/corpus/templates/clips/actions/delete-folder.ts +7 -4
  49. package/corpus/templates/clips/actions/delete-recording-permanent.ts +5 -2
  50. package/corpus/templates/clips/actions/finalize-recording.ts +5 -2
  51. package/corpus/templates/clips/actions/import-loom-recording.ts +2 -1
  52. package/corpus/templates/clips/actions/list-notifications.ts +9 -5
  53. package/corpus/templates/clips/actions/list-organization-state.ts +2 -1
  54. package/corpus/templates/clips/actions/list-recordings.ts +4 -1
  55. package/corpus/templates/clips/actions/move-recording.ts +5 -1
  56. package/corpus/templates/clips/actions/rename-folder.ts +6 -3
  57. package/corpus/templates/clips/actions/request-transcript.ts +5 -2
  58. package/corpus/templates/clips/actions/restore-recording.ts +5 -2
  59. package/corpus/templates/clips/actions/save-browser-diagnostics.ts +21 -8
  60. package/corpus/templates/clips/actions/set-chapters.ts +5 -2
  61. package/corpus/templates/clips/actions/set-thumbnail.ts +5 -2
  62. package/corpus/templates/clips/actions/split-recording.ts +5 -2
  63. package/corpus/templates/clips/actions/stitch-recordings.ts +6 -2
  64. package/corpus/templates/clips/actions/trash-recording.ts +5 -2
  65. package/corpus/templates/clips/actions/undo-edit.ts +5 -2
  66. package/corpus/templates/clips/actions/view-screen.ts +2 -1
  67. package/corpus/templates/clips/app/components/library/recording-card.tsx +24 -7
  68. package/corpus/templates/clips/app/i18n/ar-SA.ts +14 -0
  69. package/corpus/templates/clips/app/i18n/de-DE.ts +14 -0
  70. package/corpus/templates/clips/app/i18n/en-US.ts +14 -0
  71. package/corpus/templates/clips/app/i18n/es-ES.ts +14 -0
  72. package/corpus/templates/clips/app/i18n/fr-FR.ts +14 -0
  73. package/corpus/templates/clips/app/i18n/hi-IN.ts +14 -0
  74. package/corpus/templates/clips/app/i18n/ja-JP.ts +14 -0
  75. package/corpus/templates/clips/app/i18n/ko-KR.ts +14 -0
  76. package/corpus/templates/clips/app/i18n/pt-BR.ts +14 -0
  77. package/corpus/templates/clips/app/i18n/zh-CN.ts +14 -0
  78. package/corpus/templates/clips/app/lib/browser-diagnostics-capture.ts +5 -1
  79. package/corpus/templates/clips/app/root.tsx +2 -2
  80. package/corpus/templates/clips/app/routes/_app.extensions.$id.tsx +4 -1
  81. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +4 -1
  82. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +12 -5
  83. package/corpus/templates/clips/changelog/2026-06-25-chrome-extension-recordings-now-upload-large-capture-bursts.md +6 -0
  84. package/corpus/templates/clips/changelog/2026-06-25-console-diagnostics-keep-more-useful-non-secret-details-when.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-06-25-fixed-owner-recognition-so-clips-created-from-desktop-and-we.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-06-25-localized-clips-settings-changelog-and-extension-sidebar-lab.md +6 -0
  87. package/corpus/templates/clips/chrome-extension/package.json +3 -0
  88. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  89. package/corpus/templates/clips/chrome-extension/src/background.ts +85 -9
  90. package/corpus/templates/clips/chrome-extension/src/content-script.ts +55 -0
  91. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +79 -3
  92. package/corpus/templates/clips/chrome-extension/src/options.ts +10 -0
  93. package/corpus/templates/clips/chrome-extension/src/overlay.ts +6 -0
  94. package/corpus/templates/clips/chrome-extension/src/permission.ts +8 -1
  95. package/corpus/templates/clips/chrome-extension/src/popup.ts +65 -39
  96. package/corpus/templates/clips/chrome-extension/src/sentry.ts +206 -0
  97. package/corpus/templates/clips/chrome-extension/vite.config.ts +47 -0
  98. package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +4 -2
  99. package/corpus/templates/clips/desktop/src/styles.css +15 -1
  100. package/corpus/templates/clips/netlify.toml +1 -0
  101. package/corpus/templates/clips/server/db/index.ts +1 -0
  102. package/corpus/templates/clips/server/lib/google-calendar-oauth.ts +35 -23
  103. package/corpus/templates/clips/server/lib/public-agent-context.ts +8 -1
  104. package/corpus/templates/clips/server/lib/recordings.ts +18 -2
  105. package/corpus/templates/clips/server/lib/slack-oauth.ts +9 -6
  106. package/corpus/templates/clips/server/plugins/onboarding-calendar.ts +6 -2
  107. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +2 -2
  108. package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +5 -2
  109. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -2
  110. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +5 -2
  111. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +5 -2
  112. package/corpus/templates/clips/shared/browser-diagnostics.ts +14 -4
  113. package/corpus/templates/content/netlify.toml +1 -0
  114. package/corpus/templates/design/netlify.toml +1 -0
  115. package/corpus/templates/dispatch/netlify.toml +1 -0
  116. package/corpus/templates/forms/netlify.toml +1 -0
  117. package/corpus/templates/macros/netlify.toml +1 -0
  118. package/corpus/templates/mail/app/components/email/EmailList.tsx +7 -3
  119. package/corpus/templates/mail/app/components/email/EmailThread.tsx +4 -1
  120. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +10 -2
  121. package/corpus/templates/mail/app/hooks/use-compose-state.ts +15 -5
  122. package/corpus/templates/mail/app/hooks/use-google-auth.ts +47 -9
  123. package/corpus/templates/mail/changelog/2026-06-25-account-avatars-stay-stable-while-mail-refreshes-google-acco.md +6 -0
  124. package/corpus/templates/mail/changelog/2026-06-25-mail-background-draft-saves-and-thread-prefetches-no-longer.md +6 -0
  125. package/corpus/templates/mail/netlify.toml +1 -0
  126. package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +8 -0
  127. package/corpus/templates/plan/netlify.toml +1 -0
  128. package/corpus/templates/slides/netlify.toml +1 -0
  129. package/corpus/templates/videos/netlify.toml +1 -0
  130. package/dist/agent/production-agent.d.ts.map +1 -1
  131. package/dist/agent/production-agent.js +31 -0
  132. package/dist/agent/production-agent.js.map +1 -1
  133. package/dist/agent/run-store.d.ts +2 -0
  134. package/dist/agent/run-store.d.ts.map +1 -1
  135. package/dist/agent/run-store.js +2 -0
  136. package/dist/agent/run-store.js.map +1 -1
  137. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  138. package/dist/agent/thread-data-builder.js +23 -2
  139. package/dist/agent/thread-data-builder.js.map +1 -1
  140. package/dist/cli/create.d.ts.map +1 -1
  141. package/dist/cli/create.js +28 -1
  142. package/dist/cli/create.js.map +1 -1
  143. package/dist/cli/skills.d.ts +1 -1
  144. package/dist/cli/skills.d.ts.map +1 -1
  145. package/dist/cli/skills.js +8 -0
  146. package/dist/cli/skills.js.map +1 -1
  147. package/dist/client/AgentPanel.d.ts +8 -2
  148. package/dist/client/AgentPanel.d.ts.map +1 -1
  149. package/dist/client/AgentPanel.js +29 -15
  150. package/dist/client/AgentPanel.js.map +1 -1
  151. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  152. package/dist/client/MultiTabAssistantChat.js +4 -4
  153. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  154. package/dist/client/analytics.d.ts.map +1 -1
  155. package/dist/client/analytics.js +22 -2
  156. package/dist/client/analytics.js.map +1 -1
  157. package/dist/client/changelog/Changelog.d.ts +7 -2
  158. package/dist/client/changelog/Changelog.d.ts.map +1 -1
  159. package/dist/client/changelog/Changelog.js +9 -7
  160. package/dist/client/changelog/Changelog.js.map +1 -1
  161. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
  162. package/dist/client/extensions/ExtensionsSidebarSection.js +339 -11
  163. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  164. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  165. package/dist/client/use-agent-engine-configured.js +23 -13
  166. package/dist/client/use-agent-engine-configured.js.map +1 -1
  167. package/dist/client/use-chat-threads.d.ts +4 -0
  168. package/dist/client/use-chat-threads.d.ts.map +1 -1
  169. package/dist/client/use-chat-threads.js +49 -4
  170. package/dist/client/use-chat-threads.js.map +1 -1
  171. package/dist/collab/routes.d.ts +1 -1
  172. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  173. package/dist/observability/routes.d.ts +7 -7
  174. package/dist/server/agent-engine-api-key-route.d.ts +2 -2
  175. package/dist/server/analytics.d.ts +4 -0
  176. package/dist/server/analytics.d.ts.map +1 -1
  177. package/dist/server/analytics.js +19 -3
  178. package/dist/server/analytics.js.map +1 -1
  179. package/dist/sharing/access.d.ts.map +1 -1
  180. package/dist/sharing/access.js +26 -12
  181. package/dist/sharing/access.js.map +1 -1
  182. package/dist/sharing/actions/share-resource.js +20 -9
  183. package/dist/sharing/actions/share-resource.js.map +1 -1
  184. package/dist/sharing/actions/unshare-resource.js +13 -2
  185. package/dist/sharing/actions/unshare-resource.js.map +1 -1
  186. package/dist/sharing/registry.d.ts +9 -0
  187. package/dist/sharing/registry.d.ts.map +1 -1
  188. package/dist/sharing/registry.js.map +1 -1
  189. package/dist/vite/client.d.ts.map +1 -1
  190. package/dist/vite/client.js +1 -0
  191. package/dist/vite/client.js.map +1 -1
  192. package/package.json +3 -3
package/corpus/README.md CHANGED
@@ -28,4 +28,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
  ## Generated Counts
29
29
 
30
30
  - core files: 1932
31
- - template files: 4278
31
+ - template files: 4289
@@ -1,5 +1,36 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.77.7
4
+
5
+ ### Patch Changes
6
+
7
+ - e5bdcb3: Paginate the All Chats history client so older conversations can be loaded without truncating the history list.
8
+ - e5bdcb3: Drop benign reasonless browser AbortError events from Sentry reporting.
9
+ - e5bdcb3: Repair duplicate persisted chat message ids during thread normalization.
10
+ - e5bdcb3: Ensure hosted template apps can inject Google Analytics from Netlify build-time configuration.
11
+ - e5bdcb3: Normalize email casing when resolving shareable-resource ownership and user shares, and let resources opt into owner access across active-org drift.
12
+ - e5bdcb3: Re-check current LLM connection status before a stale missing-credentials event can keep chat locked after reconnecting Builder, and prevent page chat surfaces from rendering sidebar settings in the main content area.
13
+ - e5bdcb3: Allow browser Sentry initialization to build a DSN from Vite-exposed Sentry client key, project id, and ingest host env vars when runtime config injection is unavailable.
14
+
15
+ ## 0.77.6
16
+
17
+ ### Patch Changes
18
+
19
+ - 1fab043: Localize shared extension sidebar chrome and changelog labels/dates.
20
+
21
+ ## 0.77.5
22
+
23
+ ### Patch Changes
24
+
25
+ - 355be37: Add diagnostic-only worker-side progressive setup-stage diagnostics
26
+ (`worker_setup_step`) to localize where a durable background worker hangs between
27
+ `auth_passed` and `claimBackgroundRun`. For the background worker only (gated on
28
+ `isBackgroundWorker`, using the marker's early-available runId), emit the last
29
+ setup stage reached — `db_request_ctx`, `env_config`, `context_all`,
30
+ `action_tool_setup`, `owner_thread`, `prestart` — as the run's `diag_stage`, so a
31
+ worker that stalls leaves a breadcrumb at the stage it stopped in. Best-effort and
32
+ fire-and-forget; no control-flow change.
33
+
3
34
  ## 0.77.4
4
35
 
5
36
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.77.4",
3
+ "version": "0.77.7",
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": {
@@ -211,8 +211,8 @@
211
211
  "@react-router/dev": "^8.0.1",
212
212
  "@react-router/fs-routes": "^8.0.1",
213
213
  "@resvg/resvg-js": "^2.6.2",
214
- "@sentry/browser": "^10.50.0",
215
- "@sentry/node": "^10.50.0",
214
+ "@sentry/browser": "^10.61.0",
215
+ "@sentry/node": "^10.61.0",
216
216
  "@standard-schema/spec": "^1.1.0",
217
217
  "@tanstack/react-table": "^8.21.3",
218
218
  "@tiptap/core": "3.27.1",
@@ -3934,6 +3934,22 @@ export function createProductionAgentHandler(
3934
3934
  ? body[AGENT_CHAT_BACKGROUND_RUN_FIELD]!
3935
3935
  : null;
3936
3936
  const isBackgroundWorker = backgroundRunMarker !== null;
3937
+ // DIAGNOSTIC-ONLY: progressive per-stage hang localizer for the bg worker.
3938
+ // The worker's runId is available EARLY on the marker (the general `runId`
3939
+ // var resolves much later), so capture it now and emit the LAST setup stage
3940
+ // reached as the run's `diag_stage`. Best-effort, gated on the worker, never
3941
+ // blocks or alters control flow.
3942
+ const bgRunId = isBackgroundWorker
3943
+ ? (backgroundRunMarker?.runId as string)
3944
+ : null;
3945
+ const workerStep = (s: string) => {
3946
+ if (bgRunId)
3947
+ void recordRunDiagnostic(
3948
+ bgRunId,
3949
+ RUN_DIAG_STAGE.workerSetupStep,
3950
+ `${s}=${Date.now() - setupT0}ms`,
3951
+ ).catch(() => {});
3952
+ };
3937
3953
  // Whether this worker is REALLY executing inside a 15-min Netlify
3938
3954
  // `-background` function (proven by the runtime function name), not merely a
3939
3955
  // `_process-run` re-entry that may have landed on the ~60s synchronous
@@ -4002,6 +4018,10 @@ export function createProductionAgentHandler(
4002
4018
  requestAttachments = preparedRequest.attachments;
4003
4019
  }
4004
4020
  }
4021
+ // DIAGNOSTIC-ONLY: owner/request context prep (resolveAgentOwnerEmail +
4022
+ // prepareRequest) finished. A worker stuck before this points at the
4023
+ // owner/request-context awaits.
4024
+ workerStep("db_request_ctx");
4005
4025
 
4006
4026
  // Pre-upload chat attachments (images AND files/PDFs) through the framework
4007
4027
  // file-upload provider (Builder.io by default). The model still sees the
@@ -4164,6 +4184,10 @@ export function createProductionAgentHandler(
4164
4184
  }
4165
4185
 
4166
4186
  setupMark("prepDone");
4187
+ // DIAGNOSTIC-ONLY: engine/model/api-key resolution finished. A worker that
4188
+ // reached db_request_ctx but not env_config hung in attachment upload or
4189
+ // engine/model resolution.
4190
+ workerStep("env_config");
4167
4191
  // Run all independent pre-send steps in parallel. Each of these hits
4168
4192
  // the DB or invokes an action; running them sequentially was the
4169
4193
  // single biggest contributor to pre-LLM latency.
@@ -4418,6 +4442,9 @@ export function createProductionAgentHandler(
4418
4442
  enrichedMessagePromise,
4419
4443
  ]);
4420
4444
  setupMark("ctxAll");
4445
+ // DIAGNOSTIC-ONLY: all parallel context gathering (system prompt, screen,
4446
+ // files, loop settings, enriched message) resolved.
4447
+ workerStep("context_all");
4421
4448
 
4422
4449
  if (systemPromptError) {
4423
4450
  setResponseHeader(event, "Content-Type", "text/event-stream");
@@ -4446,6 +4473,8 @@ export function createProductionAgentHandler(
4446
4473
  options.initialToolNames,
4447
4474
  );
4448
4475
  setupMark("actions");
4476
+ // DIAGNOSTIC-ONLY: action/tool resolution + engine-tool filtering finished.
4477
+ workerStep("action_tool_setup");
4449
4478
  const requestSystemPrompt =
4450
4479
  requestMode === "plan"
4451
4480
  ? `${systemPrompt}\n\n${PLAN_MODE_SYSTEM_PROMPT}`
@@ -4557,6 +4586,9 @@ export function createProductionAgentHandler(
4557
4586
  }
4558
4587
  }
4559
4588
  setupMark("depsThread");
4589
+ // DIAGNOSTIC-ONLY: owner/thread resolution + runId/effectiveThreadId +
4590
+ // chained-continuation thread fetch finished.
4591
+ workerStep("owner_thread");
4560
4592
 
4561
4593
  // Persist the user's turn exactly once. The foreground POST does this
4562
4594
  // before dispatching; the background worker must NOT repeat it (it re-enters
@@ -4971,6 +5003,9 @@ export function createProductionAgentHandler(
4971
5003
  // startRun's callback below — the run row does not exist until startRun
4972
5004
  // inserts it, so a pre-startRun write would no-op on the inline path.
4973
5005
  setupMark("preStart");
5006
+ // DIAGNOSTIC-ONLY: last stage before startRun fires. A worker that reaches
5007
+ // prestart but never workerStarted is hanging inside startRun itself.
5008
+ workerStep("prestart");
4974
5009
  const setupDetail =
4975
5010
  Object.entries(setupMarks)
4976
5011
  .map(([k, v]) => `${k}=${v}`)
@@ -540,6 +540,8 @@ export const RUN_DIAG_STAGE = {
540
540
  workerClaimLost: "worker_claim_lost",
541
541
  /** The agent loop started (startRun fired). */
542
542
  workerStarted: "worker_started",
543
+ /** Last worker setup stage reached before startRun (progressive hang localizer). */
544
+ workerSetupStep: "worker_setup_step",
543
545
  /** Pre-claim setup timing breakdown (diagnostic). */
544
546
  setupTimings: "setup_timings",
545
547
  /** The worker threw before/while running the loop (message carried in detail). */
@@ -482,7 +482,26 @@ function normalizeAssistantToolCallIds(message: any): any {
482
482
  */
483
483
  export function normalizeThreadRepository(repo: any): any {
484
484
  const normalized = repo && typeof repo === "object" ? { ...repo } : {};
485
- const sourceMessages = Array.isArray(repo?.messages) ? repo.messages : [];
485
+ const sourceMessages: any[] = Array.isArray(repo?.messages)
486
+ ? repo.messages
487
+ : [];
488
+ const firstIndexById = new Map<string, number>();
489
+ const lastEntryById = new Map<string, any>();
490
+ sourceMessages.forEach((entry, index) => {
491
+ const id = messageId(getStoredMessage(entry));
492
+ if (!id) return;
493
+ if (!firstIndexById.has(id)) firstIndexById.set(id, index);
494
+ lastEntryById.set(id, entry);
495
+ });
496
+ const uniqueSourceMessages = sourceMessages
497
+ .filter((entry, index) => {
498
+ const id = messageId(getStoredMessage(entry));
499
+ return id && firstIndexById.get(id) === index;
500
+ })
501
+ .map((entry) => {
502
+ const id = messageId(getStoredMessage(entry));
503
+ return (id && lastEntryById.get(id)) || entry;
504
+ });
486
505
  const messages: Array<{
487
506
  message: any;
488
507
  parentId: string | null;
@@ -491,7 +510,7 @@ export function normalizeThreadRepository(repo: any): any {
491
510
  const seenIds = new Set<string>();
492
511
  let previousId: string | null = null;
493
512
 
494
- for (const entry of sourceMessages) {
513
+ for (const entry of uniqueSourceMessages) {
495
514
  const message = getStoredMessage(entry);
496
515
  const id = messageId(message);
497
516
  if (!id) continue;
@@ -27,6 +27,10 @@ const STANDALONE_EXACT_DEPENDENCY_OVERRIDES: Record<string, string> = {
27
27
  "@react-router/fs-routes": "8.0.1",
28
28
  "react-router": "8.0.1",
29
29
  };
30
+ const SENTRY_MINIMUM_RELEASE_AGE_EXCLUDES = [
31
+ '"@sentry/browser"',
32
+ '"@sentry/node"',
33
+ ];
30
34
  const FIRST_PARTY_TARBALL_SYMLINK_EXCLUDES = [
31
35
  "*/CLAUDE.md",
32
36
  "*/.claude/skills",
@@ -1054,7 +1058,12 @@ function postProcessStandalone(
1054
1058
  '"@assistant-ui/tap"': '"^0.5.14"',
1055
1059
  };
1056
1060
  }
1057
- const updated = mergeWorkspaceYamlSections(existing, sections);
1061
+ let updated = mergeWorkspaceYamlSections(existing, sections);
1062
+ updated = mergeWorkspaceYamlListItems(
1063
+ updated,
1064
+ "minimumReleaseAgeExclude",
1065
+ SENTRY_MINIMUM_RELEASE_AGE_EXCLUDES,
1066
+ );
1058
1067
  if (updated !== existing) {
1059
1068
  fs.writeFileSync(wsPath, updated);
1060
1069
  }
@@ -1366,6 +1375,31 @@ function mergeWorkspaceYamlSections(
1366
1375
  return result;
1367
1376
  }
1368
1377
 
1378
+ function mergeWorkspaceYamlListItems(
1379
+ yaml: string,
1380
+ section: string,
1381
+ items: string[],
1382
+ ): string {
1383
+ let result = yaml;
1384
+ for (const item of items) {
1385
+ const rendered = ` - ${item}`;
1386
+ if (result.includes(rendered)) continue;
1387
+ const sectionHeader = new RegExp(`^${section}:\\s*$`, "m");
1388
+ const match = sectionHeader.exec(result);
1389
+ if (match) {
1390
+ const insertAt = match.index + match[0].length;
1391
+ result =
1392
+ result.slice(0, insertAt) + `\n${rendered}` + result.slice(insertAt);
1393
+ } else {
1394
+ result =
1395
+ result.trimEnd() +
1396
+ (result ? "\n" : "") +
1397
+ `\n${section}:\n${rendered}\n`;
1398
+ }
1399
+ }
1400
+ return result;
1401
+ }
1402
+
1369
1403
  /**
1370
1404
  * Load the pnpm workspace catalog.
1371
1405
  * First tries the build-time snapshot at dist/catalog.json (works when
@@ -795,6 +795,14 @@ or cross either artboard, remove the label and explain the transition with a
795
795
  nearby annotation instead. Before handoff, inspect the top canvas at default zoom
796
796
  and move any frame whose label, connector, or annotation crosses another frame.
797
797
 
798
+ **Board-unit spacing defaults.** The canvas coordinate system uses approximately 2 board units per screen pixel. \`browser\` frames occupy roughly 700 × 600 board units; \`desktop\` frames roughly 900 × 700 board units. Apply these minimum x/y gaps when placing frames explicitly — any less and frames will touch or overlap:
799
+
800
+ - x-gap between \`browser\` frames: **≥ 1100** (700-unit frame + 400-unit gutter)
801
+ - x-gap between \`desktop\` frames: **≥ 1300** (900-unit frame + 400-unit gutter)
802
+ - y-gap between rows of any surface: **≥ 1400** (includes frame height + section header + buffer)
803
+
804
+ When in doubt, use larger values — the canvas auto-zooms to fit everything.
805
+
798
806
  **Canvas annotations are designer notes on the artboard.** When a top canvas is
799
807
  present, sprinkle Figma-style notes near the frames they explain: a short
800
808
  heading, supporting text, and bullets — plain text layers, never bordered or
@@ -164,6 +164,12 @@ const CLI_DEFAULT = "claude";
164
164
  const EXEC_MODE_KEY = "agent-native-exec-mode";
165
165
  type ExecMode = "build" | "plan";
166
166
  type PanelMode = "chat" | "cli" | "resources" | "settings";
167
+ export function normalizeAgentPanelModeForSurface(
168
+ mode: PanelMode,
169
+ allowSettingsMode: boolean,
170
+ ): PanelMode {
171
+ return mode === "settings" && !allowSettingsMode ? "chat" : mode;
172
+ }
167
173
  const AGENT_PANEL_FONT_FAMILY =
168
174
  'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
169
175
  const AGENT_PANEL_ROOT_STYLE = {
@@ -377,15 +383,11 @@ export function shouldShowAgentPanelChatTabBar(
377
383
  }
378
384
 
379
385
  export function shouldShowAgentPanelPageNewChatButton(
380
- tabs: MultiTabAssistantChatHeaderProps["tabs"],
386
+ _tabs: MultiTabAssistantChatHeaderProps["tabs"],
381
387
  activeTabId: string,
382
- activeTabMessageCount: number,
388
+ _activeTabMessageCount: number,
383
389
  ) {
384
- const activeTab = tabs.find((tab) => tab.id === activeTabId);
385
- return (
386
- Boolean(activeTabId) &&
387
- (activeTabMessageCount > 0 || activeTab?.status === "running")
388
- );
390
+ return Boolean(activeTabId);
389
391
  }
390
392
 
391
393
  export function shouldShowAgentPanelCliTabBar(cliTabs: string[]) {
@@ -548,6 +550,8 @@ export interface AgentPanelProps extends Omit<
548
550
  showTabBar?: boolean;
549
551
  /** Show a compact New chat action in page chat when the main header is hidden. */
550
552
  showPageNewChatButton?: boolean;
553
+ /** Allow the sidebar settings view to render inside this panel. Default: true. */
554
+ allowSettingsMode?: boolean;
551
555
  /** Capability gate for source edits and CLI access. */
552
556
  codeAccess?: AgentPanelCodeAccess;
553
557
  }
@@ -666,6 +670,7 @@ function AgentPanelInner({
666
670
  chatNotice,
667
671
  showTabBar = true,
668
672
  showPageNewChatButton = false,
673
+ allowSettingsMode = true,
669
674
  codeAccess,
670
675
  ...assistantChatProps
671
676
  }: AgentPanelProps) {
@@ -715,9 +720,9 @@ function AgentPanelInner({
715
720
  saved === "resources" ||
716
721
  saved === "settings"
717
722
  )
718
- return saved;
723
+ return normalizeAgentPanelModeForSurface(saved, allowSettingsMode);
719
724
  } catch {}
720
- return defaultMode;
725
+ return normalizeAgentPanelModeForSurface(defaultMode, allowSettingsMode);
721
726
  });
722
727
  useEffect(() => {
723
728
  try {
@@ -728,9 +733,18 @@ function AgentPanelInner({
728
733
  section: string | null;
729
734
  requestKey: number;
730
735
  }>({ section: null, requestKey: 0 });
731
- const switchMode = useCallback((m: PanelMode) => {
732
- startTransition(() => setMode(m));
733
- }, []);
736
+ const switchMode = useCallback(
737
+ (m: PanelMode) => {
738
+ startTransition(() =>
739
+ setMode(normalizeAgentPanelModeForSurface(m, allowSettingsMode)),
740
+ );
741
+ },
742
+ [allowSettingsMode],
743
+ );
744
+ useEffect(() => {
745
+ const nextMode = normalizeAgentPanelModeForSurface(mode, allowSettingsMode);
746
+ if (nextMode !== mode) switchMode(nextMode);
747
+ }, [mode, allowSettingsMode, switchMode]);
734
748
  const openRunThread = useCallback(
735
749
  (threadId: string, run?: AgentRun) => {
736
750
  switchMode("chat");
@@ -794,6 +808,10 @@ function AgentPanelInner({
794
808
  section: section ?? null,
795
809
  requestKey: prev.requestKey + 1,
796
810
  }));
811
+ if (!allowSettingsMode) {
812
+ switchMode("chat");
813
+ return;
814
+ }
797
815
  switchMode("settings");
798
816
  }
799
817
  window.addEventListener(
@@ -805,7 +823,7 @@ function AgentPanelInner({
805
823
  AGENT_PANEL_OPEN_SETTINGS_EVENT,
806
824
  handleOpenSettings,
807
825
  );
808
- }, [switchMode]);
826
+ }, [allowSettingsMode, switchMode]);
809
827
 
810
828
  // CLI terminal tabs (ephemeral — not persisted to SQL)
811
829
  const [cliTabs, setCliTabs] = useState<string[]>(["cli-1"]);
@@ -1151,15 +1169,17 @@ function AgentPanelInner({
1151
1169
  <DropdownMenuSeparator />
1152
1170
  </>
1153
1171
  )}
1154
- <DropdownMenuItem
1155
- onSelect={() => switchMode("settings")}
1156
- className={cn(
1157
- mode === "settings" ? "font-medium" : "text-muted-foreground",
1158
- )}
1159
- >
1160
- <IconSettings size={14} className="shrink-0" />
1161
- {t("agentPanel.settings")}
1162
- </DropdownMenuItem>
1172
+ {allowSettingsMode && (
1173
+ <DropdownMenuItem
1174
+ onSelect={() => switchMode("settings")}
1175
+ className={cn(
1176
+ mode === "settings" ? "font-medium" : "text-muted-foreground",
1177
+ )}
1178
+ >
1179
+ <IconSettings size={14} className="shrink-0" />
1180
+ {t("agentPanel.settings")}
1181
+ </DropdownMenuItem>
1182
+ )}
1163
1183
  <DropdownMenuItem onSelect={() => setFeedbackOpen(true)}>
1164
1184
  <IconMessageDots size={14} className="shrink-0" />
1165
1185
  {t("agentPanel.feedback")}
@@ -1241,6 +1261,7 @@ function AgentPanelInner({
1241
1261
  [
1242
1262
  activeCliTab,
1243
1263
  addCliTab,
1264
+ allowSettingsMode,
1244
1265
  availableClis,
1245
1266
  canUseCodeTools,
1246
1267
  closeAllCliTabs,
@@ -1287,7 +1308,7 @@ function AgentPanelInner({
1287
1308
  data-agent-page-new-chat=""
1288
1309
  aria-label={t("agentPanel.newChat")}
1289
1310
  onClick={() => {
1290
- addTab();
1311
+ void addTab();
1291
1312
  }}
1292
1313
  className="pointer-events-auto inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background/95 px-2.5 text-xs font-medium text-foreground shadow-sm backdrop-blur transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1293
1314
  >
@@ -2241,9 +2262,16 @@ export interface AgentChatSurfaceProps extends AgentPanelProps {
2241
2262
 
2242
2263
  export function shouldDefaultAgentChatSurfacePageNewChatButton(
2243
2264
  mode: AgentChatSurfaceMode | undefined,
2244
- showTabBar: boolean | undefined,
2265
+ _showTabBar: boolean | undefined,
2245
2266
  ): boolean {
2246
- return mode === "page" && showTabBar !== false;
2267
+ return mode === "page";
2268
+ }
2269
+
2270
+ export function shouldAllowAgentChatSurfaceSettingsMode(
2271
+ mode: AgentChatSurfaceMode | undefined,
2272
+ allowSettingsMode: boolean | undefined,
2273
+ ): boolean {
2274
+ return allowSettingsMode ?? mode !== "page";
2247
2275
  }
2248
2276
 
2249
2277
  /**
@@ -2272,6 +2300,10 @@ export function AgentChatSurface({
2272
2300
  {...props}
2273
2301
  defaultMode={defaultMode}
2274
2302
  isFullscreen={isFullscreen ?? pageMode}
2303
+ allowSettingsMode={shouldAllowAgentChatSurfaceSettingsMode(
2304
+ mode,
2305
+ props.allowSettingsMode,
2306
+ )}
2275
2307
  showPageNewChatButton={
2276
2308
  showPageNewChatButton ?? defaultShowPageNewChatButton
2277
2309
  }
@@ -428,16 +428,24 @@ function HistoryPopover({
428
428
  openTabIds,
429
429
  activeThreadId,
430
430
  currentScope,
431
+ hasMoreThreads = false,
432
+ isLoadingMoreThreads = false,
433
+ loadError,
431
434
  onSelect,
432
435
  onClose,
436
+ onLoadMore,
433
437
  onSearch,
434
438
  }: {
435
439
  threads: ChatThreadSummary[];
436
440
  openTabIds: Set<string>;
437
441
  activeThreadId: string | null;
438
442
  currentScope?: ChatThreadScope | null;
443
+ hasMoreThreads?: boolean;
444
+ isLoadingMoreThreads?: boolean;
445
+ loadError?: string | null;
439
446
  onSelect: (id: string) => void;
440
447
  onClose: () => void;
448
+ onLoadMore?: () => void;
441
449
  onSearch?: (query: string) => Promise<ChatThreadSummary[]>;
442
450
  }) {
443
451
  const [search, setSearch] = useState("");
@@ -542,7 +550,11 @@ function HistoryPopover({
542
550
  />
543
551
  </div>
544
552
  <div className="max-h-64 overflow-y-auto py-1">
545
- {isSearching ? (
553
+ {loadError && !search.trim() ? (
554
+ <div className="px-3 py-4 text-xs text-amber-500 text-center">
555
+ {loadError}
556
+ </div>
557
+ ) : isSearching ? (
546
558
  <div className="px-3 py-4 text-xs text-muted-foreground text-center">
547
559
  Searching...
548
560
  </div>
@@ -599,6 +611,16 @@ function HistoryPopover({
599
611
  ),
600
612
  )
601
613
  )}
614
+ {!search.trim() && hasMoreThreads && (
615
+ <button
616
+ type="button"
617
+ onClick={() => onLoadMore?.()}
618
+ disabled={isLoadingMoreThreads}
619
+ className="mx-1 mt-1 flex w-[calc(100%-0.5rem)] items-center justify-center rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-60"
620
+ >
621
+ {isLoadingMoreThreads ? "Loading..." : "Load older chats"}
622
+ </button>
623
+ )}
602
624
  </div>
603
625
  </PopoverContent>
604
626
  </Popover>
@@ -1034,7 +1056,11 @@ export function MultiTabAssistantChat({
1034
1056
  saveThreadData,
1035
1057
  generateTitle,
1036
1058
  searchThreads,
1059
+ loadMoreThreads,
1037
1060
  refreshThreads,
1061
+ hasMoreThreads,
1062
+ isLoadingMoreThreads,
1063
+ threadsLoadError,
1038
1064
  isNewThread,
1039
1065
  } = useChatThreads(apiUrl, storageKey, scope, {
1040
1066
  restoreActiveThread,
@@ -2649,8 +2675,12 @@ export function MultiTabAssistantChat({
2649
2675
  openTabIds={new Set(openTabIds)}
2650
2676
  activeThreadId={activeThreadId}
2651
2677
  currentScope={scope}
2678
+ hasMoreThreads={hasMoreThreads}
2679
+ isLoadingMoreThreads={isLoadingMoreThreads}
2680
+ loadError={threadsLoadError}
2652
2681
  onSelect={openFromHistory}
2653
2682
  onClose={() => setShowHistory(false)}
2683
+ onLoadMore={loadMoreThreads}
2654
2684
  onSearch={searchThreads}
2655
2685
  />
2656
2686
  )}
@@ -501,9 +501,12 @@ function shouldDropBrowserSentryNoise(event: Sentry.Event): boolean {
501
501
  .toLowerCase();
502
502
  return (
503
503
  exceptionValue === "the user aborted a request." ||
504
+ exceptionValue === "signal is aborted without reason" ||
504
505
  exceptionValue === "aborterror: the user aborted a request." ||
506
+ exceptionValue === "aborterror: signal is aborted without reason" ||
505
507
  (exceptionType === "aborterror" &&
506
- exceptionValue.includes("the user aborted a request"))
508
+ (exceptionValue.includes("the user aborted a request") ||
509
+ exceptionValue.includes("signal is aborted without reason")))
507
510
  );
508
511
  })
509
512
  ) {
@@ -534,12 +537,31 @@ function shouldDropBrowserSentryNoise(event: Sentry.Event): boolean {
534
537
  );
535
538
  }
536
539
 
540
+ function firstNonEmpty(...values: Array<string | undefined>): string {
541
+ for (const value of values) {
542
+ const trimmed = value?.trim();
543
+ if (trimmed) return trimmed;
544
+ }
545
+ return "";
546
+ }
547
+
548
+ function resolveClientSentryDsnFromKeyProject(
549
+ env: Record<string, string | undefined>,
550
+ ): string | undefined {
551
+ const key = firstNonEmpty(env.VITE_SENTRY_CLIENT_KEY);
552
+ const projectId = firstNonEmpty(env.VITE_SENTRY_PROJECT_ID);
553
+ const host = firstNonEmpty(env.VITE_SENTRY_INGEST_HOST);
554
+ if (!key || !projectId || !host) return undefined;
555
+ return `https://${key}@${host}/${projectId}`;
556
+ }
557
+
537
558
  function getClientSentryDsn(): string | undefined {
538
559
  const env = (import.meta.env as Record<string, string | undefined>) ?? {};
539
560
  return (
540
561
  env.VITE_SENTRY_CLIENT_DSN ||
541
562
  env.VITE_SENTRY_DSN ||
542
- window.__AGENT_NATIVE_CONFIG__?.sentryDsn
563
+ window.__AGENT_NATIVE_CONFIG__?.sentryDsn ||
564
+ resolveClientSentryDsnFromKeyProject(env)
543
565
  );
544
566
  }
545
567