@agent-native/core 0.90.10 → 0.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +30 -0
  3. package/corpus/core/docs/content/toolkit-settings.mdx +56 -3
  4. package/corpus/core/package.json +2 -1
  5. package/corpus/core/src/agent/durable-background.ts +5 -5
  6. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +40 -8
  7. package/corpus/core/src/agent/engine/anthropic-engine.ts +25 -7
  8. package/corpus/core/src/agent/engine/output-tokens.ts +94 -0
  9. package/corpus/core/src/agent/production-agent.ts +34 -6
  10. package/corpus/core/src/agent/run-ownership.ts +29 -1
  11. package/corpus/core/src/agent/types.ts +3 -2
  12. package/corpus/core/src/changelog/parse.ts +78 -0
  13. package/corpus/core/src/chat-threads/schema.ts +43 -0
  14. package/corpus/core/src/chat-threads/store.ts +144 -12
  15. package/corpus/core/src/cli/workspace-dev.ts +25 -5
  16. package/corpus/core/src/client/AgentPanel.tsx +79 -14
  17. package/corpus/core/src/client/AssistantChat.tsx +359 -425
  18. package/corpus/core/src/client/analytics-session.ts +70 -0
  19. package/corpus/core/src/client/analytics.ts +175 -49
  20. package/corpus/core/src/client/chat/message-components.tsx +22 -2
  21. package/corpus/core/src/client/components/ui/message-scroller.tsx +131 -0
  22. package/corpus/core/src/client/conversation/AgentConversation.tsx +63 -41
  23. package/corpus/core/src/client/error-capture.ts +616 -0
  24. package/corpus/core/src/client/index.ts +12 -0
  25. package/corpus/core/src/client/session-replay.ts +57 -7
  26. package/corpus/core/src/client/settings/SettingsPanel.tsx +123 -1
  27. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +292 -45
  28. package/corpus/core/src/client/settings/index.ts +1 -0
  29. package/corpus/core/src/client/sharing/ShareButton.tsx +14 -11
  30. package/corpus/core/src/client/sharing/ShareDialog.tsx +13 -10
  31. package/corpus/core/src/db/client.ts +12 -9
  32. package/corpus/core/src/mcp/builtin-tools.ts +29 -2
  33. package/corpus/core/src/server/agent-chat-plugin.ts +174 -79
  34. package/corpus/core/src/server/onboarding-html.ts +28 -16
  35. package/corpus/core/src/shared/reasoning-effort.ts +23 -0
  36. package/corpus/core/src/sharing/actions/list-resource-shares.ts +45 -1
  37. package/corpus/core/src/sharing/actions/share-resource.ts +9 -0
  38. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  39. package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
  40. package/corpus/core/src/vite/client.ts +217 -35
  41. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +8 -7
  42. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +14 -10
  43. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +15 -15
  44. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +1 -0
  45. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -22
  46. package/corpus/templates/analytics/.agents/skills/data-programs/SKILL.md +114 -61
  47. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +1 -0
  48. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +7 -7
  49. package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +1 -0
  50. package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +1 -1
  51. package/corpus/templates/analytics/.builder/skills/bigquery/SKILL.md +17 -17
  52. package/corpus/templates/analytics/.builder/skills/gcloud/SKILL.md +5 -5
  53. package/corpus/templates/analytics/.builder/skills/github/SKILL.md +7 -7
  54. package/corpus/templates/analytics/.builder/skills/gong/SKILL.md +12 -12
  55. package/corpus/templates/analytics/.builder/skills/grafana/SKILL.md +7 -7
  56. package/corpus/templates/analytics/.builder/skills/sentry/SKILL.md +7 -7
  57. package/corpus/templates/analytics/.builder/skills/slack/SKILL.md +6 -6
  58. package/corpus/templates/analytics/.builder/skills/stripe/SKILL.md +7 -7
  59. package/corpus/templates/analytics/AGENTS.md +47 -2
  60. package/corpus/templates/analytics/actions/add-status-page-monitor.ts +36 -0
  61. package/corpus/templates/analytics/actions/capture-test-error.ts +33 -0
  62. package/corpus/templates/analytics/actions/delete-monitor.ts +24 -0
  63. package/corpus/templates/analytics/actions/delete-status-page.ts +23 -0
  64. package/corpus/templates/analytics/actions/get-error-issue.ts +36 -0
  65. package/corpus/templates/analytics/actions/get-monitor-stats.ts +55 -0
  66. package/corpus/templates/analytics/actions/get-monitor.ts +27 -0
  67. package/corpus/templates/analytics/actions/get-public-status-page.ts +41 -0
  68. package/corpus/templates/analytics/actions/get-status-page.ts +29 -0
  69. package/corpus/templates/analytics/actions/list-error-issues.ts +40 -0
  70. package/corpus/templates/analytics/actions/list-monitors.ts +21 -0
  71. package/corpus/templates/analytics/actions/list-status-pages.ts +21 -0
  72. package/corpus/templates/analytics/actions/match-error-issues.ts +51 -0
  73. package/corpus/templates/analytics/actions/navigate.ts +58 -4
  74. package/corpus/templates/analytics/actions/remove-status-page-monitor.ts +22 -0
  75. package/corpus/templates/analytics/actions/reorder-status-page-monitors.ts +25 -0
  76. package/corpus/templates/analytics/actions/resolve-error-issue.ts +38 -0
  77. package/corpus/templates/analytics/actions/run-monitor-check.ts +23 -0
  78. package/corpus/templates/analytics/actions/save-analysis.ts +1 -0
  79. package/corpus/templates/analytics/actions/save-monitor.ts +145 -0
  80. package/corpus/templates/analytics/actions/save-status-page.ts +47 -0
  81. package/corpus/templates/analytics/actions/view-screen.ts +212 -0
  82. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  83. package/corpus/templates/analytics/app/components/layout/Layout.tsx +10 -1
  84. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +134 -39
  85. package/corpus/templates/analytics/app/components/monitoring/PublicStatusView.tsx +351 -0
  86. package/corpus/templates/analytics/app/components/monitoring/ResponseTimeChart.tsx +174 -0
  87. package/corpus/templates/analytics/app/components/monitoring/UptimeStatCards.tsx +85 -0
  88. package/corpus/templates/analytics/app/components/monitoring/UptimeTimelineBars.tsx +106 -0
  89. package/corpus/templates/analytics/app/components/monitoring/chart-utils.ts +101 -0
  90. package/corpus/templates/analytics/app/components/monitoring/index.ts +28 -0
  91. package/corpus/templates/analytics/app/components/monitoring/types.ts +42 -0
  92. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +34 -0
  93. package/corpus/templates/analytics/app/i18n/zh-TW.ts +6 -0
  94. package/corpus/templates/analytics/app/i18n-data.ts +33 -0
  95. package/corpus/templates/analytics/app/pages/DataSources.tsx +28 -0
  96. package/corpus/templates/analytics/app/pages/Settings.tsx +77 -9
  97. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -3
  98. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +7 -0
  99. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +205 -0
  100. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +100 -0
  101. package/corpus/templates/analytics/app/pages/monitoring/UptimePanel.tsx +475 -0
  102. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +372 -0
  103. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +360 -0
  104. package/corpus/templates/analytics/app/pages/monitoring/errors/Sparkline.tsx +55 -0
  105. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +106 -0
  106. package/corpus/templates/analytics/app/pages/monitoring/errors/types.ts +75 -0
  107. package/corpus/templates/analytics/app/pages/monitoring/errors/utils.ts +132 -0
  108. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +535 -0
  109. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +1026 -0
  110. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorList.tsx +311 -0
  111. package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +235 -0
  112. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPageEditor.tsx +661 -0
  113. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPagesView.tsx +355 -0
  114. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/i18n.ts +106 -0
  115. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/slug.ts +24 -0
  116. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/types.ts +20 -0
  117. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-summary.ts +87 -0
  118. package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +161 -0
  119. package/corpus/templates/analytics/app/pages/monitoring/uptime/utils.ts +210 -0
  120. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +76 -1
  121. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +74 -9
  122. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +197 -20
  123. package/corpus/templates/analytics/app/root.tsx +33 -1
  124. package/corpus/templates/analytics/app/routes/monitoring._index.tsx +10 -0
  125. package/corpus/templates/analytics/app/routes/status.$slug.tsx +118 -0
  126. package/corpus/templates/analytics/changelog/2026-07-08-add-uptime-monitors-that-ping-your-urls-and-alert-you-when-t.md +6 -0
  127. package/corpus/templates/analytics/changelog/2026-07-08-analytics-chats-keep-large-data-dictionaries-compact-so-prov.md +6 -0
  128. package/corpus/templates/analytics/changelog/2026-07-08-create-public-status-pages-that-share-the-live-health-of-cho.md +6 -0
  129. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-extension-panels-use-clearer-open-copy.md +6 -0
  130. package/corpus/templates/analytics/changelog/2026-07-08-error-capture.md +6 -0
  131. package/corpus/templates/analytics/changelog/2026-07-08-fixed-single-panel-dashboard-drags-so-they-no-longer-rewrite.md +6 -0
  132. package/corpus/templates/analytics/changelog/2026-07-08-jump-straight-from-an-error-in-a-session-recording-to-its-fu.md +6 -0
  133. package/corpus/templates/analytics/changelog/2026-07-08-monitor-detail-and-list-now-show-colorful-uptime-timelines-r.md +6 -0
  134. package/corpus/templates/analytics/changelog/2026-07-08-pinned-ask-chats-now-show-a-pin-icon-in-the-sidebar.md +6 -0
  135. package/corpus/templates/analytics/changelog/2026-07-08-publish-a-public-uptime-status-page-with-colorful-uptime-tim.md +6 -0
  136. package/corpus/templates/analytics/changelog/2026-07-08-sessions-now-hide-visitor-emails-in-demo-mode-and-show-clean.md +6 -0
  137. package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +5 -0
  138. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +5 -0
  139. package/corpus/templates/analytics/changelog/2026-07-08-the-uptime-monitor-list-now-has-a-current-status-overview-wi.md +6 -0
  140. package/corpus/templates/analytics/docs/error-capture.md +172 -0
  141. package/corpus/templates/analytics/docs/uptime-monitoring.md +150 -0
  142. package/corpus/templates/analytics/server/db/schema-errors.ts +150 -0
  143. package/corpus/templates/analytics/server/db/schema-monitoring.ts +177 -0
  144. package/corpus/templates/analytics/server/db/schema.ts +6 -0
  145. package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +123 -0
  146. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +41 -0
  147. package/corpus/templates/analytics/server/lib/data-dictionary-context.ts +55 -12
  148. package/corpus/templates/analytics/server/lib/error-capture.ts +1331 -0
  149. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +47 -1
  150. package/corpus/templates/analytics/server/lib/monitor-stats.ts +494 -0
  151. package/corpus/templates/analytics/server/lib/status-pages.ts +798 -0
  152. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +1665 -0
  153. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -37
  154. package/corpus/templates/analytics/server/plugins/auth.ts +11 -1
  155. package/corpus/templates/analytics/server/plugins/db.ts +408 -0
  156. package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +63 -0
  157. package/corpus/templates/assets/app/routes/_index.tsx +1 -1
  158. package/corpus/templates/assets/app/routes/settings.tsx +22 -1
  159. package/corpus/templates/assets/changelog/2026-07-08-recent-drafts-keep-comfortable-side-padding.md +6 -0
  160. package/corpus/templates/assets/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  161. package/corpus/templates/brain/app/routes/settings.tsx +43 -5
  162. package/corpus/templates/brain/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  163. package/corpus/templates/calendar/app/pages/Settings.tsx +44 -6
  164. package/corpus/templates/calendar/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  165. package/corpus/templates/chat/app/routes/settings.tsx +16 -1
  166. package/corpus/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  167. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +79 -37
  168. package/corpus/templates/clips/actions/regenerate-chapters.ts +7 -0
  169. package/corpus/templates/clips/app/components/player/player-controls.tsx +35 -0
  170. package/corpus/templates/clips/app/components/player/video-player.tsx +162 -18
  171. package/corpus/templates/clips/app/hooks/use-auto-title.ts +15 -5
  172. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +63 -6
  173. package/corpus/templates/clips/app/routes/download.tsx +12 -4
  174. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -0
  175. package/corpus/templates/clips/app/routes/record.tsx +18 -1
  176. package/corpus/templates/clips/changelog/2026-07-07-auto-generated-chapter-requests-now-open-the-matching-agent-.md +6 -0
  177. package/corpus/templates/clips/changelog/2026-07-07-shared-clips-start-playback-reliably-from-the-first-play-click.md +6 -0
  178. package/corpus/templates/clips/changelog/2026-07-07-the-clips-desktop-download-page-now-uses-the-agent-native-ap.md +6 -0
  179. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-camera-and-microphone-menus-now-show-which-.md +6 -0
  180. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-copies-new-clip-links-after-saving.md +6 -0
  181. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-recordings-now-continue-after-granting-came.md +6 -0
  182. package/corpus/templates/clips/changelog/2026-07-08-mobile-shared-clips-now-have-loom-style-tap-and-skip-controls.md +6 -0
  183. package/corpus/templates/clips/changelog/2026-07-08-recordings-and-uploads-copy-links-after-saving.md +6 -0
  184. package/corpus/templates/clips/changelog/2026-07-08-settings-are-cleaner-searchable-and-collapse-s3-with-builder.md +5 -0
  185. package/corpus/templates/clips/changelog/2026-07-08-transcripts-are-more-reliable-for-longer-desktop-recordings-.md +6 -0
  186. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -0
  187. package/corpus/templates/clips/chrome-extension/src/background.ts +162 -13
  188. package/corpus/templates/clips/chrome-extension/src/content-script.ts +14 -0
  189. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +36 -0
  190. package/corpus/templates/clips/chrome-extension/src/overlay.ts +99 -25
  191. package/corpus/templates/clips/chrome-extension/src/permission.ts +63 -14
  192. package/corpus/templates/clips/chrome-extension/src/popup.ts +123 -23
  193. package/corpus/templates/content/actions/_builder-cms-read-client.ts +47 -0
  194. package/corpus/templates/content/actions/_database-source-utils.ts +33 -4
  195. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +214 -29
  196. package/corpus/templates/content/actions/attach-content-database-source.ts +3 -0
  197. package/corpus/templates/content/actions/change-content-database-source-role.ts +1 -0
  198. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +42 -32
  199. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +352 -81
  200. package/corpus/templates/content/app/components/editor/database/settings.tsx +49 -24
  201. package/corpus/templates/content/app/routes/_app.settings.tsx +19 -1
  202. package/corpus/templates/content/changelog/2026-07-07-builder-source-tag-fields-now-import-as-multi-select.md +6 -0
  203. package/corpus/templates/content/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  204. package/corpus/templates/content/shared/api.ts +3 -0
  205. package/corpus/templates/content/shared/properties.ts +42 -0
  206. package/corpus/templates/design/actions/generate-design.ts +3 -0
  207. package/corpus/templates/design/actions/get-design-system.ts +147 -1
  208. package/corpus/templates/design/app/routes/settings.tsx +16 -1
  209. package/corpus/templates/design/changelog/2026-07-08-design-generation-now-follows-hydrated-builder-design-system.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  211. package/corpus/templates/dispatch/app/routes/settings.tsx +23 -2
  212. package/corpus/templates/dispatch/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  213. package/corpus/templates/forms/app/routes/_app.settings.tsx +16 -1
  214. package/corpus/templates/forms/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  215. package/corpus/templates/macros/app/routes/settings.tsx +16 -1
  216. package/corpus/templates/macros/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  217. package/corpus/templates/mail/app/components/settings/GmailFiltersSection.tsx +1 -1
  218. package/corpus/templates/mail/app/components/settings/SnippetsSection.tsx +1 -1
  219. package/corpus/templates/mail/app/pages/SettingsPage.tsx +123 -105
  220. package/corpus/templates/mail/changelog/2026-07-08-settings-redesigned-with-a-consistent-searchable-layout.md +5 -0
  221. package/corpus/templates/plan/app/routes/settings.tsx +23 -2
  222. package/corpus/templates/plan/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  223. package/corpus/templates/slides/actions/add-slide.ts +1 -0
  224. package/corpus/templates/slides/actions/get-design-system.ts +147 -1
  225. package/corpus/templates/slides/app/pages/Index.tsx +46 -1
  226. package/corpus/templates/slides/app/routes/settings.tsx +16 -1
  227. package/corpus/templates/slides/changelog/2026-07-08-deck-generation-now-follows-hydrated-builder-design-system-g.md +6 -0
  228. package/corpus/templates/slides/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  229. package/dist/agent/durable-background.d.ts +1 -1
  230. package/dist/agent/durable-background.d.ts.map +1 -1
  231. package/dist/agent/durable-background.js +5 -3
  232. package/dist/agent/durable-background.js.map +1 -1
  233. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  234. package/dist/agent/engine/ai-sdk-engine.js +24 -4
  235. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  236. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  237. package/dist/agent/engine/anthropic-engine.js +13 -3
  238. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  239. package/dist/agent/engine/output-tokens.d.ts +27 -0
  240. package/dist/agent/engine/output-tokens.d.ts.map +1 -1
  241. package/dist/agent/engine/output-tokens.js +70 -0
  242. package/dist/agent/engine/output-tokens.js.map +1 -1
  243. package/dist/agent/production-agent.d.ts.map +1 -1
  244. package/dist/agent/production-agent.js +28 -7
  245. package/dist/agent/production-agent.js.map +1 -1
  246. package/dist/agent/run-ownership.d.ts +6 -0
  247. package/dist/agent/run-ownership.d.ts.map +1 -1
  248. package/dist/agent/run-ownership.js +11 -1
  249. package/dist/agent/run-ownership.js.map +1 -1
  250. package/dist/agent/types.d.ts +3 -2
  251. package/dist/agent/types.d.ts.map +1 -1
  252. package/dist/agent/types.js.map +1 -1
  253. package/dist/changelog/parse.d.ts +7 -0
  254. package/dist/changelog/parse.d.ts.map +1 -1
  255. package/dist/changelog/parse.js +64 -0
  256. package/dist/changelog/parse.js.map +1 -1
  257. package/dist/chat-threads/schema.d.ts +445 -0
  258. package/dist/chat-threads/schema.d.ts.map +1 -0
  259. package/dist/chat-threads/schema.js +39 -0
  260. package/dist/chat-threads/schema.js.map +1 -0
  261. package/dist/chat-threads/store.d.ts +12 -0
  262. package/dist/chat-threads/store.d.ts.map +1 -1
  263. package/dist/chat-threads/store.js +99 -10
  264. package/dist/chat-threads/store.js.map +1 -1
  265. package/dist/cli/workspace-dev.d.ts.map +1 -1
  266. package/dist/cli/workspace-dev.js +20 -5
  267. package/dist/cli/workspace-dev.js.map +1 -1
  268. package/dist/client/AgentPanel.d.ts.map +1 -1
  269. package/dist/client/AgentPanel.js +35 -7
  270. package/dist/client/AgentPanel.js.map +1 -1
  271. package/dist/client/AssistantChat.d.ts.map +1 -1
  272. package/dist/client/AssistantChat.js +105 -183
  273. package/dist/client/AssistantChat.js.map +1 -1
  274. package/dist/client/analytics-session.d.ts +3 -0
  275. package/dist/client/analytics-session.d.ts.map +1 -0
  276. package/dist/client/analytics-session.js +66 -0
  277. package/dist/client/analytics-session.js.map +1 -0
  278. package/dist/client/analytics.d.ts +32 -0
  279. package/dist/client/analytics.d.ts.map +1 -1
  280. package/dist/client/analytics.js +116 -46
  281. package/dist/client/analytics.js.map +1 -1
  282. package/dist/client/chat/message-components.d.ts +2 -1
  283. package/dist/client/chat/message-components.d.ts.map +1 -1
  284. package/dist/client/chat/message-components.js +16 -6
  285. package/dist/client/chat/message-components.js.map +1 -1
  286. package/dist/client/components/ui/message-scroller.d.ts +16 -0
  287. package/dist/client/components/ui/message-scroller.d.ts.map +1 -0
  288. package/dist/client/components/ui/message-scroller.js +25 -0
  289. package/dist/client/components/ui/message-scroller.js.map +1 -0
  290. package/dist/client/conversation/AgentConversation.d.ts +1 -1
  291. package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
  292. package/dist/client/conversation/AgentConversation.js +3 -8
  293. package/dist/client/conversation/AgentConversation.js.map +1 -1
  294. package/dist/client/error-capture.d.ts +99 -0
  295. package/dist/client/error-capture.d.ts.map +1 -0
  296. package/dist/client/error-capture.js +437 -0
  297. package/dist/client/error-capture.js.map +1 -0
  298. package/dist/client/index.d.ts +2 -2
  299. package/dist/client/index.d.ts.map +1 -1
  300. package/dist/client/index.js +3 -1
  301. package/dist/client/index.js.map +1 -1
  302. package/dist/client/session-replay.d.ts +22 -0
  303. package/dist/client/session-replay.d.ts.map +1 -1
  304. package/dist/client/session-replay.js +45 -4
  305. package/dist/client/session-replay.js.map +1 -1
  306. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  307. package/dist/client/settings/SettingsPanel.js +100 -5
  308. package/dist/client/settings/SettingsPanel.js.map +1 -1
  309. package/dist/client/settings/SettingsTabsPage.d.ts +49 -1
  310. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  311. package/dist/client/settings/SettingsTabsPage.js +131 -19
  312. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  313. package/dist/client/settings/index.d.ts +1 -1
  314. package/dist/client/settings/index.d.ts.map +1 -1
  315. package/dist/client/settings/index.js.map +1 -1
  316. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  317. package/dist/client/sharing/ShareButton.js +13 -8
  318. package/dist/client/sharing/ShareButton.js.map +1 -1
  319. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  320. package/dist/client/sharing/ShareDialog.js +12 -7
  321. package/dist/client/sharing/ShareDialog.js.map +1 -1
  322. package/dist/collab/routes.d.ts +1 -1
  323. package/dist/collab/struct-routes.d.ts +1 -1
  324. package/dist/db/client.d.ts.map +1 -1
  325. package/dist/db/client.js +12 -7
  326. package/dist/db/client.js.map +1 -1
  327. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  328. package/dist/mcp/builtin-tools.js +21 -2
  329. package/dist/mcp/builtin-tools.js.map +1 -1
  330. package/dist/notifications/routes.d.ts +1 -1
  331. package/dist/observability/routes.d.ts +5 -5
  332. package/dist/org/schema.d.ts +15 -15
  333. package/dist/progress/routes.d.ts +1 -1
  334. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  335. package/dist/resources/handlers.d.ts +3 -3
  336. package/dist/secrets/routes.d.ts +9 -9
  337. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  338. package/dist/server/agent-chat-plugin.js +109 -73
  339. package/dist/server/agent-chat-plugin.js.map +1 -1
  340. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  341. package/dist/server/onboarding-html.d.ts.map +1 -1
  342. package/dist/server/onboarding-html.js +28 -16
  343. package/dist/server/onboarding-html.js.map +1 -1
  344. package/dist/shared/reasoning-effort.d.ts +1 -0
  345. package/dist/shared/reasoning-effort.d.ts.map +1 -1
  346. package/dist/shared/reasoning-effort.js +18 -0
  347. package/dist/shared/reasoning-effort.js.map +1 -1
  348. package/dist/sharing/actions/list-resource-shares.js +30 -1
  349. package/dist/sharing/actions/list-resource-shares.js.map +1 -1
  350. package/dist/sharing/actions/share-resource.js +6 -0
  351. package/dist/sharing/actions/share-resource.js.map +1 -1
  352. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  353. package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
  354. package/dist/vite/client.d.ts.map +1 -1
  355. package/dist/vite/client.js +192 -30
  356. package/dist/vite/client.js.map +1 -1
  357. package/docs/content/toolkit-settings.mdx +56 -3
  358. package/package.json +2 -1
  359. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  360. package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
