@agent-native/core 0.127.3 → 0.128.1

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 (457) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +101 -0
  3. package/corpus/core/README.md +5 -26
  4. package/corpus/core/docs/content/actions.mdx +47 -4
  5. package/corpus/core/docs/content/native-chat-ui.mdx +7 -0
  6. package/corpus/core/docs/content/organizations-teams-permissions.mdx +109 -3
  7. package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
  8. package/corpus/core/docs/content/toolkit-org-team.mdx +72 -12
  9. package/corpus/core/package.json +2 -1
  10. package/corpus/core/src/a2a/artifact-response.ts +36 -29
  11. package/corpus/core/src/action.ts +83 -2
  12. package/corpus/core/src/chat-threads/store.ts +48 -43
  13. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
  14. package/corpus/core/src/client/AgentPanel.tsx +26 -6
  15. package/corpus/core/src/client/AssistantChat.tsx +63 -7
  16. package/corpus/core/src/client/DefaultSpinner.tsx +29 -0
  17. package/corpus/core/src/client/org/TeamPage.tsx +161 -6
  18. package/corpus/core/src/client/org/hooks.ts +67 -0
  19. package/corpus/core/src/client/org/index.ts +9 -0
  20. package/corpus/core/src/client/transcription/use-live-transcription.ts +62 -10
  21. package/corpus/core/src/db/client.ts +42 -3
  22. package/corpus/core/src/db/ensure-additive-columns.ts +60 -33
  23. package/corpus/core/src/deploy/build.ts +31 -2
  24. package/corpus/core/src/mcp-client/routes.ts +10 -1
  25. package/corpus/core/src/org/app-roles-handlers.ts +152 -0
  26. package/corpus/core/src/org/app-roles.ts +355 -0
  27. package/corpus/core/src/org/index.ts +27 -1
  28. package/corpus/core/src/org/migrations.ts +22 -0
  29. package/corpus/core/src/org/plugin.ts +27 -0
  30. package/corpus/core/src/org/schema.ts +15 -0
  31. package/corpus/core/src/scripts/docs/search.ts +17 -0
  32. package/corpus/core/src/scripts/docs/source-search.ts +10 -0
  33. package/corpus/core/src/server/agent-chat/script-entries.ts +3 -0
  34. package/corpus/core/src/server/agent-chat-plugin.ts +53 -52
  35. package/corpus/core/src/server/builder-design-systems.ts +299 -82
  36. package/corpus/core/src/server/core-routes-plugin.ts +6 -1
  37. package/corpus/core/src/server/framework-request-handler.ts +44 -6
  38. package/corpus/core/src/server/index.ts +8 -0
  39. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
  40. package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
  41. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +31 -0
  42. package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
  43. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
  44. package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
  45. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
  46. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
  47. package/corpus/core/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
  48. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -0
  49. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +31 -0
  50. package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +4 -0
  51. package/corpus/templates/analytics/_gitignore +2 -0
  52. package/corpus/templates/analytics/actions/data-source-status.ts +21 -12
  53. package/corpus/templates/analytics/app/global.css +7 -7
  54. package/corpus/templates/analytics/app/i18n-data.ts +30 -0
  55. package/corpus/templates/analytics/app/lib/data-source-status.ts +2 -1
  56. package/corpus/templates/analytics/app/pages/DataSources.tsx +54 -10
  57. package/corpus/templates/analytics/changelog/2026-07-28-analytics-pages-load-reliably-with-large-dashboard-workspaces.md +6 -0
  58. package/corpus/templates/analytics/changelog/2026-07-28-dashboard-loading-shimmer-now-flows-smoothly-in-one-directi.md +6 -0
  59. package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +5 -4
  60. package/corpus/templates/analytics/server/plugins/agent-chat.ts +54 -42
  61. package/corpus/templates/analytics/server/plugins/db.ts +1 -18
  62. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -0
  63. package/corpus/templates/assets/.agents/skills/security/SKILL.md +31 -0
  64. package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +4 -0
  65. package/corpus/templates/assets/_gitignore +2 -0
  66. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -0
  67. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +7 -0
  68. package/corpus/templates/brain/.agents/skills/security/SKILL.md +31 -0
  69. package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +4 -0
  70. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -0
  71. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +31 -0
  72. package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +4 -0
  73. package/corpus/templates/calendar/_gitignore +2 -0
  74. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -0
  75. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
  76. package/corpus/templates/chat/.agents/skills/security/SKILL.md +31 -0
  77. package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
  78. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -0
  79. package/corpus/templates/clips/.agents/skills/security/SKILL.md +31 -0
  80. package/corpus/templates/clips/_gitignore +2 -0
  81. package/corpus/templates/clips/actions/request-transcript.ts +77 -11
  82. package/corpus/templates/clips/actions/save-browser-transcript.ts +28 -7
  83. package/corpus/templates/clips/app/routes/record.tsx +11 -3
  84. package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-save-a-truncated-transcript-as-.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-stop-transcribing-partway-throu.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-07-28-desktop-full-screen-clips-now-get-an-instant-local-transcrip.md +6 -0
  87. package/corpus/templates/clips/changelog/2026-07-28-long-transcriptions-no-longer-show-a-false-transcription-sto.md +6 -0
  88. package/corpus/templates/clips/changelog/2026-07-28-restarting-for-an-update-now-installs-the-newest-release-ins.md +6 -0
  89. package/corpus/templates/clips/changelog/2026-07-28-the-meeting-recording-pill-now-anchors-to-the-right-edge-of-.md +6 -0
  90. package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +31 -2
  91. package/corpus/templates/clips/desktop/src/lib/recorder.ts +114 -13
  92. package/corpus/templates/clips/desktop/src/lib/updater.ts +12 -4
  93. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +40 -15
  94. package/corpus/templates/clips/desktop/src/styles.css +10 -7
  95. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +21 -14
  96. package/corpus/templates/clips/shared/transcript-status.ts +10 -0
  97. package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -0
  98. package/corpus/templates/content/.agents/skills/security/SKILL.md +31 -0
  99. package/corpus/templates/content/.agents/skills/sharing/SKILL.md +4 -0
  100. package/corpus/templates/content/_gitignore +2 -0
  101. package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +4 -0
  102. package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -0
  103. package/corpus/templates/design/.agents/skills/security/SKILL.md +31 -0
  104. package/corpus/templates/design/.agents/skills/sharing/SKILL.md +4 -0
  105. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +46 -3
  106. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +7 -5
  107. package/corpus/templates/design/_gitignore +2 -0
  108. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  109. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -4
  110. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
  111. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +10 -4
  112. package/corpus/templates/design/app/i18n-data.ts +10 -0
  113. package/corpus/templates/design/app/lib/builder-design-system-upload.ts +280 -0
  114. package/corpus/templates/design/app/lib/design-ui-events.ts +5 -0
  115. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +90 -54
  116. package/corpus/templates/design/app/root.tsx +18 -1
  117. package/corpus/templates/design/changelog/2026-07-28-figma-fig-uploads-now-show-indexing-progress-and-an-open-in-.md +6 -0
  118. package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +6 -0
  119. package/corpus/templates/design/server/handlers/design-system-decode-job-status.ts +49 -0
  120. package/corpus/templates/design/server/handlers/design-system-upload-start.ts +87 -0
  121. package/corpus/templates/design/server/handlers/index-design-system-sources.ts +82 -0
  122. package/corpus/templates/design/server/routes/api/design-system-decode-job-status.get.ts +1 -0
  123. package/corpus/templates/design/server/routes/api/design-system-upload-start.post.ts +1 -0
  124. package/corpus/templates/design/server/routes/api/index-design-system-sources.post.ts +1 -0
  125. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -0
  126. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +31 -0
  127. package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +4 -0
  128. package/corpus/templates/dispatch/_gitignore +2 -0
  129. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -0
  130. package/corpus/templates/forms/.agents/skills/security/SKILL.md +31 -0
  131. package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +4 -0
  132. package/corpus/templates/forms/_gitignore +2 -0
  133. package/corpus/templates/macros/.agents/skills/security/SKILL.md +31 -0
  134. package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +4 -0
  135. package/corpus/templates/macros/_gitignore +2 -0
  136. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -0
  137. package/corpus/templates/mail/.agents/skills/security/SKILL.md +31 -0
  138. package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +4 -0
  139. package/corpus/templates/mail/_gitignore +2 -0
  140. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -0
  141. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +7 -0
  142. package/corpus/templates/plan/.agents/skills/security/SKILL.md +31 -0
  143. package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +4 -0
  144. package/corpus/templates/plan/_gitignore +2 -0
  145. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -0
  146. package/corpus/templates/slides/.agents/skills/security/SKILL.md +31 -0
  147. package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +4 -0
  148. package/corpus/templates/slides/_gitignore +2 -0
  149. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +126 -53
  150. package/corpus/templates/slides/app/components/design-system/builder-design-system-upload.ts +271 -0
  151. package/corpus/templates/slides/app/components/design-system/builder-index-response.ts +1 -1
  152. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  153. package/corpus/templates/slides/server/handlers/design-system-decode-job-status.ts +49 -0
  154. package/corpus/templates/slides/server/handlers/design-system-upload-start.ts +87 -0
  155. package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +79 -0
  156. package/corpus/templates/slides/server/routes/api/design-system-decode-job-status.get.ts +1 -0
  157. package/corpus/templates/slides/server/routes/api/design-system-upload-start.post.ts +1 -0
  158. package/corpus/templates/slides/server/routes/api/index-design-system-sources.post.ts +1 -0
  159. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -0
  160. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +7 -0
  161. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +31 -0
  162. package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +4 -0
  163. package/corpus/templates/tasks/_gitignore +2 -0
  164. package/dist/a2a/artifact-response.d.ts.map +1 -1
  165. package/dist/a2a/artifact-response.js +30 -29
  166. package/dist/a2a/artifact-response.js.map +1 -1
  167. package/dist/action.d.ts +36 -0
  168. package/dist/action.d.ts.map +1 -1
  169. package/dist/action.js +37 -2
  170. package/dist/action.js.map +1 -1
  171. package/dist/chat-threads/store.d.ts +12 -0
  172. package/dist/chat-threads/store.d.ts.map +1 -1
  173. package/dist/chat-threads/store.js +48 -42
  174. package/dist/chat-threads/store.js.map +1 -1
  175. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  176. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  177. package/dist/cli/skills-content/design-visual-edit-skill.js +46 -3
  178. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  179. package/dist/client/AgentPanel.d.ts +3 -1
  180. package/dist/client/AgentPanel.d.ts.map +1 -1
  181. package/dist/client/AgentPanel.js +11 -9
  182. package/dist/client/AgentPanel.js.map +1 -1
  183. package/dist/client/AssistantChat.d.ts.map +1 -1
  184. package/dist/client/AssistantChat.js +57 -6
  185. package/dist/client/AssistantChat.js.map +1 -1
  186. package/dist/client/DefaultSpinner.d.ts +7 -0
  187. package/dist/client/DefaultSpinner.d.ts.map +1 -1
  188. package/dist/client/DefaultSpinner.js +25 -1
  189. package/dist/client/DefaultSpinner.js.map +1 -1
  190. package/dist/client/org/TeamPage.d.ts +13 -1
  191. package/dist/client/org/TeamPage.d.ts.map +1 -1
  192. package/dist/client/org/TeamPage.js +41 -11
  193. package/dist/client/org/TeamPage.js.map +1 -1
  194. package/dist/client/org/hooks.d.ts +37 -0
  195. package/dist/client/org/hooks.d.ts.map +1 -1
  196. package/dist/client/org/hooks.js +37 -0
  197. package/dist/client/org/hooks.js.map +1 -1
  198. package/dist/client/org/index.d.ts +3 -2
  199. package/dist/client/org/index.d.ts.map +1 -1
  200. package/dist/client/org/index.js +1 -1
  201. package/dist/client/org/index.js.map +1 -1
  202. package/dist/client/transcription/use-live-transcription.d.ts +8 -0
  203. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  204. package/dist/client/transcription/use-live-transcription.js +38 -11
  205. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  206. package/dist/collab/routes.d.ts +1 -1
  207. package/dist/db/client.d.ts +12 -0
  208. package/dist/db/client.d.ts.map +1 -1
  209. package/dist/db/client.js +40 -3
  210. package/dist/db/client.js.map +1 -1
  211. package/dist/db/ensure-additive-columns.d.ts.map +1 -1
  212. package/dist/db/ensure-additive-columns.js +54 -32
  213. package/dist/db/ensure-additive-columns.js.map +1 -1
  214. package/dist/deploy/build.d.ts +1 -0
  215. package/dist/deploy/build.d.ts.map +1 -1
  216. package/dist/deploy/build.js +23 -2
  217. package/dist/deploy/build.js.map +1 -1
  218. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  219. package/dist/mcp-client/routes.d.ts +3 -1
  220. package/dist/mcp-client/routes.d.ts.map +1 -1
  221. package/dist/mcp-client/routes.js +8 -2
  222. package/dist/mcp-client/routes.js.map +1 -1
  223. package/dist/notifications/routes.d.ts +1 -1
  224. package/dist/org/app-roles-handlers.d.ts +27 -0
  225. package/dist/org/app-roles-handlers.d.ts.map +1 -0
  226. package/dist/org/app-roles-handlers.js +125 -0
  227. package/dist/org/app-roles-handlers.js.map +1 -0
  228. package/dist/org/app-roles.d.ts +136 -0
  229. package/dist/org/app-roles.d.ts.map +1 -0
  230. package/dist/org/app-roles.js +216 -0
  231. package/dist/org/app-roles.js.map +1 -0
  232. package/dist/org/index.d.ts +4 -1
  233. package/dist/org/index.d.ts.map +1 -1
  234. package/dist/org/index.js +3 -1
  235. package/dist/org/index.js.map +1 -1
  236. package/dist/org/migrations.d.ts.map +1 -1
  237. package/dist/org/migrations.js +22 -0
  238. package/dist/org/migrations.js.map +1 -1
  239. package/dist/org/plugin.d.ts +2 -0
  240. package/dist/org/plugin.d.ts.map +1 -1
  241. package/dist/org/plugin.js +20 -0
  242. package/dist/org/plugin.js.map +1 -1
  243. package/dist/org/schema.d.ts +143 -0
  244. package/dist/org/schema.d.ts.map +1 -1
  245. package/dist/org/schema.js +14 -0
  246. package/dist/org/schema.js.map +1 -1
  247. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  248. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  249. package/dist/scripts/docs/search.d.ts.map +1 -1
  250. package/dist/scripts/docs/search.js +15 -0
  251. package/dist/scripts/docs/search.js.map +1 -1
  252. package/dist/scripts/docs/source-search.d.ts +7 -0
  253. package/dist/scripts/docs/source-search.d.ts.map +1 -1
  254. package/dist/scripts/docs/source-search.js +9 -0
  255. package/dist/scripts/docs/source-search.js.map +1 -1
  256. package/dist/secrets/routes.d.ts +9 -9
  257. package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
  258. package/dist/server/agent-chat/script-entries.js +3 -0
  259. package/dist/server/agent-chat/script-entries.js.map +1 -1
  260. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  261. package/dist/server/agent-chat-plugin.js +46 -48
  262. package/dist/server/agent-chat-plugin.js.map +1 -1
  263. package/dist/server/builder-design-systems.d.ts +49 -0
  264. package/dist/server/builder-design-systems.d.ts.map +1 -1
  265. package/dist/server/builder-design-systems.js +221 -69
  266. package/dist/server/builder-design-systems.js.map +1 -1
  267. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  268. package/dist/server/core-routes-plugin.js +6 -1
  269. package/dist/server/core-routes-plugin.js.map +1 -1
  270. package/dist/server/framework-request-handler.d.ts.map +1 -1
  271. package/dist/server/framework-request-handler.js +42 -6
  272. package/dist/server/framework-request-handler.js.map +1 -1
  273. package/dist/server/index.d.ts +1 -1
  274. package/dist/server/index.d.ts.map +1 -1
  275. package/dist/server/index.js +1 -1
  276. package/dist/server/index.js.map +1 -1
  277. package/dist/server/realtime-token.d.ts +1 -1
  278. package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -0
  279. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
  280. package/dist/templates/chat/.agents/skills/security/SKILL.md +31 -0
  281. package/dist/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
  282. package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -0
  283. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
  284. package/dist/templates/default/.agents/skills/security/SKILL.md +31 -0
  285. package/dist/templates/headless/.agents/skills/actions/SKILL.md +32 -0
  286. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
  287. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
  288. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
  289. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
  290. package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
  291. package/docs/content/actions.mdx +47 -4
  292. package/docs/content/native-chat-ui.mdx +7 -0
  293. package/docs/content/organizations-teams-permissions.mdx +109 -3
  294. package/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
  295. package/docs/content/toolkit-org-team.mdx +72 -12
  296. package/package.json +2 -1
  297. package/src/a2a/artifact-response.ts +36 -29
  298. package/src/action.ts +83 -2
  299. package/src/chat-threads/store.ts +48 -43
  300. package/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
  301. package/src/client/AgentPanel.tsx +26 -6
  302. package/src/client/AssistantChat.tsx +63 -7
  303. package/src/client/DefaultSpinner.tsx +29 -0
  304. package/src/client/org/TeamPage.tsx +161 -6
  305. package/src/client/org/hooks.ts +67 -0
  306. package/src/client/org/index.ts +9 -0
  307. package/src/client/transcription/use-live-transcription.ts +62 -10
  308. package/src/db/client.ts +42 -3
  309. package/src/db/ensure-additive-columns.ts +60 -33
  310. package/src/deploy/build.ts +31 -2
  311. package/src/mcp-client/routes.ts +10 -1
  312. package/src/org/app-roles-handlers.ts +152 -0
  313. package/src/org/app-roles.ts +355 -0
  314. package/src/org/index.ts +27 -1
  315. package/src/org/migrations.ts +22 -0
  316. package/src/org/plugin.ts +27 -0
  317. package/src/org/schema.ts +15 -0
  318. package/src/scripts/docs/search.ts +17 -0
  319. package/src/scripts/docs/source-search.ts +10 -0
  320. package/src/server/agent-chat/script-entries.ts +3 -0
  321. package/src/server/agent-chat-plugin.ts +53 -52
  322. package/src/server/builder-design-systems.ts +299 -82
  323. package/src/server/core-routes-plugin.ts +6 -1
  324. package/src/server/framework-request-handler.ts +44 -6
  325. package/src/server/index.ts +8 -0
  326. package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -0
  327. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
  328. package/src/templates/chat/.agents/skills/security/SKILL.md +31 -0
  329. package/src/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
  330. package/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
  331. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
  332. package/src/templates/default/.agents/skills/security/SKILL.md +31 -0
  333. package/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
  334. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
  335. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
  336. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
  337. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
  338. package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
  339. package/corpus/core/src/templates/chat/.agents/skills/actions/SKILL.md +0 -530
  340. package/corpus/core/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +0 -191
  341. package/corpus/core/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +0 -115
  342. package/corpus/core/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +0 -177
  343. package/corpus/core/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +0 -89
  344. package/corpus/core/src/templates/chat/.agents/skills/create-skill/SKILL.md +0 -221
  345. package/corpus/core/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +0 -220
  346. package/corpus/core/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +0 -263
  347. package/corpus/core/src/templates/chat/.agents/skills/feature-flags/SKILL.md +0 -169
  348. package/corpus/core/src/templates/chat/.agents/skills/frontend-design/SKILL.md +0 -174
  349. package/corpus/core/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +0 -232
  350. package/corpus/core/src/templates/chat/.agents/skills/security/SKILL.md +0 -282
  351. package/corpus/core/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +0 -119
  352. package/corpus/core/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +0 -123
  353. package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +0 -122
  354. package/corpus/core/src/templates/chat/.env.example +0 -5
  355. package/corpus/core/src/templates/chat/.ignore +0 -0
  356. package/corpus/core/src/templates/chat/.oxfmtrc.json +0 -8
  357. package/corpus/core/src/templates/chat/AGENTS.md +0 -77
  358. package/corpus/core/src/templates/chat/CHANGELOG.md +0 -10
  359. package/corpus/core/src/templates/chat/DEVELOPING.md +0 -185
  360. package/corpus/core/src/templates/chat/README.md +0 -32
  361. package/corpus/core/src/templates/chat/_gitignore +0 -46
  362. package/corpus/core/src/templates/chat/actions/hello.ts +0 -13
  363. package/corpus/core/src/templates/chat/actions/navigate.ts +0 -41
  364. package/corpus/core/src/templates/chat/actions/run.ts +0 -2
  365. package/corpus/core/src/templates/chat/actions/view-screen.ts +0 -31
  366. package/corpus/core/src/templates/chat/app/components/layout/Header.tsx +0 -60
  367. package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +0 -188
  368. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +0 -505
  369. package/corpus/core/src/templates/chat/app/components/ui/button.tsx +0 -1
  370. package/corpus/core/src/templates/chat/app/components/ui/card.tsx +0 -1
  371. package/corpus/core/src/templates/chat/app/components/ui/dropdown-menu.tsx +0 -1
  372. package/corpus/core/src/templates/chat/app/components/ui/input.tsx +0 -1
  373. package/corpus/core/src/templates/chat/app/components/ui/label.tsx +0 -1
  374. package/corpus/core/src/templates/chat/app/components/ui/sheet.tsx +0 -1
  375. package/corpus/core/src/templates/chat/app/components/ui/toolkit-provider.tsx +0 -17
  376. package/corpus/core/src/templates/chat/app/components/ui/tooltip.tsx +0 -1
  377. package/corpus/core/src/templates/chat/app/design-system.ts +0 -3
  378. package/corpus/core/src/templates/chat/app/entry.client.tsx +0 -22
  379. package/corpus/core/src/templates/chat/app/entry.server.tsx +0 -10
  380. package/corpus/core/src/templates/chat/app/global.css +0 -93
  381. package/corpus/core/src/templates/chat/app/hooks/use-navigation-state.ts +0 -93
  382. package/corpus/core/src/templates/chat/app/i18n/ar-SA.ts +0 -68
  383. package/corpus/core/src/templates/chat/app/i18n/de-DE.ts +0 -71
  384. package/corpus/core/src/templates/chat/app/i18n/en-US.ts +0 -70
  385. package/corpus/core/src/templates/chat/app/i18n/es-ES.ts +0 -71
  386. package/corpus/core/src/templates/chat/app/i18n/fr-FR.ts +0 -71
  387. package/corpus/core/src/templates/chat/app/i18n/hi-IN.ts +0 -69
  388. package/corpus/core/src/templates/chat/app/i18n/index.ts +0 -34
  389. package/corpus/core/src/templates/chat/app/i18n/ja-JP.ts +0 -70
  390. package/corpus/core/src/templates/chat/app/i18n/ko-KR.ts +0 -70
  391. package/corpus/core/src/templates/chat/app/i18n/pt-BR.ts +0 -71
  392. package/corpus/core/src/templates/chat/app/i18n/zh-CN.ts +0 -66
  393. package/corpus/core/src/templates/chat/app/i18n/zh-TW.ts +0 -66
  394. package/corpus/core/src/templates/chat/app/i18n-data.ts +0 -556
  395. package/corpus/core/src/templates/chat/app/lib/agent-page.tsx +0 -49
  396. package/corpus/core/src/templates/chat/app/lib/app-config.ts +0 -11
  397. package/corpus/core/src/templates/chat/app/lib/tab-id.ts +0 -1
  398. package/corpus/core/src/templates/chat/app/lib/utils.ts +0 -1
  399. package/corpus/core/src/templates/chat/app/root.tsx +0 -173
  400. package/corpus/core/src/templates/chat/app/routes/_index.tsx +0 -94
  401. package/corpus/core/src/templates/chat/app/routes/agent.tsx +0 -24
  402. package/corpus/core/src/templates/chat/app/routes/chat.$threadId.tsx +0 -1
  403. package/corpus/core/src/templates/chat/app/routes/database.tsx +0 -17
  404. package/corpus/core/src/templates/chat/app/routes/extensions.$id.$slug.tsx +0 -2
  405. package/corpus/core/src/templates/chat/app/routes/extensions.$id.tsx +0 -11
  406. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +0 -11
  407. package/corpus/core/src/templates/chat/app/routes/extensions.tsx +0 -5
  408. package/corpus/core/src/templates/chat/app/routes/observability.tsx +0 -19
  409. package/corpus/core/src/templates/chat/app/routes/settings.tsx +0 -89
  410. package/corpus/core/src/templates/chat/app/routes/team.tsx +0 -11
  411. package/corpus/core/src/templates/chat/app/routes.ts +0 -4
  412. package/corpus/core/src/templates/chat/app/vite-env.d.ts +0 -6
  413. package/corpus/core/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
  414. package/corpus/core/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
  415. package/corpus/core/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
  416. package/corpus/core/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +0 -6
  417. package/corpus/core/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
  418. package/corpus/core/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +0 -5
  419. package/corpus/core/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +0 -6
  420. package/corpus/core/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +0 -6
  421. package/corpus/core/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +0 -6
  422. package/corpus/core/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +0 -6
  423. package/corpus/core/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +0 -6
  424. package/corpus/core/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +0 -6
  425. package/corpus/core/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +0 -6
  426. package/corpus/core/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
  427. package/corpus/core/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +0 -6
  428. package/corpus/core/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +0 -6
  429. package/corpus/core/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +0 -6
  430. package/corpus/core/src/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +0 -6
  431. package/corpus/core/src/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +0 -6
  432. package/corpus/core/src/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +0 -6
  433. package/corpus/core/src/templates/chat/components.json +0 -20
  434. package/corpus/core/src/templates/chat/learnings.defaults.md +0 -5
  435. package/corpus/core/src/templates/chat/netlify.toml +0 -11
  436. package/corpus/core/src/templates/chat/package.json +0 -92
  437. package/corpus/core/src/templates/chat/public/agent-native-icon-dark.svg +0 -10
  438. package/corpus/core/src/templates/chat/public/agent-native-icon-light.svg +0 -10
  439. package/corpus/core/src/templates/chat/public/agent-native-logo-dark.svg +0 -21
  440. package/corpus/core/src/templates/chat/public/agent-native-logo-light.svg +0 -21
  441. package/corpus/core/src/templates/chat/public/favicon.svg +0 -1
  442. package/corpus/core/src/templates/chat/public/icon-180.svg +0 -1
  443. package/corpus/core/src/templates/chat/public/icon-192.svg +0 -1
  444. package/corpus/core/src/templates/chat/public/icon-512.svg +0 -1
  445. package/corpus/core/src/templates/chat/public/manifest.json +0 -21
  446. package/corpus/core/src/templates/chat/react-router.config.ts +0 -7
  447. package/corpus/core/src/templates/chat/server/middleware/auth.ts +0 -15
  448. package/corpus/core/src/templates/chat/server/plugins/agent-chat.ts +0 -21
  449. package/corpus/core/src/templates/chat/server/plugins/auth.ts +0 -17
  450. package/corpus/core/src/templates/chat/server/routes/[...page].get.ts +0 -5
  451. package/corpus/core/src/templates/chat/ssr-entry.ts +0 -15
  452. package/corpus/core/src/templates/chat/tsconfig.json +0 -21
  453. package/corpus/core/src/templates/chat/vite.config.ts +0 -19
  454. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +0 -151
  455. package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +0 -1
  456. package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +0 -119
  457. package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +0 -1
