@agent-native/core 0.121.1 → 0.122.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 (465) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +145 -0
  3. package/corpus/core/docs/content/actions.mdx +2 -2
  4. package/corpus/core/docs/content/agent-surfaces.mdx +1 -1
  5. package/corpus/core/docs/content/authentication.mdx +2 -0
  6. package/corpus/core/docs/content/deployment.mdx +36 -0
  7. package/corpus/core/docs/content/getting-started.mdx +477 -115
  8. package/corpus/core/docs/content/http-api.mdx +266 -0
  9. package/corpus/core/docs/content/syncing-template-changes.mdx +159 -0
  10. package/corpus/core/package.json +2 -1
  11. package/corpus/core/src/a2a/activity.ts +38 -27
  12. package/corpus/core/src/agent/engine/builder-engine.ts +35 -4
  13. package/corpus/core/src/agent/run-manager.ts +20 -1
  14. package/corpus/core/src/agent/run-store.ts +4 -1
  15. package/corpus/core/src/agent/types.ts +28 -8
  16. package/corpus/core/src/chat-threads/store.ts +45 -0
  17. package/corpus/core/src/checkpoints/index.ts +2 -0
  18. package/corpus/core/src/checkpoints/route-match.ts +13 -0
  19. package/corpus/core/src/cli/create.ts +105 -17
  20. package/corpus/core/src/cli/index.ts +20 -0
  21. package/corpus/core/src/cli/template-baseline.ts +410 -0
  22. package/corpus/core/src/cli/template-sync.ts +1004 -0
  23. package/corpus/core/src/cli/templates-meta.ts +1 -0
  24. package/corpus/core/src/client/AssistantChat.tsx +38 -12
  25. package/corpus/core/src/client/NewWorkspaceAppFlow.tsx +75 -10
  26. package/corpus/core/src/client/chat/message-components.tsx +127 -66
  27. package/corpus/core/src/client/chat/tool-call-display.tsx +29 -5
  28. package/corpus/core/src/client/error-format.ts +18 -0
  29. package/corpus/core/src/client/org/OrgSwitcher.tsx +30 -8
  30. package/corpus/core/src/client/org/TeamPage.tsx +7 -6
  31. package/corpus/core/src/client/sse-event-processor.ts +3 -0
  32. package/corpus/core/src/data-widgets/index.ts +41 -0
  33. package/corpus/core/src/deploy/build.ts +11 -9
  34. package/corpus/core/src/integrations/webhook-handler.ts +63 -9
  35. package/corpus/core/src/org/auto-join-domain.ts +4 -3
  36. package/corpus/core/src/org/context.ts +108 -39
  37. package/corpus/core/src/org/index.ts +5 -0
  38. package/corpus/core/src/org/service-identity.ts +60 -0
  39. package/corpus/core/src/provider-api/index.ts +22 -1
  40. package/corpus/core/src/scripts/docs/search.ts +12 -1
  41. package/corpus/core/src/server/agent-chat/context-tools.ts +9 -4
  42. package/corpus/core/src/server/agent-chat/framework-prompts.ts +38 -66
  43. package/corpus/core/src/server/agent-chat-plugin.ts +17 -10
  44. package/corpus/core/src/server/agents-bundle.ts +84 -5
  45. package/corpus/core/src/server/auth.ts +4 -3
  46. package/corpus/core/src/server/builder-browser.ts +47 -19
  47. package/corpus/core/src/server/core-routes-plugin.ts +2 -2
  48. package/corpus/core/src/server/credential-provider.ts +209 -68
  49. package/corpus/core/src/server/index.ts +3 -0
  50. package/corpus/core/src/server/prompts/framework-core-compact.ts +6 -13
  51. package/corpus/core/src/server/prompts/framework-core.ts +9 -21
  52. package/corpus/core/src/server/prompts/index.ts +0 -1
  53. package/corpus/core/src/server/prompts/shared-rules.ts +19 -11
  54. package/corpus/core/src/server/ssr-handler.ts +13 -2
  55. package/corpus/core/src/settings/user-settings.ts +13 -1
  56. package/corpus/core/src/shared/cache-control.ts +141 -0
  57. package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  58. package/corpus/core/src/templates/chat/_gitignore +1 -0
  59. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  60. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  61. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
  62. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  63. package/corpus/core/src/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
  64. package/corpus/core/src/vite/agents-bundle-plugin.ts +7 -6
  65. package/corpus/core/src/vite/client.ts +5 -0
  66. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +4 -5
  67. package/corpus/templates/analytics/.agents/skills/admin-surfaces/SKILL.md +55 -0
  68. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +10 -0
  69. package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +62 -0
  70. package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +85 -0
  71. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +27 -2
  72. package/corpus/templates/analytics/.agents/skills/data-querying/references/inline-chart-embeds.md +139 -0
  73. package/corpus/templates/analytics/.agents/skills/monitoring/SKILL.md +77 -0
  74. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  75. package/corpus/templates/analytics/AGENTS.md +91 -563
  76. package/corpus/templates/analytics/_gitignore +1 -0
  77. package/corpus/templates/analytics/actions/bigquery.ts +12 -4
  78. package/corpus/templates/analytics/actions/generate-chart.ts +5 -5
  79. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -1
  80. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +3 -0
  81. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +1 -1
  82. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +1 -0
  83. package/corpus/templates/analytics/changelog/2026-07-24-analytics-finds-saved-dashboards-and-queries-far-more-reliab.md +6 -0
  84. package/corpus/templates/analytics/changelog/2026-07-24-notion-access-errors-now-explain-that-a-page-needs-sharing-w.md +6 -0
  85. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +7 -0
  86. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +191 -30
  87. package/corpus/templates/analytics/server/lib/bigquery.ts +11 -1
  88. package/corpus/templates/analytics/server/lib/notion.ts +15 -4
  89. package/corpus/templates/analytics/server/plugins/agent-chat.ts +10 -0
  90. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  91. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  92. package/corpus/templates/brain/_gitignore +1 -0
  93. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  94. package/corpus/templates/calendar/_gitignore +1 -0
  95. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  96. package/corpus/templates/chat/_gitignore +1 -0
  97. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  98. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +34 -0
  99. package/corpus/templates/clips/AGENTS.md +5 -4
  100. package/corpus/templates/clips/_gitignore +1 -0
  101. package/corpus/templates/clips/actions/cleanup-transcript.ts +5 -0
  102. package/corpus/templates/clips/actions/get-recording-insights.ts +44 -8
  103. package/corpus/templates/clips/actions/get-recording-player-data.ts +39 -70
  104. package/corpus/templates/clips/actions/list-clip-views.ts +2 -4
  105. package/corpus/templates/clips/actions/list-recordings.ts +69 -19
  106. package/corpus/templates/clips/actions/list-viewers.ts +5 -2
  107. package/corpus/templates/clips/actions/regenerate-summary.ts +5 -1
  108. package/corpus/templates/clips/actions/request-transcript.ts +28 -1
  109. package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +21 -1
  110. package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +14 -1
  111. package/corpus/templates/clips/app/components/player/insights-panel.tsx +8 -20
  112. package/corpus/templates/clips/app/components/player/recording-views-badge.tsx +335 -0
  113. package/corpus/templates/clips/app/components/player/video-player.tsx +13 -8
  114. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +2 -1
  115. package/corpus/templates/clips/app/i18n/en-US.ts +13 -2
  116. package/corpus/templates/clips/app/lib/capture-permissions.ts +110 -0
  117. package/corpus/templates/clips/app/lib/recording-link.ts +32 -0
  118. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +27 -0
  119. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +46 -15
  120. package/corpus/templates/clips/app/routes/record.tsx +77 -31
  121. package/corpus/templates/clips/app/routes/share.$shareId.tsx +42 -31
  122. package/corpus/templates/clips/changelog/2026-07-24-clip-pages-now-show-a-view-count-with-viewer-avatars-next-to.md +6 -0
  123. package/corpus/templates/clips/changelog/2026-07-24-clip-summaries-read-like-a-description-you-d-write-yourself-.md +6 -0
  124. package/corpus/templates/clips/changelog/2026-07-24-clip-view-counts-now-separate-human-views-from-agent-views-s.md +6 -0
  125. package/corpus/templates/clips/changelog/2026-07-24-clips-actions-can-now-be-called-by-org-service-tokens-so-ext.md +6 -0
  126. package/corpus/templates/clips/changelog/2026-07-24-clips-no-longer-briefly-shows-a-dock-icon-while-launching.md +6 -0
  127. package/corpus/templates/clips/changelog/2026-07-24-fixed-owner-and-editor-share-links-redirecting-to-the-record.md +6 -0
  128. package/corpus/templates/clips/changelog/2026-07-24-meeting-and-note-recordings-now-start-as-a-compact-pill-on-t.md +6 -0
  129. package/corpus/templates/clips/changelog/2026-07-24-new-clips-now-copy-a-shareable-link-instead-of-the-owner-onl.md +6 -0
  130. package/corpus/templates/clips/changelog/2026-07-24-opening-a-share-link-as-the-clip-s-owner-now-goes-straight-t.md +6 -0
  131. package/corpus/templates/clips/changelog/2026-07-24-permissions-and-rewind-controls-now-use-compact-consistent-h.md +6 -0
  132. package/corpus/templates/clips/changelog/2026-07-24-quitting-the-desktop-app-no-longer-shows-a-false-macos-crash.md +6 -0
  133. package/corpus/templates/clips/changelog/2026-07-24-screen-recording-permission-errors-now-name-the-exact-app-to.md +6 -0
  134. package/corpus/templates/clips/changelog/2026-07-24-the-desktop-app-now-copies-a-new-clip-s-share-link-to-your-c.md +6 -0
  135. package/corpus/templates/clips/changelog/2026-07-24-the-recording-toolbar-now-stays-above-other-apps-including-o.md +6 -0
  136. package/corpus/templates/clips/changelog/2026-07-24-transcript-captions-now-use-real-speech-timings-instead-of-a.md +6 -0
  137. package/corpus/templates/clips/changelog/2026-07-24-video-playback-no-longer-stutters-and-replays-the-last-momen.md +6 -0
  138. package/corpus/templates/clips/chrome-extension/src/background.ts +17 -1
  139. package/corpus/templates/clips/desktop/package.json +2 -0
  140. package/corpus/templates/clips/desktop/src/app.tsx +89 -0
  141. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +9 -3
  142. package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +51 -0
  143. package/corpus/templates/clips/desktop/src/lib/native-notification.ts +44 -0
  144. package/corpus/templates/clips/desktop/src/lib/recording-link.ts +67 -0
  145. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +35 -35
  146. package/corpus/templates/clips/desktop/src/overlays/live-transcript.tsx +3 -2
  147. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +47 -46
  148. package/corpus/templates/clips/desktop/src/styles.css +127 -78
  149. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +7 -0
  150. package/corpus/templates/clips/desktop/src-tauri/Info.plist +6 -0
  151. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +1 -0
  152. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +7 -3
  153. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -1
  154. package/corpus/templates/clips/desktop/src-tauri/src/main.rs +8 -0
  155. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +21 -10
  156. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +40 -0
  157. package/corpus/templates/clips/server/db/schema.ts +25 -0
  158. package/corpus/templates/clips/server/lib/agent-views.ts +145 -0
  159. package/corpus/templates/clips/server/lib/public-agent-context.ts +7 -0
  160. package/corpus/templates/clips/server/lib/recording-share-grant.ts +79 -0
  161. package/corpus/templates/clips/server/lib/recordings.ts +51 -3
  162. package/corpus/templates/clips/server/plugins/db.ts +23 -0
  163. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +49 -17
  164. package/corpus/templates/clips/shared/recording-link.ts +43 -0
  165. package/corpus/templates/clips/shared/share-attribution.ts +4 -0
  166. package/corpus/templates/clips/shared/share-dashboard-redirect.ts +42 -0
  167. package/corpus/templates/clips/shared/view-analytics.ts +26 -0
  168. package/corpus/templates/content/.agents/skills/content/SKILL.md +5 -0
  169. package/corpus/templates/content/.agents/skills/content/references/local-file-mode.md +135 -0
  170. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +12 -0
  171. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +238 -0
  172. package/corpus/templates/content/.agents/skills/document-editing/references/document-behavior.md +91 -0
  173. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  174. package/corpus/templates/content/AGENTS.md +62 -583
  175. package/corpus/templates/content/_gitignore +1 -0
  176. package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +11 -0
  177. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +50 -8
  178. package/corpus/templates/design/.agents/skills/design-generation/references/code-layers.md +81 -0
  179. package/corpus/templates/design/.agents/skills/design-generation/references/code-workspace.md +42 -0
  180. package/corpus/templates/design/.agents/skills/design-generation/references/editor-extensions.md +34 -0
  181. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +4 -1
  182. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  183. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
  184. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +8196 -0
  185. package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +265 -0
  186. package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +642 -0
  187. package/corpus/templates/design/.generated/bridge/motion-preview.generated.ts +571 -0
  188. package/corpus/templates/design/.generated/bridge/nav.generated.ts +91 -0
  189. package/corpus/templates/design/.generated/bridge/sample.generated.ts +23 -0
  190. package/corpus/templates/design/.generated/bridge/shader-fill-preview.generated.ts +263 -0
  191. package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +518 -0
  192. package/corpus/templates/design/.generated/bridge/tweak.generated.ts +20 -0
  193. package/corpus/templates/design/.generated/bridge/zoom.generated.ts +32 -0
  194. package/corpus/templates/design/AGENTS.md +88 -503
  195. package/corpus/templates/design/_gitignore +1 -0
  196. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  197. package/corpus/templates/dispatch/_gitignore +1 -0
  198. package/corpus/templates/dispatch/changelog/2026-07-24-signing-in-now-puts-you-in-your-companys-workspace.md +6 -0
  199. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  200. package/corpus/templates/forms/_gitignore +1 -0
  201. package/corpus/templates/forms/server/lib/public-form-ssr.ts +2 -2
  202. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  203. package/corpus/templates/macros/_gitignore +1 -0
  204. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  205. package/corpus/templates/mail/_gitignore +1 -0
  206. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  207. package/corpus/templates/plan/_gitignore +1 -0
  208. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  209. package/corpus/templates/slides/_gitignore +1 -0
  210. package/corpus/templates/slides/server/plugins/db.ts +4 -0
  211. package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  212. package/corpus/templates/tasks/AGENTS.md +1 -1
  213. package/corpus/templates/tasks/_gitignore +1 -0
  214. package/dist/a2a/activity.d.ts +4 -1
  215. package/dist/a2a/activity.d.ts.map +1 -1
  216. package/dist/a2a/activity.js +27 -16
  217. package/dist/a2a/activity.js.map +1 -1
  218. package/dist/agent/engine/builder-engine.d.ts +1 -0
  219. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  220. package/dist/agent/engine/builder-engine.js +31 -4
  221. package/dist/agent/engine/builder-engine.js.map +1 -1
  222. package/dist/agent/run-manager.d.ts.map +1 -1
  223. package/dist/agent/run-manager.js +18 -1
  224. package/dist/agent/run-manager.js.map +1 -1
  225. package/dist/agent/run-store.d.ts +1 -0
  226. package/dist/agent/run-store.d.ts.map +1 -1
  227. package/dist/agent/run-store.js +2 -1
  228. package/dist/agent/run-store.js.map +1 -1
  229. package/dist/agent/types.d.ts +12 -1
  230. package/dist/agent/types.d.ts.map +1 -1
  231. package/dist/agent/types.js +21 -1
  232. package/dist/agent/types.js.map +1 -1
  233. package/dist/chat-threads/store.d.ts +10 -0
  234. package/dist/chat-threads/store.d.ts.map +1 -1
  235. package/dist/chat-threads/store.js +41 -0
  236. package/dist/chat-threads/store.js.map +1 -1
  237. package/dist/checkpoints/index.d.ts +1 -0
  238. package/dist/checkpoints/index.d.ts.map +1 -1
  239. package/dist/checkpoints/index.js +1 -0
  240. package/dist/checkpoints/index.js.map +1 -1
  241. package/dist/checkpoints/route-match.d.ts +8 -0
  242. package/dist/checkpoints/route-match.d.ts.map +1 -0
  243. package/dist/checkpoints/route-match.js +13 -0
  244. package/dist/checkpoints/route-match.js.map +1 -0
  245. package/dist/cli/create.d.ts +35 -4
  246. package/dist/cli/create.d.ts.map +1 -1
  247. package/dist/cli/create.js +69 -19
  248. package/dist/cli/create.js.map +1 -1
  249. package/dist/cli/index.js +19 -0
  250. package/dist/cli/index.js.map +1 -1
  251. package/dist/cli/template-baseline.d.ts +54 -0
  252. package/dist/cli/template-baseline.d.ts.map +1 -0
  253. package/dist/cli/template-baseline.js +334 -0
  254. package/dist/cli/template-baseline.js.map +1 -0
  255. package/dist/cli/template-sync.d.ts +57 -0
  256. package/dist/cli/template-sync.d.ts.map +1 -0
  257. package/dist/cli/template-sync.js +787 -0
  258. package/dist/cli/template-sync.js.map +1 -0
  259. package/dist/cli/templates-meta.d.ts.map +1 -1
  260. package/dist/cli/templates-meta.js +1 -0
  261. package/dist/cli/templates-meta.js.map +1 -1
  262. package/dist/client/AssistantChat.d.ts +2 -1
  263. package/dist/client/AssistantChat.d.ts.map +1 -1
  264. package/dist/client/AssistantChat.js +38 -10
  265. package/dist/client/AssistantChat.js.map +1 -1
  266. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
  267. package/dist/client/NewWorkspaceAppFlow.js +32 -2
  268. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  269. package/dist/client/chat/message-components.d.ts +20 -0
  270. package/dist/client/chat/message-components.d.ts.map +1 -1
  271. package/dist/client/chat/message-components.js +81 -46
  272. package/dist/client/chat/message-components.js.map +1 -1
  273. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  274. package/dist/client/chat/tool-call-display.js +14 -6
  275. package/dist/client/chat/tool-call-display.js.map +1 -1
  276. package/dist/client/error-format.d.ts.map +1 -1
  277. package/dist/client/error-format.js +13 -0
  278. package/dist/client/error-format.js.map +1 -1
  279. package/dist/client/org/OrgSwitcher.d.ts +8 -1
  280. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  281. package/dist/client/org/OrgSwitcher.js +3 -2
  282. package/dist/client/org/OrgSwitcher.js.map +1 -1
  283. package/dist/client/org/TeamPage.d.ts.map +1 -1
  284. package/dist/client/org/TeamPage.js +1 -1
  285. package/dist/client/org/TeamPage.js.map +1 -1
  286. package/dist/client/sse-event-processor.d.ts.map +1 -1
  287. package/dist/client/sse-event-processor.js +3 -0
  288. package/dist/client/sse-event-processor.js.map +1 -1
  289. package/dist/collab/awareness.d.ts +2 -2
  290. package/dist/collab/awareness.d.ts.map +1 -1
  291. package/dist/data-widgets/index.d.ts +3 -0
  292. package/dist/data-widgets/index.d.ts.map +1 -1
  293. package/dist/data-widgets/index.js +33 -0
  294. package/dist/data-widgets/index.js.map +1 -1
  295. package/dist/deploy/build.d.ts.map +1 -1
  296. package/dist/deploy/build.js +11 -7
  297. package/dist/deploy/build.js.map +1 -1
  298. package/dist/eject/provider-api-definitions.d.ts +1 -0
  299. package/dist/eject/provider-api-definitions.d.ts.map +1 -1
  300. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  301. package/dist/integrations/webhook-handler.js +46 -9
  302. package/dist/integrations/webhook-handler.js.map +1 -1
  303. package/dist/notifications/routes.d.ts +3 -3
  304. package/dist/observability/routes.d.ts +3 -3
  305. package/dist/org/auto-join-domain.d.ts +3 -2
  306. package/dist/org/auto-join-domain.d.ts.map +1 -1
  307. package/dist/org/auto-join-domain.js +1 -1
  308. package/dist/org/auto-join-domain.js.map +1 -1
  309. package/dist/org/context.d.ts.map +1 -1
  310. package/dist/org/context.js +84 -28
  311. package/dist/org/context.js.map +1 -1
  312. package/dist/org/index.d.ts +1 -0
  313. package/dist/org/index.d.ts.map +1 -1
  314. package/dist/org/index.js +1 -0
  315. package/dist/org/index.js.map +1 -1
  316. package/dist/org/service-identity.d.ts +43 -0
  317. package/dist/org/service-identity.d.ts.map +1 -0
  318. package/dist/org/service-identity.js +34 -0
  319. package/dist/org/service-identity.js.map +1 -0
  320. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  321. package/dist/provider-api/actions/provider-api.d.ts +13 -11
  322. package/dist/provider-api/actions/provider-api.d.ts.map +1 -1
  323. package/dist/provider-api/index.d.ts +4 -0
  324. package/dist/provider-api/index.d.ts.map +1 -1
  325. package/dist/provider-api/index.js +17 -1
  326. package/dist/provider-api/index.js.map +1 -1
  327. package/dist/resources/handlers.d.ts +1 -1
  328. package/dist/scripts/docs/search.d.ts.map +1 -1
  329. package/dist/scripts/docs/search.js +12 -1
  330. package/dist/scripts/docs/search.js.map +1 -1
  331. package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
  332. package/dist/server/agent-chat/context-tools.js +3 -3
  333. package/dist/server/agent-chat/context-tools.js.map +1 -1
  334. package/dist/server/agent-chat/framework-prompts.d.ts +8 -3
  335. package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -1
  336. package/dist/server/agent-chat/framework-prompts.js +33 -62
  337. package/dist/server/agent-chat/framework-prompts.js.map +1 -1
  338. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  339. package/dist/server/agent-chat-plugin.js +16 -10
  340. package/dist/server/agent-chat-plugin.js.map +1 -1
  341. package/dist/server/agents-bundle.d.ts +18 -0
  342. package/dist/server/agents-bundle.d.ts.map +1 -1
  343. package/dist/server/agents-bundle.js +70 -4
  344. package/dist/server/agents-bundle.js.map +1 -1
  345. package/dist/server/auth.d.ts.map +1 -1
  346. package/dist/server/auth.js +4 -3
  347. package/dist/server/auth.js.map +1 -1
  348. package/dist/server/builder-browser.d.ts.map +1 -1
  349. package/dist/server/builder-browser.js +43 -19
  350. package/dist/server/builder-browser.js.map +1 -1
  351. package/dist/server/core-routes-plugin.js +2 -2
  352. package/dist/server/core-routes-plugin.js.map +1 -1
  353. package/dist/server/credential-provider.d.ts +38 -2
  354. package/dist/server/credential-provider.d.ts.map +1 -1
  355. package/dist/server/credential-provider.js +142 -64
  356. package/dist/server/credential-provider.js.map +1 -1
  357. package/dist/server/index.d.ts +1 -1
  358. package/dist/server/index.d.ts.map +1 -1
  359. package/dist/server/index.js +1 -1
  360. package/dist/server/index.js.map +1 -1
  361. package/dist/server/prompts/framework-core-compact.d.ts +1 -1
  362. package/dist/server/prompts/framework-core-compact.d.ts.map +1 -1
  363. package/dist/server/prompts/framework-core-compact.js +7 -13
  364. package/dist/server/prompts/framework-core-compact.js.map +1 -1
  365. package/dist/server/prompts/framework-core.d.ts +1 -1
  366. package/dist/server/prompts/framework-core.d.ts.map +1 -1
  367. package/dist/server/prompts/framework-core.js +10 -21
  368. package/dist/server/prompts/framework-core.js.map +1 -1
  369. package/dist/server/prompts/index.d.ts +1 -1
  370. package/dist/server/prompts/index.d.ts.map +1 -1
  371. package/dist/server/prompts/index.js +1 -1
  372. package/dist/server/prompts/index.js.map +1 -1
  373. package/dist/server/prompts/shared-rules.d.ts +16 -10
  374. package/dist/server/prompts/shared-rules.d.ts.map +1 -1
  375. package/dist/server/prompts/shared-rules.js +19 -10
  376. package/dist/server/prompts/shared-rules.js.map +1 -1
  377. package/dist/server/ssr-handler.d.ts +1 -1
  378. package/dist/server/ssr-handler.d.ts.map +1 -1
  379. package/dist/server/ssr-handler.js +10 -3
  380. package/dist/server/ssr-handler.js.map +1 -1
  381. package/dist/server/transcribe-voice.d.ts +1 -1
  382. package/dist/settings/user-settings.d.ts.map +1 -1
  383. package/dist/settings/user-settings.js +13 -1
  384. package/dist/settings/user-settings.js.map +1 -1
  385. package/dist/shared/cache-control.d.ts +52 -0
  386. package/dist/shared/cache-control.d.ts.map +1 -1
  387. package/dist/shared/cache-control.js +107 -0
  388. package/dist/shared/cache-control.js.map +1 -1
  389. package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  390. package/dist/templates/chat/_gitignore +1 -0
  391. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  392. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  393. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
  394. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  395. package/dist/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
  396. package/dist/vite/agents-bundle-plugin.d.ts.map +1 -1
  397. package/dist/vite/agents-bundle-plugin.js +8 -7
  398. package/dist/vite/agents-bundle-plugin.js.map +1 -1
  399. package/dist/vite/client.d.ts.map +1 -1
  400. package/dist/vite/client.js +2 -0
  401. package/dist/vite/client.js.map +1 -1
  402. package/docs/content/actions.mdx +2 -2
  403. package/docs/content/agent-surfaces.mdx +1 -1
  404. package/docs/content/authentication.mdx +2 -0
  405. package/docs/content/deployment.mdx +36 -0
  406. package/docs/content/getting-started.mdx +477 -115
  407. package/docs/content/http-api.mdx +266 -0
  408. package/docs/content/syncing-template-changes.mdx +159 -0
  409. package/package.json +4 -3
  410. package/src/a2a/activity.ts +38 -27
  411. package/src/agent/engine/builder-engine.ts +35 -4
  412. package/src/agent/run-manager.ts +20 -1
  413. package/src/agent/run-store.ts +4 -1
  414. package/src/agent/types.ts +28 -8
  415. package/src/chat-threads/store.ts +45 -0
  416. package/src/checkpoints/index.ts +2 -0
  417. package/src/checkpoints/route-match.ts +13 -0
  418. package/src/cli/create.ts +105 -17
  419. package/src/cli/index.ts +20 -0
  420. package/src/cli/template-baseline.ts +410 -0
  421. package/src/cli/template-sync.ts +1004 -0
  422. package/src/cli/templates-meta.ts +1 -0
  423. package/src/client/AssistantChat.tsx +38 -12
  424. package/src/client/NewWorkspaceAppFlow.tsx +75 -10
  425. package/src/client/chat/message-components.tsx +127 -66
  426. package/src/client/chat/tool-call-display.tsx +29 -5
  427. package/src/client/error-format.ts +18 -0
  428. package/src/client/org/OrgSwitcher.tsx +30 -8
  429. package/src/client/org/TeamPage.tsx +7 -6
  430. package/src/client/sse-event-processor.ts +3 -0
  431. package/src/data-widgets/index.ts +41 -0
  432. package/src/deploy/build.ts +11 -9
  433. package/src/integrations/webhook-handler.ts +63 -9
  434. package/src/org/auto-join-domain.ts +4 -3
  435. package/src/org/context.ts +108 -39
  436. package/src/org/index.ts +5 -0
  437. package/src/org/service-identity.ts +60 -0
  438. package/src/provider-api/index.ts +22 -1
  439. package/src/scripts/docs/search.ts +12 -1
  440. package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +25 -36
  441. package/src/server/agent-chat/context-tools.ts +9 -4
  442. package/src/server/agent-chat/framework-prompts.ts +38 -66
  443. package/src/server/agent-chat-plugin.ts +17 -10
  444. package/src/server/agents-bundle.ts +84 -5
  445. package/src/server/auth.ts +4 -3
  446. package/src/server/builder-browser.ts +47 -19
  447. package/src/server/core-routes-plugin.ts +2 -2
  448. package/src/server/credential-provider.ts +209 -68
  449. package/src/server/index.ts +3 -0
  450. package/src/server/prompts/framework-core-compact.ts +6 -13
  451. package/src/server/prompts/framework-core.ts +9 -21
  452. package/src/server/prompts/index.ts +0 -1
  453. package/src/server/prompts/shared-rules.ts +19 -11
  454. package/src/server/ssr-handler.ts +13 -2
  455. package/src/settings/user-settings.ts +13 -1
  456. package/src/shared/cache-control.ts +141 -0
  457. package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  458. package/src/templates/chat/_gitignore +1 -0
  459. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  460. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  461. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
  462. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  463. package/src/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
  464. package/src/vite/agents-bundle-plugin.ts +7 -6
  465. package/src/vite/client.ts +5 -0