@@ -242,4 +242,82 @@ export function rollupChangelog(
242
242
  return `${head}\n\n${section}\n${rest}\n`;
243
243
  }
244
244
 
245
+ function changelogHeader(markdown: string): string {
246
+ const doc = (markdown || CHANGELOG_HEADER).replace(/\s+$/, "");
247
+ const firstRelease = doc.search(/^##\s+(?!#)/m);
248
+ const header =
249
+ (firstRelease === -1 ? doc : doc.slice(0, firstRelease)).replace(
250
+ /\s+$/,
251
+ "",
252
+ ) || CHANGELOG_HEADER.trim();
253
+ return header;
254
+ }
255
+
256
+ function pendingSectionTitle(entry: PendingChangelogEntry): string {
257
+ return entry.date?.match(ISO_DATE)?.[1] ?? "Unreleased";
258
+ }
259
+
260
+ function pendingSectionSort(a: string, b: string): number {
261
+ const aDate = a.match(ISO_DATE)?.[1];
262
+ const bDate = b.match(ISO_DATE)?.[1];
263
+ if (aDate && bDate) return bDate.localeCompare(aDate);
264
+ if (!aDate && bDate) return -1;
265
+ if (aDate && !bDate) return 1;
266
+ return a.localeCompare(b);
267
+ }
268
+
269
+ /**
270
+ * Render an app-facing changelog that includes both released CHANGELOG.md
271
+ * sections and adjacent pending `changelog/*.md` entries. Unlike `release`,
272
+ * this is pure and non-destructive, so build/dev bundles can show current
273
+ * product notes without deleting the conflict-free pending files.
274
+ */
275
+ export function mergePendingChangelog(
276
+ existing: string,
277
+ pending: PendingChangelogEntry[],
278
+ ): string {
279
+ const pendingWithText = pending.filter((entry) => entry.text.trim());
280
+ if (pendingWithText.length === 0) return existing || CHANGELOG_HEADER;
281
+
282
+ const existingEntries = parseChangelog(existing);
283
+ const usedExistingIndexes = new Set<number>();
284
+ const pendingByTitle = new Map<string, PendingChangelogEntry[]>();
285
+
286
+ for (const entry of pendingWithText) {
287
+ const title = pendingSectionTitle(entry);
288
+ pendingByTitle.set(title, [...(pendingByTitle.get(title) ?? []), entry]);
289
+ }
290
+
291
+ const sections: string[] = [];
292
+ for (const title of [...pendingByTitle.keys()].sort(pendingSectionSort)) {
293
+ const body = renderReleaseBody(pendingByTitle.get(title) ?? []);
294
+ if (!body) continue;
295
+
296
+ const existingIndex = existingEntries.findIndex((entry, index) => {
297
+ if (usedExistingIndexes.has(index)) return false;
298
+ return entry.date === title || entry.title === title;
299
+ });
300
+
301
+ if (existingIndex === -1) {
302
+ sections.push(`## ${title}\n\n${body}`);
303
+ continue;
304
+ }
305
+
306
+ usedExistingIndexes.add(existingIndex);
307
+ const existingEntry = existingEntries[existingIndex];
308
+ sections.push(
309
+ `## ${existingEntry.title}\n\n${[body, existingEntry.body]
310
+ .filter(Boolean)
311
+ .join("\n\n")}`,
312
+ );
313
+ }
314
+
315
+ existingEntries.forEach((entry, index) => {
316
+ if (usedExistingIndexes.has(index)) return;
317
+ sections.push(`## ${entry.title}\n\n${entry.body}`);
318
+ });
319
+
320
+ return `${changelogHeader(existing)}\n\n${sections.join("\n\n")}\n`;
321
+ }
322
+
245
323
  export { CHANGELOG_HEADER, GROUP_LABELS };
@@ -0,0 +1,43 @@
1
+ import { table, text, integer } from "../db/schema.js";
2
+ import { createSharesTable, ownableColumns } from "../sharing/schema.js";
3
+
4
+ export const chatThreads = table("chat_threads", {
5
+ id: text("id").primaryKey(),
6
+ title: text("title").notNull().default(""),
7
+ preview: text("preview").notNull().default(""),
8
+ threadData: text("thread_data").notNull().default("{}"),
9
+ messageCount: integer("message_count").notNull().default(0),
10
+ createdAt: integer("created_at").notNull(),
11
+ updatedAt: integer("updated_at").notNull(),
12
+ scopeType: text("scope_type"),
13
+ scopeId: text("scope_id"),
14
+ scopeLabel: text("scope_label"),
15
+ pinnedAt: integer("pinned_at"),
16
+ archivedAt: integer("archived_at"),
17
+ shareTokenHash: text("share_token_hash"),
18
+ ...ownableColumns(),
19
+ });
20
+
21
+ export const chatThreadShares = createSharesTable("chat_thread_shares");
22
+
23
+ export const CHAT_THREAD_SHARES_CREATE_SQL = `CREATE TABLE IF NOT EXISTS chat_thread_shares (
24
+ id TEXT PRIMARY KEY,
25
+ resource_id TEXT NOT NULL,
26
+ principal_type TEXT NOT NULL,
27
+ principal_id TEXT NOT NULL,
28
+ role TEXT NOT NULL DEFAULT 'viewer',
29
+ created_by TEXT NOT NULL,
30
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
31
+ )`;
32
+
33
+ export const CHAT_THREAD_SHARES_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS chat_thread_shares (
34
+ id TEXT PRIMARY KEY,
35
+ resource_id TEXT NOT NULL,
36
+ principal_type TEXT NOT NULL,
37
+ principal_id TEXT NOT NULL,
38
+ role TEXT NOT NULL DEFAULT 'viewer',
39
+ created_by TEXT NOT NULL,
40
+ created_at TEXT NOT NULL DEFAULT now()
41
+ )`;
42
+
43
+ export const CHAT_THREAD_SHARES_RESOURCE_INDEX_SQL = `CREATE INDEX IF NOT EXISTS chat_thread_shares_resource_idx ON chat_thread_shares (resource_id)`;
@@ -6,13 +6,25 @@ import {
6
6
  normalizeThreadTitle,
7
7
  } from "../agent/thread-data-builder.js";
8
8
  import { getDbExec, intType, isPostgres } from "../db/client.js";
9
+ import { createGetDb } from "../db/create-get-db.js";
9
10
  import {
10
11
  ensureColumnExists,
11
12
  ensureIndexExists,
12
13
  ensureTableExists,
13
14
  } from "../db/ddl-guard.js";
14
15
  import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
16
+ import { getRequestOrgId } from "../server/request-context.js";
17
+ import { resolveAccess, type AccessContext } from "../sharing/access.js";
18
+ import { registerShareableResource } from "../sharing/registry.js";
19
+ import { roleSatisfies, type ShareRole } from "../sharing/schema.js";
15
20
  import { emitChatThreadChange } from "./emitter.js";
21
+ import {
22
+ chatThreads,
23
+ chatThreadShares,
24
+ CHAT_THREAD_SHARES_CREATE_SQL,
25
+ CHAT_THREAD_SHARES_CREATE_SQL_PG,
26
+ CHAT_THREAD_SHARES_RESOURCE_INDEX_SQL,
27
+ } from "./schema.js";
16
28
 
17
29
  let _initPromise: Promise<void> | undefined;
18
30
 
@@ -33,6 +45,7 @@ let _initPromise: Promise<void> | undefined;
33
45
  const _threadDataLocks = new Map<string, Promise<unknown>>();
34
46
  const DEFAULT_THREAD_DATA_UPDATE_ATTEMPTS = 12;
35
47
  const THREAD_DATA_CONFLICT_BACKOFF_MS = 25;
48
+ const getChatThreadsDb = createGetDb({ chatThreads, chatThreadShares });
36
49
 
37
50
  export function withThreadDataLock<T>(
38
51
  threadId: string,
@@ -73,7 +86,9 @@ async function ensureTable(): Promise<void> {
73
86
  scope_label TEXT,
74
87
  pinned_at ${intType()},
75
88
  archived_at ${intType()},
76
- share_token_hash TEXT
89
+ share_token_hash TEXT,
90
+ org_id TEXT,
91
+ visibility TEXT NOT NULL DEFAULT 'private'
77
92
  )
78
93
  `;
79
94
 
@@ -100,6 +115,8 @@ async function ensureTable(): Promise<void> {
100
115
  ["pinned_at", intType()],
101
116
  ["archived_at", intType()],
102
117
  ["share_token_hash", "TEXT"],
118
+ ["org_id", "TEXT"],
119
+ ["visibility", "TEXT NOT NULL DEFAULT 'private'"],
103
120
  ] as const) {
104
121
  await ensureColumnExists(
105
122
  "chat_threads",
@@ -107,6 +124,10 @@ async function ensureTable(): Promise<void> {
107
124
  `ALTER TABLE chat_threads ADD COLUMN IF NOT EXISTS ${col} ${type}`,
108
125
  );
109
126
  }
127
+ await ensureTableExists(
128
+ "chat_thread_shares",
129
+ CHAT_THREAD_SHARES_CREATE_SQL_PG,
130
+ );
110
131
  // Widen millisecond-timestamp columns that older deployments created as
111
132
  // 32-bit `INTEGER`; on Postgres the `Date.now()` written on every turn
112
133
  // overflows int4. No-op once widened / on fresh BIGINT databases.
@@ -134,6 +155,10 @@ async function ensureTable(): Promise<void> {
134
155
  "chat_threads_share_token_idx",
135
156
  `CREATE INDEX IF NOT EXISTS chat_threads_share_token_idx ON chat_threads (share_token_hash)`,
136
157
  );
158
+ await ensureIndexExists(
159
+ "chat_thread_shares_resource_idx",
160
+ CHAT_THREAD_SHARES_RESOURCE_INDEX_SQL,
161
+ );
137
162
  // One-time backfill of message_count for legacy rows written before
138
163
  // the column was maintained.
139
164
  await backfillLegacyMessageCounts(client);
@@ -153,6 +178,8 @@ async function ensureTable(): Promise<void> {
153
178
  ["pinned_at", intType()],
154
179
  ["archived_at", intType()],
155
180
  ["share_token_hash", "TEXT"],
181
+ ["org_id", "TEXT"],
182
+ ["visibility", "TEXT NOT NULL DEFAULT 'private'"],
156
183
  ] as const) {
157
184
  try {
158
185
  await client.execute(
@@ -162,6 +189,11 @@ async function ensureTable(): Promise<void> {
162
189
  // Column already exists.
163
190
  }
164
191
  }
192
+ try {
193
+ await client.execute(CHAT_THREAD_SHARES_CREATE_SQL);
194
+ } catch {
195
+ // Table already exists.
196
+ }
165
197
  // Widen millisecond-timestamp columns that older deployments created as
166
198
  // 32-bit `INTEGER`; on Postgres the `Date.now()` written on every turn
167
199
  // overflows int4. No-op once widened / on fresh BIGINT databases.
@@ -181,6 +213,7 @@ async function ensureTable(): Promise<void> {
181
213
  `CREATE INDEX IF NOT EXISTS chat_threads_owner_updated_idx ON chat_threads (owner_email, updated_at)`,
182
214
  `CREATE INDEX IF NOT EXISTS chat_threads_scope_updated_idx ON chat_threads (scope_type, scope_id, updated_at)`,
183
215
  `CREATE INDEX IF NOT EXISTS chat_threads_share_token_idx ON chat_threads (share_token_hash)`,
216
+ CHAT_THREAD_SHARES_RESOURCE_INDEX_SQL,
184
217
  ]) {
185
218
  try {
186
219
  await client.execute(ddl);
@@ -261,6 +294,8 @@ export interface ChatThread {
261
294
  scope: ChatThreadScope | null;
262
295
  pinnedAt: number | null;
263
296
  archivedAt: number | null;
297
+ orgId: string | null;
298
+ visibility: "private" | "org" | "public";
264
299
  }
265
300
 
266
301
  export interface ChatThreadSummary {
@@ -273,6 +308,8 @@ export interface ChatThreadSummary {
273
308
  scope: ChatThreadScope | null;
274
309
  pinnedAt: number | null;
275
310
  archivedAt: number | null;
311
+ orgId: string | null;
312
+ visibility: "private" | "org" | "public";
276
313
  }
277
314
 
278
315
  export interface ForkThreadSourceSnapshot {
@@ -297,6 +334,10 @@ function readNullableNumber(value: unknown): number | null {
297
334
  return Number.isFinite(n) && n > 0 ? n : null;
298
335
  }
299
336
 
337
+ function readVisibility(value: unknown): "private" | "org" | "public" {
338
+ return value === "org" || value === "public" ? value : "private";
339
+ }
340
+
300
341
  function normalizeForkSourceSnapshot(
301
342
  source: ForkThreadSourceSnapshot | null | undefined,
302
343
  ): {
@@ -359,6 +400,8 @@ function rowToThread(r: Record<string, unknown>): ChatThread {
359
400
  scope: readScope(r),
360
401
  pinnedAt: readNullableNumber(r.pinned_at),
361
402
  archivedAt: readNullableNumber(r.archived_at),
403
+ orgId: (r.org_id as string | null | undefined) ?? null,
404
+ visibility: readVisibility(r.visibility),
362
405
  };
363
406
  }
364
407
 
@@ -378,6 +421,8 @@ function rowToSummary(r: Record<string, unknown>): ChatThreadSummary | null {
378
421
  scope: readScope(r),
379
422
  pinnedAt: readNullableNumber(r.pinned_at),
380
423
  archivedAt: readNullableNumber(r.archived_at),
424
+ orgId: (r.org_id as string | null | undefined) ?? null,
425
+ visibility: readVisibility(r.visibility),
381
426
  };
382
427
  }
383
428
 
@@ -391,9 +436,10 @@ export async function createThread(
391
436
  const now = Date.now();
392
437
  const title = opts?.title ?? "";
393
438
  const scope = opts?.scope ?? null;
439
+ const orgId = getRequestOrgId() ?? null;
394
440
 
395
441
  await client.execute({
396
- sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?, ?)`,
442
+ sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, org_id, visibility) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?, ?, ?, 'private')`,
397
443
  args: [
398
444
  id,
399
445
  ownerEmail,
@@ -403,6 +449,7 @@ export async function createThread(
403
449
  scope?.type ?? null,
404
450
  scope?.id ?? null,
405
451
  scope?.label ?? null,
452
+ orgId,
406
453
  ],
407
454
  });
408
455
 
@@ -418,10 +465,12 @@ export async function createThread(
418
465
  scope,
419
466
  pinnedAt: null,
420
467
  archivedAt: null,
468
+ orgId,
469
+ visibility: "private",
421
470
  };
422
471
  }
423
472
 
424
- const THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;
473
+ const THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at, org_id, visibility`;
425
474
  // The list/summary path deliberately omits `thread_data`: it is the full
426
475
  // message-history JSON blob and selecting it for every row turns "open the
427
476
  // sidebar" into "download every conversation". The summary derives nothing
@@ -429,7 +478,41 @@ const THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_co
429
478
  // (message_count is maintained on write and backfilled for legacy rows at
430
479
  // bootstrap). The detail path (`THREAD_COLUMNS` / `getThread`) still returns
431
480
  // the full blob.
432
- const SUMMARY_COLUMNS = `id, title, preview, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;
481
+ const SUMMARY_COLUMNS = `id, title, preview, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at, org_id, visibility`;
482
+
483
+ export function registerChatThreadsShareable(): void {
484
+ registerShareableResource({
485
+ type: "chat_thread",
486
+ resourceTable: chatThreads,
487
+ sharesTable: chatThreadShares,
488
+ displayName: "Chat",
489
+ titleColumn: "title",
490
+ getResourcePath: (thread) =>
491
+ `/?thread=${encodeURIComponent(String(thread.id ?? ""))}`,
492
+ getDb: () => getChatThreadsDb(),
493
+ allowPublic: false,
494
+ ownerAccessIgnoresOrg: true,
495
+ });
496
+ }
497
+
498
+ export async function ensureChatThreadTables(): Promise<void> {
499
+ await ensureTable();
500
+ }
501
+
502
+ export async function resolveThreadAccess(
503
+ userEmail: string | null | undefined,
504
+ threadId: string | null | undefined,
505
+ minRole: ShareRole | "owner" = "viewer",
506
+ ctx: Omit<AccessContext, "userEmail"> = {},
507
+ ): Promise<ChatThread | null> {
508
+ if (!userEmail || !threadId) return null;
509
+ const access = await resolveAccess("chat_thread", threadId, {
510
+ userEmail,
511
+ orgId: ctx.orgId,
512
+ });
513
+ if (!access || !roleSatisfies(access.role, minRole)) return null;
514
+ return await getThread(threadId);
515
+ }
433
516
 
434
517
  export async function getThread(id: string): Promise<ChatThread | null> {
435
518
  await ensureTable();
@@ -445,7 +528,11 @@ export async function getThread(id: string): Promise<ChatThread | null> {
445
528
  export async function forkThread(
446
529
  sourceId: string,
447
530
  ownerEmail: string,
448
- opts?: { id?: string; source?: ForkThreadSourceSnapshot | null },
531
+ opts?: {
532
+ id?: string;
533
+ source?: ForkThreadSourceSnapshot | null;
534
+ sourceAccessGranted?: boolean;
535
+ },
449
536
  ): Promise<ChatThread | null> {
450
537
  const snapshot = normalizeForkSourceSnapshot(opts?.source);
451
538
  let source = await getThread(sourceId);
@@ -494,13 +581,19 @@ export async function forkThread(
494
581
  messageCount: snapshot.messageCount,
495
582
  };
496
583
  }
497
- if (!source || source.ownerEmail !== ownerEmail) return null;
584
+ if (
585
+ !source ||
586
+ (!opts?.sourceAccessGranted && source.ownerEmail !== ownerEmail)
587
+ ) {
588
+ return null;
589
+ }
498
590
  const id = opts?.id ?? generateId();
499
591
  const now = Date.now();
500
592
  const title = source.title ? `${source.title} (fork)` : "";
501
593
  const client = getDbExec();
594
+ const orgId = getRequestOrgId() ?? null;
502
595
  await client.execute({
503
- sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
596
+ sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, org_id, visibility) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'private')`,
504
597
  args: [
505
598
  id,
506
599
  ownerEmail,
@@ -513,6 +606,7 @@ export async function forkThread(
513
606
  source.scope?.type ?? null,
514
607
  source.scope?.id ?? null,
515
608
  source.scope?.label ?? null,
609
+ orgId,
516
610
  ],
517
611
  });
518
612
  return {
@@ -527,6 +621,8 @@ export async function forkThread(
527
621
  scope: source.scope,
528
622
  pinnedAt: null,
529
623
  archivedAt: null,
624
+ orgId,
625
+ visibility: "private",
530
626
  };
531
627
  }
532
628
 
@@ -542,6 +638,28 @@ export interface ListThreadsOptions {
542
638
  scope?: { type: string; id: string };
543
639
  /** When true, returns only threads with no scope (general chats). */
544
640
  unscopedOnly?: boolean;
641
+ orgId?: string | null;
642
+ }
643
+
644
+ function chatThreadAccessSql(
645
+ userEmail: string,
646
+ orgId: string | null | undefined,
647
+ ): { sql: string; args: (string | number)[] } {
648
+ const normalizedEmail = userEmail.trim().toLowerCase();
649
+ const clauses = [
650
+ `LOWER(owner_email) = ?`,
651
+ `EXISTS (SELECT 1 FROM chat_thread_shares WHERE chat_thread_shares.resource_id = chat_threads.id AND chat_thread_shares.principal_type = 'user' AND LOWER(chat_thread_shares.principal_id) = ?)`,
652
+ ];
653
+ const args: (string | number)[] = [normalizedEmail, normalizedEmail];
654
+ if (orgId) {
655
+ clauses.push(`(visibility = 'org' AND org_id = ?)`);
656
+ args.push(orgId);
657
+ clauses.push(
658
+ `EXISTS (SELECT 1 FROM chat_thread_shares WHERE chat_thread_shares.resource_id = chat_threads.id AND chat_thread_shares.principal_type = 'org' AND chat_thread_shares.principal_id = ?)`,
659
+ );
660
+ args.push(orgId);
661
+ }
662
+ return { sql: `(${clauses.join(" OR ")})`, args };
545
663
  }
546
664
 
547
665
  export async function listThreads(
@@ -562,8 +680,12 @@ export async function listThreads(
562
680
  // maintained on every write and backfilled for legacy rows at bootstrap,
563
681
  // so the old `OR thread_data LIKE '%"messages"%'` substring scan over the
564
682
  // full blob is no longer needed here.
565
- const filters: string[] = [`owner_email = ?`, `message_count > 0`];
566
- const args: (string | number)[] = [ownerEmail];
683
+ const access = chatThreadAccessSql(
684
+ ownerEmail,
685
+ opts.orgId ?? getRequestOrgId(),
686
+ );
687
+ const filters: string[] = [access.sql, `message_count > 0`];
688
+ const args: (string | number)[] = [...access.args];
567
689
  if (opts.scope) {
568
690
  filters.push(`scope_type = ? AND scope_id = ?`);
569
691
  args.push(opts.scope.type, opts.scope.id);
@@ -588,7 +710,7 @@ export async function searchThreads(
588
710
  ownerEmail: string,
589
711
  query: string,
590
712
  limit = 50,
591
- options: { scope?: { type: string; id: string } } = {},
713
+ options: { scope?: { type: string; id: string }; orgId?: string | null } = {},
592
714
  ): Promise<ChatThreadSummary[]> {
593
715
  await ensureTable();
594
716
  const client = getDbExec();
@@ -596,12 +718,16 @@ export async function searchThreads(
596
718
  // The count-guard uses the maintained/backfilled `message_count` column
597
719
  // (same as listThreads). The content match still scans `thread_data` —
598
720
  // search legitimately needs to look inside message history.
721
+ const access = chatThreadAccessSql(
722
+ ownerEmail,
723
+ options.orgId ?? getRequestOrgId(),
724
+ );
599
725
  const filters: string[] = [
600
- `owner_email = ?`,
726
+ access.sql,
601
727
  `message_count > 0`,
602
728
  `(title LIKE ? ESCAPE '!' OR preview LIKE ? ESCAPE '!' OR thread_data LIKE ? ESCAPE '!')`,
603
729
  ];
604
- const args: (string | number)[] = [ownerEmail, pattern, pattern, pattern];
730
+ const args: (string | number)[] = [...access.args, pattern, pattern, pattern];
605
731
  if (options.scope) {
606
732
  filters.push(`scope_type = ? AND scope_id = ?`);
607
733
  args.push(options.scope.type, options.scope.id);
@@ -1137,6 +1263,12 @@ export async function deleteThread(id: string): Promise<boolean> {
1137
1263
  args: [id],
1138
1264
  });
1139
1265
  if (result.rowsAffected > 0) {
1266
+ await client
1267
+ .execute({
1268
+ sql: `DELETE FROM chat_thread_shares WHERE resource_id = ?`,
1269
+ args: [id],
1270
+ })
1271
+ .catch(() => {});
1140
1272
  emitChatThreadChange(id);
1141
1273
  return true;
1142
1274
  }
@@ -83,6 +83,7 @@ const DEFAULT_PROXY_RESPONSE_TIMEOUT_MS = 5_000;
83
83
  const DEFAULT_PROXY_NON_HTML_RESPONSE_TIMEOUT_MS = 120_000;
84
84
  const APP_OUTPUT_TAIL_BYTES = 8_000;
85
85
  const POLLING_WATCH_INTERVAL_MS = "1000";
86
+ const reportedDiscoverAppsReadFailures = new Set<string>();
86
87
  const STARTING_APP_RESPONSE_HEADERS: http.OutgoingHttpHeaders = {
87
88
  "content-type": "text/html; charset=utf-8",
88
89
  "cache-control": "no-store, no-cache, max-age=0, must-revalidate",
@@ -287,6 +288,23 @@ function readJson(file: string): any {
287
288
  }
288
289
  }
289
290
 
291
+ function shouldReportDiscoverAppsReadFailure(
292
+ appsDir: string,
293
+ code: string | undefined,
294
+ ): boolean {
295
+ if (code === "ENOENT") return false;
296
+ const key = `${appsDir}:${code ?? "unknown"}`;
297
+ if (reportedDiscoverAppsReadFailures.has(key)) return false;
298
+ reportedDiscoverAppsReadFailures.add(key);
299
+ return true;
300
+ }
301
+
302
+ function shouldCaptureDiscoverAppsReadFailure(
303
+ code: string | undefined,
304
+ ): boolean {
305
+ return code !== "EACCES" && code !== "EPERM";
306
+ }
307
+
290
308
  function discoverApps(appsDir: string, appPortStart: number): WorkspaceApp[] {
291
309
  if (!fs.existsSync(appsDir)) return [];
292
310
  // existsSync -> readdirSync is a TOCTOU race. Treat ENOENT as "no apps
@@ -296,15 +314,17 @@ function discoverApps(appsDir: string, appPortStart: number): WorkspaceApp[] {
296
314
  entries = fs.readdirSync(appsDir, { withFileTypes: true });
297
315
  } catch (err) {
298
316
  const code = (err as NodeJS.ErrnoException).code;
299
- if (code !== "ENOENT") {
317
+ if (shouldReportDiscoverAppsReadFailure(appsDir, code)) {
300
318
  console.warn(
301
319
  `[workspace] Could not read ${appsDir} (${code ?? "unknown"}): ` +
302
320
  `${(err as Error).message}`,
303
321
  );
304
- Sentry.captureException(err, {
305
- tags: { handled: "dev-discover-readdir" },
306
- level: "warning",
307
- });
322
+ if (shouldCaptureDiscoverAppsReadFailure(code)) {
323
+ Sentry.captureException(err, {
324
+ tags: { handled: "dev-discover-readdir" },
325
+ level: "warning",
326
+ });
327
+ }
308
328
  }
309
329
  return [];
310
330
  }