@@ -34,6 +34,8 @@ Visibility is coarse. Explicit share grants are fine-grained (per user or per or
34
34
  - **`editor`** — read + write.
35
35
  - **`admin`** — read + write + manage shares. Does NOT replace the single `owner_email` on the resource.
36
36
 
37
+ There are three role systems and they never imply one another. A share role answers "what may this person do to **one row**". An org role (`org_members.role`) answers "what may this person do to the **team**". An app role (`defineAppRoles`, see the `authentication` skill) answers "what may this person do inside **one app**". A share `admin` is not an app admin and neither is an org admin.
38
+
37
39
  ### Anonymous public URLs stay separate
38
40
 
39
41
  Form "publish" slugs, booking-link slugs, any feature that exposes a URL to unauthenticated users — these are a different axis and are NOT controlled by the sharing system. Keep them alongside it.
@@ -137,6 +139,8 @@ export default defineAction({
137
139
 
138
140
  For delete actions use `"admin"` (or fold in `"owner"` to require the real owner).
139
141
 
142
+ `authorize` is a different axis, not an alternative: it gates whether the caller may run the operation at all, while `assertAccess` scopes which row they may touch. A write action restricted to some teammates needs both — `authorize: appAccess.requireAny(...)` on the action, `assertAccess` inside `run`.
143
+
140
144
  ## Create actions must set owner
141
145
 
142
146
  When inserting a new row, fill `ownerEmail` and `orgId` from the request context:
@@ -305,6 +305,38 @@ export default defineAction({
305
305
 
306
306
  On success the validated value is returned, so coercion/defaults on `outputSchema` apply. Omit `outputSchema` and behavior is byte-for-byte unchanged (no wrapping).
307
307
 
308
+ ### Authorization (`authorize`)
309
+
310
+ `authorize` decides whether the caller may run the action at all. It wraps
311
+ `run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
312
+ hook, MCP, A2A, CLI — unlike `needsApproval`, which is honoured only in the
313
+ agent loop. Use `authorize` for "not everyone may do this"; use `needsApproval`
314
+ to ask a human to bless one call a permitted caller is already allowed to make.
315
+
316
+ ```ts
317
+ import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
318
+
319
+ export default defineAction({
320
+ description: "Archive a client roster.",
321
+ schema: z.object({ id: z.string() }),
322
+ authorize: coachAccess.requireAny("coach-admin"),
323
+ run: async (args) => {
324
+ /* ... */
325
+ },
326
+ });
327
+ ```
328
+
329
+ The wrappers compose as `validate input -> authorize -> run -> validate output
330
+ -> audit`: the gate sits inside input validation, so a guard reading `args` gets
331
+ the parsed, coerced value, and auditing is outermost, so denials are recorded.
332
+
333
+ A guard that throws denies with its own message; returning `false` denies
334
+ generically; anything else (including `undefined`) allows. A guarded action
335
+ needs a user identity, so an unattended CLI/cron caller with no user email is
336
+ denied. `authorize` gates the operation; `accessFilter` / `assertAccess` still
337
+ scope which rows a permitted caller may touch. See the `authentication` skill
338
+ for `defineAppRoles` and the `sharing` skill for row scoping.
339
+
308
340
  ### Human-in-the-Loop Approval (`needsApproval`)
309
341
 
310
342
  For high-consequence, outward-facing, hard-to-undo actions (sending an email, charging a card, deleting an account), set `needsApproval` so the agent **cannot** run the action without a human approving the specific call:
@@ -159,6 +159,35 @@ whole site. Data scoping lives in actions and API routes; the client gates
159
159
  private UI after the shell loads. See the `authentication` skill and
160
160
  `guard:ssr-cache-shell` / `ssr-handler.spec.ts`.
161
161
 
162
+ **Authorization beyond "is there a session" — `authorize`.** The auth guard only
163
+ proves *someone* is signed in. To restrict an operation to some teammates, set
164
+ `authorize` on the `defineAction`. It wraps `run`, so it applies at every
165
+ dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
166
+ `needsApproval`, which is honoured only in the agent loop.
167
+
168
+ ```ts
169
+ import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
170
+
171
+ export default defineAction({
172
+ description: "Archive a client roster.",
173
+ schema: z.object({ id: z.string() }),
174
+ authorize: coachAccess.requireAny("coach-admin"),
175
+ run: async (args) => {
176
+ /* ... */
177
+ },
178
+ });
179
+ ```
180
+
181
+ A guard that throws denies with its own message; returning `false` denies
182
+ generically; anything else (including `undefined`) allows. Guarded actions need
183
+ a user identity — an unattended CLI/cron caller with no user email is denied.
184
+
185
+ `authorize` is **not** a substitute for `accessFilter` / `assertAccess`. It
186
+ decides whether this caller may perform the operation at all; `accessFilter` /
187
+ `assertAccess` scope which rows a permitted caller may see or touch. A
188
+ restricted write action needs both. See the `authentication` skill for
189
+ `defineAppRoles` and the `actions` skill for the full surface.
190
+
162
191
  ## Human-in-the-Loop Approval for High-Consequence Actions
163
192
 
164
193
  For a small set of outward-facing, hard-to-undo operations — sending an email, charging a card, deleting an account, posting publicly — auth and access control are necessary but not sufficient: you also do not want the **agent** to perform them autonomously. Set `needsApproval` on the `defineAction` so the agent cannot run the action without a human approving the specific call.
@@ -274,6 +303,8 @@ Run `pnpm action db-check-scoping` to verify. Use `--require-org` for multi-org
274
303
  - [ ] New env vars in `.env` only, not committed
275
304
  - [ ] New user-data tables have `owner_email` column
276
305
  - [ ] Custom routes call `getSession` and reject unauthenticated requests
306
+ - [ ] Actions only some teammates may run set `authorize` (in addition to any
307
+ `accessFilter` / `assertAccess` row scoping)
277
308
 
278
309
  ## Related Skills
279
310
 
@@ -34,6 +34,8 @@ Visibility is coarse. Explicit share grants are fine-grained (per user or per or
34
34
  - **`editor`** — read + write.
35
35
  - **`admin`** — read + write + manage shares. Does NOT replace the single `owner_email` on the resource.
36
36
 
37
+ There are three role systems and they never imply one another. A share role answers "what may this person do to **one row**". An org role (`org_members.role`) answers "what may this person do to the **team**". An app role (`defineAppRoles`, see the `authentication` skill) answers "what may this person do inside **one app**". A share `admin` is not an app admin and neither is an org admin.
38
+
37
39
  ### Anonymous public URLs stay separate
38
40
 
39
41
  Form "publish" slugs, booking-link slugs, any feature that exposes a URL to unauthenticated users — these are a different axis and are NOT controlled by the sharing system. Keep them alongside it.
@@ -137,6 +139,8 @@ export default defineAction({
137
139
 
138
140
  For delete actions use `"admin"` (or fold in `"owner"` to require the real owner).
139
141
 
142
+ `authorize` is a different axis, not an alternative: it gates whether the caller may run the operation at all, while `assertAccess` scopes which row they may touch. A write action restricted to some teammates needs both — `authorize: appAccess.requireAny(...)` on the action, `assertAccess` inside `run`.
143
+
140
144
  ## Create actions must set owner
141
145
 
142
146
  When inserting a new row, fill `ownerEmail` and `orgId` from the request context:
@@ -8,8 +8,10 @@ npm-debug.log*
8
8
  pnpm-debug.log*
9
9
 
10
10
  node_modules
11
+ build/
11
12
  dist
12
13
  dist-ssr
14
+ .output/
13
15
  *.local
14
16
 
15
17
  # Editor directories and files
@@ -305,6 +305,38 @@ export default defineAction({
305
305
 
306
306
  On success the validated value is returned, so coercion/defaults on `outputSchema` apply. Omit `outputSchema` and behavior is byte-for-byte unchanged (no wrapping).
307
307
 
308
+ ### Authorization (`authorize`)
309
+
310
+ `authorize` decides whether the caller may run the action at all. It wraps
311
+ `run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
312
+ hook, MCP, A2A, CLI — unlike `needsApproval`, which is honoured only in the
313
+ agent loop. Use `authorize` for "not everyone may do this"; use `needsApproval`
314
+ to ask a human to bless one call a permitted caller is already allowed to make.
315
+
316
+ ```ts
317
+ import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
318
+
319
+ export default defineAction({
320
+ description: "Archive a client roster.",
321
+ schema: z.object({ id: z.string() }),
322
+ authorize: coachAccess.requireAny("coach-admin"),
323
+ run: async (args) => {
324
+ /* ... */
325
+ },
326
+ });
327
+ ```
328
+
329
+ The wrappers compose as `validate input -> authorize -> run -> validate output
330
+ -> audit`: the gate sits inside input validation, so a guard reading `args` gets
331
+ the parsed, coerced value, and auditing is outermost, so denials are recorded.
332
+
333
+ A guard that throws denies with its own message; returning `false` denies
334
+ generically; anything else (including `undefined`) allows. A guarded action
335
+ needs a user identity, so an unattended CLI/cron caller with no user email is
336
+ denied. `authorize` gates the operation; `accessFilter` / `assertAccess` still
337
+ scope which rows a permitted caller may touch. See the `authentication` skill
338
+ for `defineAppRoles` and the `sharing` skill for row scoping.
339
+
308
340
  ### Human-in-the-Loop Approval (`needsApproval`)
309
341
 
310
342
  For high-consequence, outward-facing, hard-to-undo actions (sending an email, charging a card, deleting an account), set `needsApproval` so the agent **cannot** run the action without a human approving the specific call:
@@ -179,8 +179,15 @@ If the feature stores **user-authored resources** (documents, dashboards, forms,
179
179
 
180
180
  TL;DR: spread `ownableColumns()` into the resource table, pair it with `createSharesTable(...)`, call `registerShareableResource(...)`, wrap list/read queries with `accessFilter`, guard writes with `assertAccess`, and drop `<ShareButton>` in the resource header. The `share-resource`, `unshare-resource`, `list-resource-shares`, and `set-resource-visibility` actions are auto-mounted framework-wide.
181
181
 
182
+ ## One more area — who inside the app may do it
183
+
184
+ If the feature is one **only some teammates should be able to perform inside this app** (an admin-only import, a settings reset), that is a per-app role — not a new organization, and not a share grant. See the `authentication` skill.
185
+
186
+ TL;DR: declare the vocabulary once with `defineAppRoles({ appId, roles, defaultRole })`, guard the action with `authorize: appAccess.requireAny("...")`, and render the picker with `<TeamPage appRoles={descriptor} />`. `defaultRole` is display only — it never satisfies a guard — and `authorize` gates the operation while `accessFilter` / `assertAccess` still scope the rows.
187
+
182
188
  ## Related Skills
183
189
 
190
+ - **authentication** — Per-app member roles (`defineAppRoles`) when only some teammates may use a feature
184
191
  - **sharing** — How to make a new resource ownable (private by default, share with users/orgs/public)
185
192
  - **context-awareness** — How to expose UI state to the agent (area 4 in detail)
186
193
  - **actions** — How to create actions with `defineAction` and the `http` option (area 2 in detail)
@@ -159,6 +159,35 @@ whole site. Data scoping lives in actions and API routes; the client gates
159
159
  private UI after the shell loads. See the `authentication` skill and
160
160
  `guard:ssr-cache-shell` / `ssr-handler.spec.ts`.
161
161
 
162
+ **Authorization beyond "is there a session" — `authorize`.** The auth guard only
163
+ proves *someone* is signed in. To restrict an operation to some teammates, set
164
+ `authorize` on the `defineAction`. It wraps `run`, so it applies at every
165
+ dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
166
+ `needsApproval`, which is honoured only in the agent loop.
167
+
168
+ ```ts
169
+ import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
170
+
171
+ export default defineAction({
172
+ description: "Archive a client roster.",
173
+ schema: z.object({ id: z.string() }),
174
+ authorize: coachAccess.requireAny("coach-admin"),
175
+ run: async (args) => {
176
+ /* ... */
177
+ },
178
+ });
179
+ ```
180
+
181
+ A guard that throws denies with its own message; returning `false` denies
182
+ generically; anything else (including `undefined`) allows. Guarded actions need
183
+ a user identity — an unattended CLI/cron caller with no user email is denied.
184
+
185
+ `authorize` is **not** a substitute for `accessFilter` / `assertAccess`. It
186
+ decides whether this caller may perform the operation at all; `accessFilter` /
187
+ `assertAccess` scope which rows a permitted caller may see or touch. A
188
+ restricted write action needs both. See the `authentication` skill for
189
+ `defineAppRoles` and the `actions` skill for the full surface.
190
+
162
191
  ## Human-in-the-Loop Approval for High-Consequence Actions
163
192
 
164
193
  For a small set of outward-facing, hard-to-undo operations — sending an email, charging a card, deleting an account, posting publicly — auth and access control are necessary but not sufficient: you also do not want the **agent** to perform them autonomously. Set `needsApproval` on the `defineAction` so the agent cannot run the action without a human approving the specific call.
@@ -274,6 +303,8 @@ Run `pnpm action db-check-scoping` to verify. Use `--require-org` for multi-org
274
303
  - [ ] New env vars in `.env` only, not committed
275
304
  - [ ] New user-data tables have `owner_email` column
276
305
  - [ ] Custom routes call `getSession` and reject unauthenticated requests
306
+ - [ ] Actions only some teammates may run set `authorize` (in addition to any
307
+ `accessFilter` / `assertAccess` row scoping)
277
308
 
278
309
  ## Related Skills
279
310
 
@@ -34,6 +34,8 @@ Visibility is coarse. Explicit share grants are fine-grained (per user or per or
34
34
  - **`editor`** — read + write.
35
35
  - **`admin`** — read + write + manage shares. Does NOT replace the single `owner_email` on the resource.
36
36
 
37
+ There are three role systems and they never imply one another. A share role answers "what may this person do to **one row**". An org role (`org_members.role`) answers "what may this person do to the **team**". An app role (`defineAppRoles`, see the `authentication` skill) answers "what may this person do inside **one app**". A share `admin` is not an app admin and neither is an org admin.
38
+
37
39
  ### Anonymous public URLs stay separate
38
40
 
39
41
  Form "publish" slugs, booking-link slugs, any feature that exposes a URL to unauthenticated users — these are a different axis and are NOT controlled by the sharing system. Keep them alongside it.
@@ -137,6 +139,8 @@ export default defineAction({
137
139
 
138
140
  For delete actions use `"admin"` (or fold in `"owner"` to require the real owner).
139
141
 
142
+ `authorize` is a different axis, not an alternative: it gates whether the caller may run the operation at all, while `assertAccess` scopes which row they may touch. A write action restricted to some teammates needs both — `authorize: appAccess.requireAny(...)` on the action, `assertAccess` inside `run`.
143
+
140
144
  ## Create actions must set owner
141
145
 
142
146
  When inserting a new row, fill `ownerEmail` and `orgId` from the request context:
@@ -305,6 +305,38 @@ export default defineAction({
305
305
 
306
306
  On success the validated value is returned, so coercion/defaults on `outputSchema` apply. Omit `outputSchema` and behavior is byte-for-byte unchanged (no wrapping).
307
307
 
308
+ ### Authorization (`authorize`)
309
+
310
+ `authorize` decides whether the caller may run the action at all. It wraps
311
+ `run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
312
+ hook, MCP, A2A, CLI — unlike `needsApproval`, which is honoured only in the
313
+ agent loop. Use `authorize` for "not everyone may do this"; use `needsApproval`
314
+ to ask a human to bless one call a permitted caller is already allowed to make.
315
+
316
+ ```ts
317
+ import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
318
+
319
+ export default defineAction({
320
+ description: "Archive a client roster.",
321
+ schema: z.object({ id: z.string() }),
322
+ authorize: coachAccess.requireAny("coach-admin"),
323
+ run: async (args) => {
324
+ /* ... */
325
+ },
326
+ });
327
+ ```
328
+
329
+ The wrappers compose as `validate input -> authorize -> run -> validate output
330
+ -> audit`: the gate sits inside input validation, so a guard reading `args` gets
331
+ the parsed, coerced value, and auditing is outermost, so denials are recorded.
332
+
333
+ A guard that throws denies with its own message; returning `false` denies
334
+ generically; anything else (including `undefined`) allows. A guarded action
335
+ needs a user identity, so an unattended CLI/cron caller with no user email is
336
+ denied. `authorize` gates the operation; `accessFilter` / `assertAccess` still
337
+ scope which rows a permitted caller may touch. See the `authentication` skill
338
+ for `defineAppRoles` and the `sharing` skill for row scoping.
339
+
308
340
  ### Human-in-the-Loop Approval (`needsApproval`)
309
341
 
310
342
  For high-consequence, outward-facing, hard-to-undo actions (sending an email, charging a card, deleting an account), set `needsApproval` so the agent **cannot** run the action without a human approving the specific call:
@@ -159,6 +159,35 @@ whole site. Data scoping lives in actions and API routes; the client gates
159
159
  private UI after the shell loads. See the `authentication` skill and
160
160
  `guard:ssr-cache-shell` / `ssr-handler.spec.ts`.
161
161
 
162
+ **Authorization beyond "is there a session" — `authorize`.** The auth guard only
163
+ proves *someone* is signed in. To restrict an operation to some teammates, set
164
+ `authorize` on the `defineAction`. It wraps `run`, so it applies at every
165
+ dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
166
+ `needsApproval`, which is honoured only in the agent loop.
167
+
168
+ ```ts
169
+ import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
170
+
171
+ export default defineAction({
172
+ description: "Archive a client roster.",
173
+ schema: z.object({ id: z.string() }),
174
+ authorize: coachAccess.requireAny("coach-admin"),
175
+ run: async (args) => {
176
+ /* ... */
177
+ },
178
+ });
179
+ ```
180
+
181
+ A guard that throws denies with its own message; returning `false` denies
182
+ generically; anything else (including `undefined`) allows. Guarded actions need
183
+ a user identity — an unattended CLI/cron caller with no user email is denied.
184
+
185
+ `authorize` is **not** a substitute for `accessFilter` / `assertAccess`. It
186
+ decides whether this caller may perform the operation at all; `accessFilter` /
187
+ `assertAccess` scope which rows a permitted caller may see or touch. A
188
+ restricted write action needs both. See the `authentication` skill for
189
+ `defineAppRoles` and the `actions` skill for the full surface.
190
+
162
191
  ## Human-in-the-Loop Approval for High-Consequence Actions
163
192
 
164
193
  For a small set of outward-facing, hard-to-undo operations — sending an email, charging a card, deleting an account, posting publicly — auth and access control are necessary but not sufficient: you also do not want the **agent** to perform them autonomously. Set `needsApproval` on the `defineAction` so the agent cannot run the action without a human approving the specific call.
@@ -274,6 +303,8 @@ Run `pnpm action db-check-scoping` to verify. Use `--require-org` for multi-org
274
303
  - [ ] New env vars in `.env` only, not committed
275
304
  - [ ] New user-data tables have `owner_email` column
276
305
  - [ ] Custom routes call `getSession` and reject unauthenticated requests
306
+ - [ ] Actions only some teammates may run set `authorize` (in addition to any
307
+ `accessFilter` / `assertAccess` row scoping)
277
308
 
278
309
  ## Related Skills
279
310
 
@@ -8,8 +8,10 @@ npm-debug.log*
8
8
  pnpm-debug.log*
9
9
 
10
10
  node_modules
11
+ build/
11
12
  dist
12
13
  dist-ssr
14
+ .output/
13
15
  *.local
14
16
 
15
17
  # Editor directories and files
@@ -59,6 +59,7 @@ import {
59
59
  normalizeTranscriptSegments,
60
60
  parseTranscriptSegments,
61
61
  } from "../shared/transcript-segments.js";
62
+ import { PENDING_TRANSCRIPT_HEARTBEAT_MS } from "../shared/transcript-status.js";
62
63
  import cleanupTranscript from "./cleanup-transcript.js";
63
64
  import { loadAgentsMdContext } from "./lib/agents-md-context.js";
64
65
  import {
@@ -419,6 +420,54 @@ function isRecentlyPendingTranscript(transcript: {
419
420
  );
420
421
  }
421
422
 
423
+ /**
424
+ * Run `work` while keeping this recording's pending transcript row marked live.
425
+ *
426
+ * `resolveTranscriptPresentation` infers "the worker is gone" from a pending
427
+ * row nothing has written for STALE_PENDING_TRANSCRIPT_MS, because the row
428
+ * carries no other liveness signal. Media fetch, ffmpeg extraction and the
429
+ * provider call legitimately add up past that window on a long recording, so
430
+ * without this ping the UI publishes a terminal "stopped before it finished"
431
+ * failure over a run that is still working — and the player's self-heal then
432
+ * forces a second concurrent transcription of the same clip.
433
+ *
434
+ * The update is scoped to `status = 'pending'` so it can never touch a row a
435
+ * concurrent run has already finished, and the interval is unref'd and cleared
436
+ * so it cannot hold a serverless invocation open.
437
+ */
438
+ async function withPendingTranscriptHeartbeat<T>(
439
+ db: ReturnType<typeof getDb>,
440
+ recordingId: string,
441
+ work: () => Promise<T>,
442
+ ): Promise<T> {
443
+ const timer = setInterval(() => {
444
+ void (async () => {
445
+ try {
446
+ await db
447
+ .update(schema.recordingTranscripts)
448
+ .set({ updatedAt: new Date().toISOString() })
449
+ .where(
450
+ and(
451
+ eq(schema.recordingTranscripts.recordingId, recordingId),
452
+ eq(schema.recordingTranscripts.status, "pending"),
453
+ ),
454
+ );
455
+ } catch (err) {
456
+ console.warn(
457
+ `[clips] transcript heartbeat failed for ${recordingId}:`,
458
+ (err as Error)?.message ?? String(err),
459
+ );
460
+ }
461
+ })();
462
+ }, PENDING_TRANSCRIPT_HEARTBEAT_MS);
463
+ (timer as { unref?: () => void }).unref?.();
464
+ try {
465
+ return await work();
466
+ } finally {
467
+ clearInterval(timer);
468
+ }
469
+ }
470
+
422
471
  async function writeTranscriptCleanupState(
423
472
  recordingId: string,
424
473
  value: Record<string, unknown>,
@@ -1251,8 +1300,15 @@ const requestTranscriptAction = defineAction({
1251
1300
 
1252
1301
  let audioMedia: AudioOnlyTranscriptionMedia;
1253
1302
  try {
1254
- audioMedia = await getAudioMedia(rec);
1255
- await ensureAudioHasSignal(audioMedia);
1303
+ audioMedia = await withPendingTranscriptHeartbeat(
1304
+ db,
1305
+ args.recordingId,
1306
+ async () => {
1307
+ const media = await getAudioMedia(rec);
1308
+ await ensureAudioHasSignal(media);
1309
+ return media;
1310
+ },
1311
+ );
1256
1312
  } catch (err) {
1257
1313
  return failAudioOnlyPreparation({
1258
1314
  db,
@@ -1266,13 +1322,18 @@ const requestTranscriptAction = defineAction({
1266
1322
 
1267
1323
  try {
1268
1324
  const startedAt = Date.now();
1269
- const builderResult = await transcribeWithBuilderModelFallback({
1270
- audioBytes: audioMedia.audioBytes,
1271
- mimeType: audioMedia.mimeType,
1272
- diarize: false,
1273
- instructions: SPEECH_ONLY_TRANSCRIPTION_INSTRUCTIONS,
1274
- timeoutMs: builderTranscriptionTimeoutMs(rec.durationMs),
1275
- });
1325
+ const builderResult = await withPendingTranscriptHeartbeat(
1326
+ db,
1327
+ args.recordingId,
1328
+ () =>
1329
+ transcribeWithBuilderModelFallback({
1330
+ audioBytes: audioMedia.audioBytes,
1331
+ mimeType: audioMedia.mimeType,
1332
+ diarize: false,
1333
+ instructions: SPEECH_ONLY_TRANSCRIPTION_INSTRUCTIONS,
1334
+ timeoutMs: builderTranscriptionTimeoutMs(rec.durationMs),
1335
+ }),
1336
+ );
1276
1337
 
1277
1338
  const segments = (builderResult.segments ?? [])
1278
1339
  .map((s) => ({
@@ -1449,6 +1510,11 @@ async function upsertTranscriptRow(
1449
1510
  .limit(1);
1450
1511
 
1451
1512
  const retryCount = row.status === "ready" ? 0 : (row.retryCount ?? undefined);
1513
+ // `row.now` is captured once at the top of a run that can last minutes, so it
1514
+ // is a creation timestamp, never a "last written" one. Stamping it as
1515
+ // `updatedAt` would walk the pending heartbeat backwards and re-arm the stale
1516
+ // check over a run that just finished.
1517
+ const updatedAt = new Date().toISOString();
1452
1518
 
1453
1519
  if (existing) {
1454
1520
  await db
@@ -1461,7 +1527,7 @@ async function upsertTranscriptRow(
1461
1527
  ...(row.segmentsJson ? { segmentsJson: row.segmentsJson } : {}),
1462
1528
  ...(row.fullText !== undefined ? { fullText: row.fullText } : {}),
1463
1529
  ...(retryCount !== undefined ? { retryCount } : {}),
1464
- updatedAt: row.now,
1530
+ updatedAt,
1465
1531
  })
1466
1532
  .where(eq(schema.recordingTranscripts.recordingId, row.recordingId));
1467
1533
  } else {
@@ -1475,7 +1541,7 @@ async function upsertTranscriptRow(
1475
1541
  failureReason: row.failureReason,
1476
1542
  retryCount: retryCount ?? 0,
1477
1543
  createdAt: row.now,
1478
- updatedAt: row.now,
1544
+ updatedAt,
1479
1545
  });
1480
1546
  }
1481
1547
  }
@@ -153,6 +153,18 @@ export default defineAction({
153
153
  };
154
154
  }
155
155
 
156
+ // Text plus a failure reason means capture died partway (a Web Speech
157
+ // session that could not restart, a revoked mic). Keep the partial text,
158
+ // but never mark it `ready`: that is the terminal state every preserve
159
+ // guard checks, so a three-line partial would permanently suppress the
160
+ // Builder fallback that can still transcribe the whole recording.
161
+ // `failed` rather than `pending`/`streaming` — a fresh pending row makes
162
+ // finalization's own transcript job skip itself as already-pending, and a
163
+ // perpetual streaming row reads as normal progress that never resolves.
164
+ const truncated = Boolean(failureReason);
165
+ const savedStatus = truncated ? ("failed" as const) : ("ready" as const);
166
+ const savedFailureReason = truncated ? failureReason : null;
167
+
156
168
  if (current) {
157
169
  // Don't overwrite an already-segmented cloud/native transcript with a
158
170
  // later lower-confidence native pass — UNLESS the caller owns this
@@ -172,8 +184,8 @@ export default defineAction({
172
184
  ownerEmail,
173
185
  fullText,
174
186
  segmentsJson,
175
- status: "ready",
176
- failureReason: null,
187
+ status: savedStatus,
188
+ failureReason: savedFailureReason,
177
189
  updatedAt: now,
178
190
  })
179
191
  .where(eq(schema.recordingTranscripts.recordingId, args.recordingId));
@@ -184,15 +196,17 @@ export default defineAction({
184
196
  language: "en",
185
197
  segmentsJson,
186
198
  fullText,
187
- status: "ready",
188
- failureReason: null,
199
+ status: savedStatus,
200
+ failureReason: savedFailureReason,
189
201
  createdAt: now,
190
202
  updatedAt: now,
191
203
  });
192
204
  }
193
205
 
194
206
  console.log(
195
- `[clips] Native transcript saved for ${args.recordingId} via ${args.source ?? "web-speech"} (${fullText.length} chars)`,
207
+ truncated
208
+ ? `[clips] Partial native transcript saved for ${args.recordingId} via ${args.source ?? "web-speech"} (${fullText.length} chars); Builder fallback will retranscribe: ${failureReason}`
209
+ : `[clips] Native transcript saved for ${args.recordingId} via ${args.source ?? "web-speech"} (${fullText.length} chars)`,
196
210
  );
197
211
 
198
212
  await writeAppState("refresh-signal", { ts: Date.now() });
@@ -212,7 +226,13 @@ export default defineAction({
212
226
  rec && isAutoTitleReplaceable(rec.title, rec.titleSource)
213
227
  );
214
228
  const summaryQueued = Boolean(rec && !rec.description?.trim());
215
- if (rec?.status === "ready" && (titleQueued || summaryQueued)) {
229
+ // A truncated capture dispatches too: the transcript job is what runs the
230
+ // Builder fallback, and it must not be skipped just because this clip
231
+ // already has a title and summary.
232
+ if (
233
+ rec?.status === "ready" &&
234
+ (truncated || titleQueued || summaryQueued)
235
+ ) {
216
236
  await dispatchPostFinalizeJob({
217
237
  recordingId: args.recordingId,
218
238
  kind: "transcript",
@@ -226,9 +246,10 @@ export default defineAction({
226
246
 
227
247
  return {
228
248
  recordingId: args.recordingId,
229
- status: "ready" as const,
249
+ status: savedStatus,
230
250
  provider: args.source ?? "web-speech",
231
251
  chars: fullText.length,
252
+ truncated,
232
253
  titleQueued,
233
254
  summaryQueued,
234
255
  };
@@ -2054,6 +2054,11 @@ export default function RecordRoute() {
2054
2054
  // (from Web Speech API) with no API key required.
2055
2055
  const browserTranscript = await liveTranscription.stopAndWait();
2056
2056
  const trimmedTranscript = browserTranscript.trim();
2057
+ // Non-null when Web Speech died before we asked it to stop, so whatever
2058
+ // it captured covers only part of the recording. Send it with the text:
2059
+ // a partial transcript must never be stored as the finished one, or the
2060
+ // cloud fallback is suppressed and the user keeps the first few lines.
2061
+ const incompleteReason = liveTranscription.getIncompleteReason();
2057
2062
  if (trimmedTranscript) {
2058
2063
  const transcriptRes = await fetch(
2059
2064
  agentNativePath("/_agent-native/actions/save-browser-transcript"),
@@ -2064,6 +2069,7 @@ export default function RecordRoute() {
2064
2069
  recordingId: pending.id,
2065
2070
  fullText: trimmedTranscript,
2066
2071
  source: "web-speech",
2072
+ failureReason: incompleteReason ?? undefined,
2067
2073
  }),
2068
2074
  },
2069
2075
  ).catch((err) => {
@@ -2086,9 +2092,11 @@ export default function RecordRoute() {
2086
2092
  recordingId: pending.id,
2087
2093
  fullText: "",
2088
2094
  source: "web-speech",
2089
- failureReason: liveTranscription.supported
2090
- ? "Browser native transcription returned no speech before recording stopped."
2091
- : "Browser Web Speech recognition is unavailable in this browser.",
2095
+ failureReason:
2096
+ incompleteReason ??
2097
+ (liveTranscription.supported
2098
+ ? "Browser native transcription returned no speech before recording stopped."
2099
+ : "Browser Web Speech recognition is unavailable in this browser."),
2092
2100
  }),
2093
2101
  },
2094
2102
  ).catch((err) => {
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-28
4
+ ---
5
+
6
+ Browser recordings no longer save a truncated transcript as complete when speech recognition stops early
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-28
4
+ ---
5
+
6
+ Browser recordings no longer stop transcribing partway through; a partial live transcript is now flagged instead of saved as the finished one.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-28
4
+ ---
5
+
6
+ Desktop full-screen clips now get an instant local transcript (and a real title) instead of waiting on cloud transcription
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-28
4
+ ---
5
+
6
+ Long transcriptions no longer show a false 'transcription stopped' error while they are still running
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-28
4
+ ---
5
+
6
+ Restarting for an update now installs the newest release instead of a stale download that immediately asks you to restart again.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-28
4
+ ---
5
+
6
+ The meeting recording pill now anchors to the right edge of the screen, shows a live waveform that reacts to both you and other speakers, and reads as a proper pill.