@@ -625,6 +625,9 @@ function isAutoRecoverableError(ev: SSEEvent, errMsg: string): boolean {
625
625
  code === "unauthorized" ||
626
626
  code === "authentication_error" ||
627
627
  code === "permission_error" ||
628
+ code === "builder_auth_error" ||
629
+ // The account cannot use this model; another POST picks the same one.
630
+ code === "builder_model_unauthorized" ||
628
631
  code === "http_401" ||
629
632
  code === "http_403" ||
630
633
  code === "rate_limit_exceeded" ||
@@ -282,6 +282,47 @@ export function isDataWidgetResult(value: unknown): value is DataWidgetResult {
282
282
  return normalizeDataWidgetResult(value) !== null;
283
283
  }
284
284
 
285
+ export const DATA_WIDGET_MAX_ROWS = 50;
286
+ export const DATA_WIDGET_MAX_CHART_POINTS = 200;
287
+
288
+ // Widget rows arrive as tool-call arguments the model decodes token by token, so an
289
+ // uncapped table costs minutes of wall clock before anything renders. Clamp server-side
290
+ // rather than rejecting: the model has already paid for the tokens by the time we see them.
291
+ export function clampDataWidgetRows(
292
+ result: DataWidgetResult,
293
+ ): DataWidgetResult {
294
+ const source = result as Partial<DataInsightsWidgetResult>;
295
+ const table = source.table;
296
+ const chartSeries = source.chartSeries;
297
+
298
+ const clampedTable =
299
+ table && table.rows.length > DATA_WIDGET_MAX_ROWS
300
+ ? {
301
+ ...table,
302
+ rows: table.rows.slice(0, DATA_WIDGET_MAX_ROWS),
303
+ totalRows: table.totalRows ?? table.rows.length,
304
+ sampledRows: DATA_WIDGET_MAX_ROWS,
305
+ truncated: true,
306
+ }
307
+ : table;
308
+
309
+ const clampedChart =
310
+ chartSeries && chartSeries.data.length > DATA_WIDGET_MAX_CHART_POINTS
311
+ ? {
312
+ ...chartSeries,
313
+ data: chartSeries.data.slice(0, DATA_WIDGET_MAX_CHART_POINTS),
314
+ sampled: true,
315
+ }
316
+ : chartSeries;
317
+
318
+ if (clampedTable === table && clampedChart === chartSeries) return result;
319
+ return {
320
+ ...result,
321
+ ...(clampedTable ? { table: clampedTable } : {}),
322
+ ...(clampedChart ? { chartSeries: clampedChart } : {}),
323
+ } as DataWidgetResult;
324
+ }
325
+
285
326
  const dataTableColumnSchema = z
286
327
  .object({
287
328
  key: z.string().min(1),
@@ -36,10 +36,8 @@ import {
36
36
  } from "../integrations/integration-durable-dispatch-config.js";
37
37
  import { normalizeAppBasePath } from "../server/app-base-path.js";
38
38
  import {
39
- DEFAULT_SSR_CDN_CACHE_CONTROL,
40
- DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL,
41
39
  DEFAULT_SPECULATION_RULES_PATH,
42
- DEFAULT_SSR_CACHE_CONTROL,
40
+ resolveSsrCacheHeaders,
43
41
  } from "../shared/cache-control.js";
44
42
  import { mcpEmbedStaticAssetRouteRules } from "../shared/mcp-embed-headers.js";
45
43
  import {
@@ -698,6 +696,9 @@ export function generateWorkerEntry(
698
696
  options: GenerateWorkerEntryOptions = {},
699
697
  ): string {
700
698
  const includeReactRouterSsr = options.includeReactRouterSsr ?? true;
699
+ // The worker ships as a static bundle with no access to runtime env, so the
700
+ // deployment-wide SSR cache policy is baked in from this build's env.
701
+ const ssrCacheHeaders = resolveSsrCacheHeaders();
701
702
  const routeImports: string[] = [];
702
703
  const routeRegistrations: string[] = [];
703
704
 
@@ -984,9 +985,10 @@ function injectHeadScript(html, script) {
984
985
  return html.slice(0, headCloseIdx) + script + html.slice(headCloseIdx);
985
986
  }
986
987
 
987
- const DEFAULT_SSR_CACHE_CONTROL = ${JSON.stringify(DEFAULT_SSR_CACHE_CONTROL)};
988
- const DEFAULT_SSR_CDN_CACHE_CONTROL = ${JSON.stringify(DEFAULT_SSR_CDN_CACHE_CONTROL)};
989
- const DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL = ${JSON.stringify(DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL)};
988
+ // Resolved from AGENT_NATIVE_SSR_CACHE at build time.
989
+ const SSR_CACHE_CONTROL = ${JSON.stringify(ssrCacheHeaders["cache-control"])};
990
+ const SSR_CDN_CACHE_CONTROL = ${JSON.stringify(ssrCacheHeaders["cdn-cache-control"])};
991
+ const SSR_NETLIFY_CDN_CACHE_CONTROL = ${JSON.stringify(ssrCacheHeaders["netlify-cdn-cache-control"])};
990
992
  const DEFAULT_SPECULATION_RULES_PATH = ${JSON.stringify(DEFAULT_SPECULATION_RULES_PATH)};
991
993
  const IMMUTABLE_ASSET_CACHE_CONTROL = ${JSON.stringify(IMMUTABLE_ASSET_CACHE_CONTROL)};
992
994
  const IMMUTABLE_ASSET_PATHS = new Set(${JSON.stringify(
@@ -1084,13 +1086,13 @@ function applyDefaultSsrCacheHeader(headers, status, pathname) {
1084
1086
  else headers.delete("vary");
1085
1087
  }
1086
1088
 
1087
- headers.set("cache-control", DEFAULT_SSR_CACHE_CONTROL);
1088
- headers.set("cdn-cache-control", DEFAULT_SSR_CDN_CACHE_CONTROL);
1089
+ headers.set("cache-control", SSR_CACHE_CONTROL);
1090
+ headers.set("cdn-cache-control", SSR_CDN_CACHE_CONTROL);
1089
1091
  // Netlify function responses are dynamic by default and can otherwise show
1090
1092
  // Cache-Status fwd=bypass even with Cache-Control: public. Keep this
1091
1093
  // Netlify-specific header so SSR HTML/.data are served from the shared
1092
1094
  // durable CDN cache instead of stampeding origin — for every visitor.
1093
- headers.set("netlify-cdn-cache-control", DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL);
1095
+ headers.set("netlify-cdn-cache-control", SSR_NETLIFY_CDN_CACHE_CONTROL);
1094
1096
  }
1095
1097
 
1096
1098
  function applyDefaultSpeculationRulesHeader(headers, status, basePath) {
@@ -8,6 +8,7 @@ import {
8
8
  type A2AToolResultSummary,
9
9
  } from "../a2a/artifact-response.js";
10
10
  import { collectFinalResponseTextFromAgentEvents } from "../a2a/response-text.js";
11
+ import { isInBackgroundFunctionRuntime } from "../agent/durable-background.js";
11
12
  import {
12
13
  formatLlmCredentialErrorMessage,
13
14
  isLlmCredentialError,
@@ -41,7 +42,11 @@ import {
41
42
  threadDataToEngineMessages,
42
43
  } from "../agent/thread-data-builder.js";
43
44
  import { attachToolSearch } from "../agent/tool-search.js";
44
- import { createThread, getThread } from "../chat-threads/store.js";
45
+ import {
46
+ createThread,
47
+ getThread,
48
+ grantThreadUserShare,
49
+ } from "../chat-threads/store.js";
45
50
  import { updateThreadData } from "../chat-threads/store.js";
46
51
  import { isLocalDatabase } from "../db/client.js";
47
52
  import { getOrgA2ASecret, resolveOrgIdForEmail } from "../org/context.js";
@@ -88,6 +93,20 @@ const DEFERRED_RESPONSE_DISPATCH_SETTLE_WAIT_MS = 1_500;
88
93
  const DEFERRED_RESPONSE_MAX_HANDLER_MS = 2_500;
89
94
  const EMPTY_INTEGRATION_RESPONSE_MESSAGE =
90
95
  "The model finished without a visible answer. Try again, or open the thread in Dispatch to inspect the run.";
96
+ const CUTOFF_INTEGRATION_RESPONSE_MESSAGE =
97
+ "I ran out of time on this one before I could write up an answer — it needed more research than a single run allows. " +
98
+ "Open the thread in Dispatch to see what I gathered, or ask me again in smaller pieces (one source at a time works best).";
99
+
100
+ /**
101
+ * True when the run stopped at a continuation boundary rather than finishing:
102
+ * the run-manager's soft timeout emits `auto_continue` and aborts the loop, and
103
+ * the loop itself emits one when it hits an internal step budget. The run's
104
+ * status is still "completed", so without this check a cut-off research request
105
+ * is reported to the user as a model that answered with nothing.
106
+ */
107
+ function endedAtContinuationBoundary(run: ActiveRun): boolean {
108
+ return run.events.some((runEvent) => runEvent.event.type === "auto_continue");
109
+ }
91
110
 
92
111
  type ToolDoneEvent = {
93
112
  type: "tool_done";
@@ -789,6 +808,29 @@ async function processIncomingMessage(
789
808
  await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
790
809
  throw error;
791
810
  }
811
+
812
+ // Channel conversations run as the integration service principal, so the
813
+ // thread is owned by `integration@<platform>` and not by the human who asked.
814
+ // The "Open thread" deep link we hand back would then 404 for them and the
815
+ // chat surface would silently render an empty new chat, so grant each
816
+ // verified participant an explicit share on the thread they are driving.
817
+ if (
818
+ incoming.senderVerified === true &&
819
+ incoming.senderEmail &&
820
+ incoming.senderEmail.trim().toLowerCase() !== ownerEmail.toLowerCase()
821
+ ) {
822
+ await grantThreadUserShare(
823
+ threadId,
824
+ incoming.senderEmail,
825
+ "editor",
826
+ ownerEmail,
827
+ ).catch((error) => {
828
+ console.warn(
829
+ `[integrations] Could not grant ${incoming.platform} sender access to thread ${threadId}:`,
830
+ error instanceof Error ? error.message : error,
831
+ );
832
+ });
833
+ }
792
834
  const existingMessages: EngineMessage[] = [];
793
835
  if (thread?.threadData) {
794
836
  existingMessages.push(...threadDataToEngineMessages(thread.threadData));
@@ -1051,7 +1093,10 @@ async function processIncomingMessage(
1051
1093
  "directly is the most reliable way to get an answer right now.";
1052
1094
  } else {
1053
1095
  responseText =
1054
- verifiedMutationReceipt ?? EMPTY_INTEGRATION_RESPONSE_MESSAGE;
1096
+ verifiedMutationReceipt ??
1097
+ (endedAtContinuationBoundary(completedRun)
1098
+ ? CUTOFF_INTEGRATION_RESPONSE_MESSAGE
1099
+ : EMPTY_INTEGRATION_RESPONSE_MESSAGE);
1055
1100
  }
1056
1101
  }
1057
1102
  if (approval?.type === "approval_required") {
@@ -1346,13 +1391,22 @@ async function processIncomingMessage(
1346
1391
  resolve(outcome);
1347
1392
  }
1348
1393
  },
1349
- // Integration workers are ordinary self-dispatched serverless requests,
1350
- // not a Netlify background-function route. Without the hosted soft
1351
- // timeout, a wedged model connection can outlive the worker and leave
1352
- // Slack's native stream in "working" forever when the host kills the
1353
- // process. Checkpoint at the foreground-safe boundary so onComplete can
1354
- // always close the provider progress surface before the function wall.
1355
- { useHostedSoftTimeoutDefault: true },
1394
+ // Without the hosted soft timeout, a wedged model connection can outlive
1395
+ // the worker and leave Slack's native stream in "working" forever when
1396
+ // the host kills the process. Checkpoint at the safe boundary so
1397
+ // onComplete can always close the provider progress surface first.
1398
+ //
1399
+ // Which boundary that is depends on where this task is actually running:
1400
+ // when durable dispatch routes it to the emitted `-background` function
1401
+ // it has ~15min, and clamping it to the 40s foreground budget anyway cut
1402
+ // off every research-shaped request (multi-source sweeps, long tool
1403
+ // chains) with no visible answer. `isInBackgroundFunctionRuntime()` is
1404
+ // the runtime proof — never a config guess — so the wider ceiling is
1405
+ // taken only where the ~60s synchronous wall genuinely does not apply.
1406
+ {
1407
+ useHostedSoftTimeoutDefault: true,
1408
+ backgroundFunction: isInBackgroundFunctionRuntime(),
1409
+ },
1356
1410
  );
1357
1411
  });
1358
1412
  }
@@ -14,9 +14,10 @@ export interface AutoJoinDomainOptions {
14
14
  /**
15
15
  * The signup hook should not clobber an org selected by an invite flow, but
16
16
  * request-time org resolution may need to move an existing account from a
17
- * personal workspace into its newly matched company org.
17
+ * personal workspace into its newly matched company org. `"never"` joins
18
+ * without touching `active-org-id` — the caller decides activation itself.
18
19
  */
19
- activateJoinedOrg?: "if-missing" | "always";
20
+ activateJoinedOrg?: "if-missing" | "always" | "never";
20
21
  }
21
22
 
22
23
  /**
@@ -96,7 +97,7 @@ export async function autoJoinDomainMatchingOrgs(
96
97
  // one, unless the caller is request-time org resolution intentionally moving
97
98
  // an existing account into its newly matched company org.
98
99
  let activeOrgId: string | null = null;
99
- if (joined[0]) {
100
+ if (joined[0] && options.activateJoinedOrg !== "never") {
100
101
  try {
101
102
  const existing = await getUserSetting(email, "active-org-id");
102
103
  const hasActive = Boolean(existing?.orgId);
@@ -20,12 +20,33 @@ function normalizeOrgRole(value: unknown): OrgRole | null {
20
20
  : null;
21
21
  }
22
22
 
23
- function isLikelyPersonalWorkspace(
24
- membership: { orgName: string },
25
- email: string,
26
- session: { name?: string } | null,
27
- ): boolean {
28
- return membership.orgName.trim() === defaultOrgName(email, session);
23
+ function emailDomainOf(email: string): string | null {
24
+ return email.split("@")[1]?.toLowerCase() || null;
25
+ }
26
+
27
+ /**
28
+ * A workspace the user owns that nobody else has joined is a default/personal
29
+ * workspace whatever it happens to be named, so moving the user into their
30
+ * company org is safe. An org with other members is a team they deliberately
31
+ * belong to — join the domain org in the background but leave them there.
32
+ */
33
+ async function isSoloOwnedWorkspace(
34
+ exec: ReturnType<typeof getDbExec>,
35
+ orgId: string,
36
+ memberships: MembershipRow[],
37
+ ): Promise<boolean> {
38
+ const membership = memberships.find((m) => m.orgId === orgId);
39
+ if (!membership || membership.role !== "owner") return false;
40
+ try {
41
+ const { rows } = await exec.execute({
42
+ sql: `SELECT COUNT(*) AS "memberCount" FROM org_members WHERE org_id = ?`,
43
+ args: [orgId],
44
+ });
45
+ const row = rows[0] as any;
46
+ return Number(row?.memberCount ?? row?.membercount ?? 0) <= 1;
47
+ } catch {
48
+ return false;
49
+ }
29
50
  }
30
51
 
31
52
  function autoCreateDefaultOrgEnabled(): boolean {
@@ -61,7 +82,12 @@ export async function getOrgContext(event: H3Event): Promise<OrgContext> {
61
82
  return (ctx.__anOrgContextCache ??= resolveOrgContextUncached(event));
62
83
  }
63
84
 
64
- type MembershipRow = { orgId: string; role: OrgRole; orgName: string };
85
+ type MembershipRow = {
86
+ orgId: string;
87
+ role: OrgRole;
88
+ orgName: string;
89
+ allowedDomain: string | null;
90
+ };
65
91
 
66
92
  const MEMBERSHIPS_CACHE_KEY = "__anOrgMembershipsCache";
67
93
  const ACTIVE_ORG_SETTING_CACHE_KEY = "__anActiveOrgSettingCache";
@@ -157,17 +183,26 @@ async function resolveOrgContextUncached(event: H3Event): Promise<OrgContext> {
157
183
  return { email, orgId: null, orgName: null, role: null };
158
184
  }
159
185
 
160
- const shouldTryDomainAutoJoin =
161
- memberships.length === 0 ||
162
- (memberships.length === 1 &&
163
- isLikelyPersonalWorkspace(memberships[0], email, session));
164
186
  const activeOrgSetting = await loadActiveOrgSettingForEvent(event, email);
165
- if (activeOrgSetting?.orgId === null) {
166
- return { email, orgId: null, orgName: null, role: null };
167
- }
168
- if (activeOrgSetting?.orgId) {
187
+ const explicitPersonal = activeOrgSetting?.orgId === null;
188
+
189
+ const emailDomain = emailDomainOf(email);
190
+ // Membership in a domain-matched org is the only durable "already joined"
191
+ // signal. Recognizing the personal workspace by its *name* instead breaks the
192
+ // moment the provider display name or the workspace name changes, which
193
+ // strands an existing user in Personal with no way into their company org.
194
+ const shouldTryDomainAutoJoin =
195
+ !explicitPersonal &&
196
+ emailDomain !== null &&
197
+ !memberships.some((m) => m.allowedDomain?.toLowerCase() === emailDomain);
198
+
199
+ if (
200
+ !explicitPersonal &&
201
+ activeOrgSetting?.orgId &&
202
+ !shouldTryDomainAutoJoin
203
+ ) {
169
204
  const active = memberships.find((m) => m.orgId === activeOrgSetting.orgId);
170
- if (active && !shouldTryDomainAutoJoin) {
205
+ if (active) {
171
206
  return {
172
207
  email,
173
208
  orgId: active.orgId,
@@ -182,30 +217,63 @@ async function resolveOrgContextUncached(event: H3Event): Promise<OrgContext> {
182
217
  : null;
183
218
 
184
219
  if (shouldTryDomainAutoJoin) {
220
+ const membershipsBeforeJoin = memberships;
185
221
  const joined = await autoJoinDomainMatchingOrgs(email, {
186
- activateJoinedOrg: "always",
222
+ activateJoinedOrg: "never",
187
223
  });
188
- if (joined.joined.length > 0) {
224
+ const joinedOrgId = joined.joined[0]?.orgId ?? null;
225
+ if (joinedOrgId) {
189
226
  const refreshed = await loadMemberships(exec, email);
190
227
  if (refreshed !== null) {
191
228
  memberships = refreshed;
192
229
  updateMembershipsForEvent(event, email, refreshed);
193
230
  }
194
- }
195
231
 
196
- if (joined.activeOrgId) {
197
- const active = memberships.find((m) => m.orgId === joined.activeOrgId);
198
- if (active) {
199
- return {
200
- email,
201
- orgId: active.orgId,
202
- orgName: active.orgName,
203
- role: active.role,
204
- };
232
+ const currentOrgId =
233
+ activeOrgSetting?.orgId ??
234
+ sessionOrgId ??
235
+ membershipsBeforeJoin[0]?.orgId ??
236
+ null;
237
+ const shouldActivate =
238
+ !explicitPersonal &&
239
+ (currentOrgId === null ||
240
+ (await isSoloOwnedWorkspace(
241
+ exec,
242
+ currentOrgId,
243
+ membershipsBeforeJoin,
244
+ )));
245
+
246
+ if (shouldActivate) {
247
+ await putUserSetting(email, "active-org-id", { orgId: joinedOrgId });
248
+ const active = memberships.find((m) => m.orgId === joinedOrgId);
249
+ if (active) {
250
+ return {
251
+ email,
252
+ orgId: active.orgId,
253
+ orgName: active.orgName,
254
+ role: active.role,
255
+ };
256
+ }
205
257
  }
206
258
  }
207
259
  }
208
260
 
261
+ if (explicitPersonal) {
262
+ return { email, orgId: null, orgName: null, role: null };
263
+ }
264
+
265
+ if (activeOrgSetting?.orgId) {
266
+ const active = memberships.find((m) => m.orgId === activeOrgSetting.orgId);
267
+ if (active) {
268
+ return {
269
+ email,
270
+ orgId: active.orgId,
271
+ orgName: active.orgName,
272
+ role: active.role,
273
+ };
274
+ }
275
+ }
276
+
209
277
  if (sessionOrgId) {
210
278
  const active =
211
279
  sessionMembership ?? memberships.find((m) => m.orgId === sessionOrgId);
@@ -247,24 +315,25 @@ async function resolveOrgContextUncached(event: H3Event): Promise<OrgContext> {
247
315
  async function loadMemberships(
248
316
  exec: ReturnType<typeof getDbExec>,
249
317
  email: string,
250
- ): Promise<Array<{
251
- orgId: string;
252
- role: OrgRole;
253
- orgName: string;
254
- }> | null> {
318
+ ): Promise<MembershipRow[] | null> {
255
319
  try {
256
320
  const { rows } = await exec.execute({
257
- sql: `SELECT m.org_id AS "orgId", m.role AS role, o.name AS "orgName"
321
+ sql: `SELECT m.org_id AS "orgId", m.role AS role, o.name AS "orgName",
322
+ o.allowed_domain AS "allowedDomain"
258
323
  FROM org_members m
259
324
  INNER JOIN organizations o ON m.org_id = o.id
260
325
  WHERE LOWER(m.email) = ?`,
261
326
  args: [email.toLowerCase()],
262
327
  });
263
- return rows.map((r: any) => ({
264
- orgId: String(r.orgId ?? r.org_id),
265
- role: String(r.role) as OrgRole,
266
- orgName: String(r.orgName ?? r.org_name),
267
- }));
328
+ return rows.map((r: any) => {
329
+ const domain = r.allowedDomain ?? r.allowed_domain;
330
+ return {
331
+ orgId: String(r.orgId ?? r.org_id),
332
+ role: String(r.role) as OrgRole,
333
+ orgName: String(r.orgName ?? r.org_name),
334
+ allowedDomain: domain ? String(domain) : null,
335
+ };
336
+ });
268
337
  } catch {
269
338
  // Tables may not exist yet on first boot before migrations finish.
270
339
  return null;
package/src/org/index.ts CHANGED
@@ -28,6 +28,11 @@ export {
28
28
  createOrganization,
29
29
  } from "./context.js";
30
30
 
31
+ export {
32
+ implicitServiceOrgRole,
33
+ parseServiceIdentityEmail,
34
+ } from "./service-identity.js";
35
+
31
36
  export { acceptPendingInvitationsForEmail } from "./accept-pending.js";
32
37
  export type { AcceptPendingResult } from "./accept-pending.js";
33
38
 
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Implicit org membership for synthetic service-token identities.
3
+ *
4
+ * Org service tokens authenticate as `svc-<name>@service.<orgId>` and are
5
+ * deliberately never inserted into `org_members` (see
6
+ * `mcp/actions/service-token-access.ts`), so any template that authorizes by
7
+ * looking up a physical membership row rejects them. The ownable/sharing layer
8
+ * already admits them via the signed `org_id` claim; this closes the gap for
9
+ * the role-lookup layer.
10
+ *
11
+ * Two constraints are load-bearing and must survive future edits:
12
+ *
13
+ * 1. The implicit role is always `member`, never admin or owner. Member-gated
14
+ * actions (create/read org data) work; admin-gated ones (invite/remove
15
+ * members, change roles, org settings, minting further service tokens) stay
16
+ * closed, so a leaked service token cannot escalate.
17
+ * 2. `requestOrgId` must independently agree with the target org. The email
18
+ * alone is not proof: a human account registered under a `@service.<orgId>`
19
+ * address would otherwise mint itself membership. A signed-in user's request
20
+ * org id is membership-derived through `getOrgContext`, so a non-member
21
+ * cannot present the target org id, while a service token carries it in its
22
+ * signed `org_id` claim.
23
+ */
24
+ import type { OrgRole } from "./types.js";
25
+
26
+ const SERVICE_IDENTITY_PATTERN = /^svc-([a-z0-9-]+)@service\.(.+)$/;
27
+
28
+ /**
29
+ * Parse `svc-<name>@service.<orgId>` into its parts, or null when the address
30
+ * is not a service identity. Counterpart to `serviceIdentityEmail()` in
31
+ * `mcp/connect-store.ts`.
32
+ */
33
+ export function parseServiceIdentityEmail(
34
+ email: string | null | undefined,
35
+ ): { serviceName: string; orgId: string } | null {
36
+ const trimmed = email?.trim();
37
+ if (!trimmed) return null;
38
+ const match = SERVICE_IDENTITY_PATTERN.exec(trimmed.toLowerCase());
39
+ if (!match) return null;
40
+ const orgId = trimmed.slice(trimmed.length - match[2].length);
41
+ if (!orgId) return null;
42
+ return { serviceName: match[1], orgId };
43
+ }
44
+
45
+ /**
46
+ * Resolve the implicit org role for a service-token caller, or null when the
47
+ * caller is not a service identity acting for the org it was minted against.
48
+ */
49
+ export function implicitServiceOrgRole(params: {
50
+ email: string | null | undefined;
51
+ orgId: string | null | undefined;
52
+ requestOrgId: string | null | undefined;
53
+ }): OrgRole | null {
54
+ const orgId = params.orgId?.trim();
55
+ if (!orgId) return null;
56
+ if (params.requestOrgId?.trim() !== orgId) return null;
57
+ const parsed = parseServiceIdentityEmail(params.email);
58
+ if (!parsed || parsed.orgId !== orgId) return null;
59
+ return "member";
60
+ }
@@ -396,6 +396,7 @@ export interface ProviderApiConfig {
396
396
  placeholders?: readonly ProviderApiPlaceholder[];
397
397
  examples?: readonly ProviderApiExample[];
398
398
  notes?: readonly string[];
399
+ accessErrorGuidance?: string;
399
400
  corpusRecipes?: readonly ProviderApiCorpusRecipe[];
400
401
  templateUses?: readonly WorkspaceConnectionTemplateUse[];
401
402
  }
@@ -1326,6 +1327,11 @@ const PROVIDER_CONFIGS: Record<ProviderApiId, ProviderApiConfig> = {
1326
1327
  defaultHeaders: { "Notion-Version": "2026-03-11" },
1327
1328
  templateUses: ["analytics", "brain", "content", "dispatch"],
1328
1329
  examples: [{ label: "Search", method: "POST", path: "/search", body: {} }],
1330
+ notes: [
1331
+ "The name GET /users/me returns is the label whoever minted the token typed into Notion, so it often names an unrelated product or team. Call it the Notion-side integration label and never restate it as the workspace, app, or product the user is currently in.",
1332
+ ],
1333
+ accessErrorGuidance:
1334
+ "Notion 401/403/404 on a specific page or database usually means that object was never shared with the integration, not that the id is wrong or the key is invalid. Fix: open the page or database in Notion, then ••• → Connections → add the integration. When naming the integration, describe it as the Notion-side integration label from GET /users/me and note that it may not match this app or workspace.",
1329
1335
  corpusRecipes: [
1330
1336
  {
1331
1337
  label: "Search a Notion data source with complete cursor coverage",
@@ -1624,6 +1630,7 @@ export function listProviderApiCatalog(
1624
1630
  defaultHeaders: config.defaultHeaders ?? {},
1625
1631
  examples: config.examples ?? [],
1626
1632
  notes: config.notes ?? [],
1633
+ accessErrorGuidance: config.accessErrorGuidance ?? null,
1627
1634
  corpusRecipes: config.corpusRecipes ?? [],
1628
1635
  templateUses: config.templateUses ?? [],
1629
1636
  }));
@@ -1990,11 +1997,25 @@ export async function executeProviderApiRequest(
1990
1997
  ...(args.connectionId ? { connectionId: args.connectionId } : {}),
1991
1998
  },
1992
1999
  response,
1993
- guidance:
2000
+ guidance: [
1994
2001
  "This was a raw provider API request. Use provider docs/spec URLs to choose endpoints and include method/path/status plus relevant filters in the methodology. Prefer this escape hatch whenever canned actions are too narrow.",
2002
+ providerAccessErrorGuidance(config, response.status),
2003
+ ]
2004
+ .filter(Boolean)
2005
+ .join(" "),
1995
2006
  };
1996
2007
  }
1997
2008
 
2009
+ const PROVIDER_ACCESS_ERROR_STATUSES = new Set([401, 403, 404]);
2010
+
2011
+ function providerAccessErrorGuidance(
2012
+ config: ProviderApiConfig,
2013
+ status: number,
2014
+ ): string | null {
2015
+ if (!PROVIDER_ACCESS_ERROR_STATUSES.has(status)) return null;
2016
+ return config.accessErrorGuidance ?? null;
2017
+ }
2018
+
1998
2019
  /**
1999
2020
  * Resolve a provider OAuth token for a trusted server-side integration bridge
2000
2021
  * such as a CRM adapter or Google Picker. Callers must keep the result out of
@@ -143,7 +143,7 @@ function slugifyDocId(value: string): string {
143
143
 
144
144
  async function loadAgentBundleDocs(): Promise<DocFull[]> {
145
145
  try {
146
- const { loadAgentsBundle, getRuntimeSkills } =
146
+ const { loadAgentsBundle, getRuntimeSkills, skillSubfileDocsSlug } =
147
147
  await import("../../server/agents-bundle.js");
148
148
  const bundle = await loadAgentsBundle();
149
149
  const docs: DocFull[] = [];
@@ -174,6 +174,17 @@ async function loadAgentBundleDocs(): Promise<DocFull[]> {
174
174
  description: skill.meta.description,
175
175
  body: skill.content,
176
176
  });
177
+ // Progressive-disclosure sub-files (e.g. `references/*.md`) get their
178
+ // own searchable/readable doc so the "also contains" pointers the
179
+ // skill prompt block advertises actually resolve to something.
180
+ for (const [relPath, content] of Object.entries(skill.files)) {
181
+ docs.push({
182
+ slug: skillSubfileDocsSlug(skill.meta.name, relPath),
183
+ title: `Skill: ${skill.meta.name} — ${relPath}`,
184
+ description: `Reference file from the "${skill.meta.name}" skill (${relPath}).`,
185
+ body: content,
186
+ });
187
+ }
177
188
  }
178
189
  return docs;
179
190
  } catch {