@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
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Per-app member roles — an overlay on org membership, never a second roster.
3
+ *
4
+ * Three role systems now coexist and answer different questions:
5
+ * - org role (`org_members.role`) — what may this person do to the *team*?
6
+ * - app role (this module) — what may this person do inside *one app*?
7
+ * - share role (`sharing/`) — what may this person do to *one row*?
8
+ *
9
+ * They do not imply one another in either direction. In particular an app
10
+ * `admin` is not an org admin: nothing here is ever read by the org handlers.
11
+ */
12
+
13
+ import type { ActionRunContext } from "../action.js";
14
+ import { getDbExec } from "../db/client.js";
15
+ import {
16
+ getRequestOrgId,
17
+ getRequestUserEmail,
18
+ } from "../server/request-context.js";
19
+ import { ForbiddenError } from "../sharing/access.js";
20
+
21
+ /**
22
+ * The serializable half of an app-role declaration. Kept free of database
23
+ * imports so the same object can be passed to `defineAppRoles` on the server
24
+ * and to `<TeamPage appRoles={...} />` in the browser bundle.
25
+ */
26
+ export interface AppRolesDescriptor<R extends string = string> {
27
+ /**
28
+ * Stable identifier for the app these roles belong to. Comes from app source,
29
+ * never from a request: the REST layer resolves a descriptor out of the
30
+ * registry below and 404s on anything unregistered, so a client cannot name
31
+ * an app the server did not declare.
32
+ */
33
+ appId: string;
34
+ /**
35
+ * The complete role vocabulary, as an unordered SET. Declaration order
36
+ * carries no meaning — `["ae", "se", "csm"]` are classifications, and ranking
37
+ * them would silently grant the last one everything the first one has.
38
+ * Authorization is always an explicit list of accepted roles.
39
+ */
40
+ roles: readonly R[];
41
+ /**
42
+ * What to show for an org member who has no assignment yet.
43
+ *
44
+ * DISPLAY ONLY — `requireAny` never matches it. If an unassigned member could
45
+ * satisfy a guard, "nobody has assigned this person" and "this person was
46
+ * granted the role" would be the same answer, and later widening the default
47
+ * would silently widen every guard that names it.
48
+ */
49
+ defaultRole?: R;
50
+ /** Optional human labels for the role picker. Falls back to the raw value. */
51
+ roleLabels?: Partial<Record<R, string>>;
52
+ /**
53
+ * Column header for the role in `<TeamPage appRoles={...} />`. Defaults to
54
+ * `appId`. Owned by the app rather than the framework's i18n catalogs — the
55
+ * vocabulary it labels is app-specific, so its translations are too.
56
+ */
57
+ label?: string;
58
+ }
59
+
60
+ /**
61
+ * Why a caller does or does not hold an app role. Each outcome is a distinct
62
+ * value on purpose: collapsing "not in this org" into "no role assigned" would
63
+ * report a membership problem as a permissions problem, and the operator would
64
+ * go looking in the wrong settings page.
65
+ *
66
+ * A failed lookup is NOT in this union — it throws. A database error is not an
67
+ * answer, and returning one of these statuses for it would deny loudly in
68
+ * exactly the same shape as a legitimate denial.
69
+ */
70
+ export type AppRoleLookup<R extends string = string> =
71
+ | { status: "assigned"; role: R; orgId: string }
72
+ | { status: "unassigned"; orgId: string }
73
+ | { status: "not-a-member"; orgId: string }
74
+ | { status: "no-identity" }
75
+ | { status: "no-org" };
76
+
77
+ export interface AppRoleCaller {
78
+ userEmail?: string | null;
79
+ orgId?: string | null;
80
+ }
81
+
82
+ export interface AppRoles<R extends string = string> {
83
+ descriptor: AppRolesDescriptor<R>;
84
+ appId: string;
85
+ roles: readonly R[];
86
+ /** Resolve without throwing on denial — for UI and for building richer guards. */
87
+ resolve: (caller?: AppRoleCaller) => Promise<AppRoleLookup<R>>;
88
+ /** Throw unless the caller holds one of `allowed`; returns the matched role. */
89
+ assertAny: (allowed: readonly R[], caller?: AppRoleCaller) => Promise<R>;
90
+ /** Guard for `defineAction({ authorize })`. Accepts an explicit assignment only. */
91
+ requireAny: (
92
+ ...allowed: R[]
93
+ ) => (args: unknown, ctx?: ActionRunContext) => Promise<void>;
94
+ }
95
+
96
+ const registry = new Map<string, AppRolesDescriptor<string>>();
97
+
98
+ /** Descriptor for a registered app id, or undefined. Used by the REST layer. */
99
+ export function getRegisteredAppRoles(
100
+ appId: string,
101
+ ): AppRolesDescriptor<string> | undefined {
102
+ return registry.get(appId);
103
+ }
104
+
105
+ export function listRegisteredAppRoles(): AppRolesDescriptor<string>[] {
106
+ return [...registry.values()];
107
+ }
108
+
109
+ const normalizeEmail = (email: string): string => email.trim().toLowerCase();
110
+
111
+ /**
112
+ * `undefined` means "the caller did not say" and falls back to the ambient
113
+ * request context; an explicit `null` means "this run has no user/org" and must
114
+ * NOT. Collapsing the two lets a system or cron caller that deliberately
115
+ * declared no identity inherit whichever request happens to be on the stack,
116
+ * and authorize as that person.
117
+ */
118
+ function callerIdentity(caller?: AppRoleCaller): {
119
+ email: string | null;
120
+ orgId: string | null;
121
+ } {
122
+ const email =
123
+ caller?.userEmail !== undefined ? caller.userEmail : getRequestUserEmail();
124
+ const orgId = caller?.orgId !== undefined ? caller.orgId : getRequestOrgId();
125
+ return {
126
+ email: email && email.trim() ? email.trim() : null,
127
+ orgId: orgId && orgId.trim() ? orgId.trim() : null,
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Declare an app's role vocabulary and get the server-side guards for it.
133
+ *
134
+ * ```ts
135
+ * export const coachAccess = defineAppRoles({
136
+ * appId: "coach",
137
+ * roles: ["member", "coach-admin"] as const,
138
+ * defaultRole: "member",
139
+ * });
140
+ * ```
141
+ */
142
+ export function defineAppRoles<const R extends string>(
143
+ descriptor: AppRolesDescriptor<R>,
144
+ ): AppRoles<R> {
145
+ const appId = descriptor.appId.trim();
146
+ if (!appId) throw new Error("defineAppRoles: appId is required");
147
+ if (!descriptor.roles.length) {
148
+ throw new Error(`defineAppRoles(${appId}): at least one role is required`);
149
+ }
150
+ if (
151
+ descriptor.defaultRole &&
152
+ !descriptor.roles.includes(descriptor.defaultRole)
153
+ ) {
154
+ throw new Error(
155
+ `defineAppRoles(${appId}): defaultRole "${descriptor.defaultRole}" is not in roles`,
156
+ );
157
+ }
158
+
159
+ const existing = registry.get(appId);
160
+ if (existing && existing !== (descriptor as AppRolesDescriptor<string>)) {
161
+ // Two vocabularies behind one appId means the REST layer and the guards can
162
+ // disagree about which roles exist, and the settings UI would offer roles
163
+ // no guard accepts.
164
+ throw new Error(
165
+ `defineAppRoles: appId "${appId}" is already declared with a different descriptor`,
166
+ );
167
+ }
168
+ registry.set(appId, descriptor as AppRolesDescriptor<string>);
169
+
170
+ const resolve = (caller?: AppRoleCaller) =>
171
+ resolveAppRole(descriptor, caller);
172
+
173
+ const assertAny = async (
174
+ allowed: readonly R[],
175
+ caller?: AppRoleCaller,
176
+ ): Promise<R> => {
177
+ const lookup = await resolve(caller);
178
+ if (lookup.status === "assigned" && allowed.includes(lookup.role)) {
179
+ return lookup.role;
180
+ }
181
+ throw new ForbiddenError(denialMessage(appId, allowed, lookup));
182
+ };
183
+
184
+ return {
185
+ descriptor,
186
+ appId,
187
+ roles: descriptor.roles,
188
+ resolve,
189
+ assertAny,
190
+ requireAny: (...allowed: R[]) => {
191
+ // An empty accepted set can never match. Failing closed at request time
192
+ // would be correct but mute — `authorize: coachAccess.requireAny()` is a
193
+ // dropped argument, and it should fail where the mistake is, not as an
194
+ // unexplained 403 in production.
195
+ if (!allowed.length) {
196
+ throw new Error(
197
+ `defineAppRoles(${appId}): requireAny() needs at least one accepted role`,
198
+ );
199
+ }
200
+ const unknown = allowed.filter((r) => !descriptor.roles.includes(r));
201
+ if (unknown.length) {
202
+ throw new Error(
203
+ `defineAppRoles(${appId}): requireAny() names undeclared role(s) ${unknown.join(", ")}`,
204
+ );
205
+ }
206
+ return async (_args: unknown, ctx?: ActionRunContext) => {
207
+ await assertAny(allowed, {
208
+ userEmail: ctx?.userEmail,
209
+ orgId: ctx?.orgId,
210
+ });
211
+ };
212
+ },
213
+ };
214
+ }
215
+
216
+ function denialMessage<R extends string>(
217
+ appId: string,
218
+ allowed: readonly R[],
219
+ lookup: AppRoleLookup<R>,
220
+ ): string {
221
+ const need = `Requires ${appId} role ${allowed.join(" or ")}`;
222
+ switch (lookup.status) {
223
+ case "assigned":
224
+ return `${need} (have ${lookup.role})`;
225
+ case "unassigned":
226
+ return `${need} (no ${appId} role assigned)`;
227
+ case "not-a-member":
228
+ return `${need} (not a member of this organization)`;
229
+ case "no-org":
230
+ return `${need} (no active organization)`;
231
+ case "no-identity":
232
+ return `${need} (no authenticated user)`;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Single read behind every app-role decision.
238
+ *
239
+ * Membership and assignment are resolved in ONE statement so an assignment left
240
+ * behind by a removed member can never authorize: the row only reaches the
241
+ * result set through `org_members`. Splitting this into two queries would also
242
+ * let a member removed between them still pass.
243
+ */
244
+ export async function resolveAppRole<R extends string>(
245
+ descriptor: AppRolesDescriptor<R>,
246
+ caller?: AppRoleCaller,
247
+ ): Promise<AppRoleLookup<R>> {
248
+ const { email, orgId } = callerIdentity(caller);
249
+ if (!email) return { status: "no-identity" };
250
+ if (!orgId) return { status: "no-org" };
251
+
252
+ const { rows } = await getDbExec().execute({
253
+ sql: `SELECT r.role AS "appRole"
254
+ FROM org_members m
255
+ LEFT JOIN app_member_roles r
256
+ ON r.org_id = m.org_id
257
+ AND r.app_id = ?
258
+ AND LOWER(r.email) = LOWER(m.email)
259
+ WHERE m.org_id = ? AND LOWER(m.email) = ?
260
+ LIMIT 1`,
261
+ args: [descriptor.appId, orgId, normalizeEmail(email)],
262
+ });
263
+
264
+ const row = rows[0] as { appRole?: unknown; approle?: unknown } | undefined;
265
+ if (!row) return { status: "not-a-member", orgId };
266
+
267
+ const raw = row.appRole ?? row.approle;
268
+ if (raw === null || raw === undefined) return { status: "unassigned", orgId };
269
+
270
+ const role = String(raw) as R;
271
+ // A stored value outside the declared vocabulary is a role that was removed
272
+ // from the app's declaration while assignments still referenced it. Treating
273
+ // it as unassigned keeps a retired role from satisfying a guard that no
274
+ // longer knows what it meant.
275
+ if (!descriptor.roles.includes(role)) return { status: "unassigned", orgId };
276
+
277
+ return { status: "assigned", role, orgId };
278
+ }
279
+
280
+ export interface AppMemberRoleRow {
281
+ email: string;
282
+ role: string;
283
+ }
284
+
285
+ /** Every assignment for current members of one app in one org. */
286
+ export async function listAppMemberRoles(
287
+ appId: string,
288
+ orgId: string,
289
+ ): Promise<AppMemberRoleRow[]> {
290
+ const { rows } = await getDbExec().execute({
291
+ sql: `SELECT r.email, r.role
292
+ FROM app_member_roles r
293
+ INNER JOIN org_members m
294
+ ON m.org_id = r.org_id
295
+ AND LOWER(m.email) = LOWER(r.email)
296
+ WHERE r.org_id = ? AND r.app_id = ?`,
297
+ args: [orgId, appId],
298
+ });
299
+ return rows.map((r: any) => ({
300
+ email: String(r.email),
301
+ role: String(r.role),
302
+ }));
303
+ }
304
+
305
+ const nanoid = (): string =>
306
+ globalThis.crypto?.randomUUID?.().replace(/-/g, "") ??
307
+ Math.random().toString(36).slice(2) + Date.now().toString(36);
308
+
309
+ /**
310
+ * Assign or clear one member's app role. `role: null` deletes the assignment,
311
+ * which is distinct from assigning the descriptor's `defaultRole` — an unassigned
312
+ * member satisfies no guard, whereas a default is only ever a display value.
313
+ *
314
+ * Callers must have already established that the target is an org member and
315
+ * that the actor may manage the org; this function does not re-check either.
316
+ */
317
+ export async function setAppMemberRole(opts: {
318
+ appId: string;
319
+ orgId: string;
320
+ email: string;
321
+ role: string | null;
322
+ updatedBy: string;
323
+ }): Promise<void> {
324
+ const exec = getDbExec();
325
+ const email = normalizeEmail(opts.email);
326
+
327
+ if (opts.role === null) {
328
+ await exec.execute({
329
+ sql: `DELETE FROM app_member_roles
330
+ WHERE org_id = ? AND app_id = ? AND LOWER(email) = ?`,
331
+ args: [opts.orgId, opts.appId, email],
332
+ });
333
+ return;
334
+ }
335
+
336
+ const now = Date.now();
337
+ await exec.execute({
338
+ sql: `INSERT INTO app_member_roles
339
+ (id, org_id, app_id, email, role, updated_by, updated_at)
340
+ VALUES (?, ?, ?, ?, ?, ?, ?)
341
+ ON CONFLICT (org_id, app_id, LOWER(email)) DO UPDATE SET
342
+ role = excluded.role,
343
+ updated_by = excluded.updated_by,
344
+ updated_at = excluded.updated_at`,
345
+ args: [
346
+ nanoid(),
347
+ opts.orgId,
348
+ opts.appId,
349
+ email,
350
+ opts.role,
351
+ opts.updatedBy,
352
+ now,
353
+ ],
354
+ });
355
+ }
@@ -39,13 +39,34 @@ export type { AcceptPendingResult } from "./accept-pending.js";
39
39
  export { autoJoinDomainMatchingOrgs } from "./auto-join-domain.js";
40
40
  export type { AutoJoinDomainResult } from "./auto-join-domain.js";
41
41
 
42
+ export {
43
+ defineAppRoles,
44
+ getRegisteredAppRoles,
45
+ listRegisteredAppRoles,
46
+ listAppMemberRoles,
47
+ resolveAppRole,
48
+ setAppMemberRole,
49
+ } from "./app-roles.js";
50
+ export type {
51
+ AppRoles,
52
+ AppRolesDescriptor,
53
+ AppRoleCaller,
54
+ AppRoleLookup,
55
+ AppMemberRoleRow,
56
+ } from "./app-roles.js";
57
+
42
58
  export { ORG_MIGRATIONS } from "./migrations.js";
43
59
 
44
60
  export { createOrgPlugin, defaultOrgPlugin } from "./plugin.js";
45
61
 
46
62
  // Drizzle schema (re-exported so templates can write typed queries against
47
63
  // org tables without redefining the schema themselves).
48
- export { organizations, orgMembers, orgInvitations } from "./schema.js";
64
+ export {
65
+ organizations,
66
+ orgMembers,
67
+ orgInvitations,
68
+ appMemberRoles,
69
+ } from "./schema.js";
49
70
 
50
71
  // Individual handlers — exported so templates can compose a custom org plugin
51
72
  // while still using the framework-provided handlers.
@@ -65,4 +86,9 @@ export {
65
86
  receiveA2ASecretHandler,
66
87
  } from "./handlers.js";
67
88
 
89
+ export {
90
+ listAppRolesHandler,
91
+ setAppRoleHandler,
92
+ } from "./app-roles-handlers.js";
93
+
68
94
  export { isFreeEmailProvider } from "./free-email-providers.js";
@@ -102,4 +102,26 @@ export const ORG_MIGRATIONS = [
102
102
  name: "org-members-unique-lower-email-idx",
103
103
  sql: `CREATE UNIQUE INDEX IF NOT EXISTS org_members_org_lower_email_uidx ON org_members (org_id, LOWER(email))`,
104
104
  },
105
+ {
106
+ version: 1011,
107
+ sql: `CREATE TABLE IF NOT EXISTS app_member_roles (
108
+ id TEXT PRIMARY KEY,
109
+ org_id TEXT NOT NULL,
110
+ app_id TEXT NOT NULL,
111
+ email TEXT NOT NULL,
112
+ role TEXT NOT NULL,
113
+ updated_by TEXT NOT NULL,
114
+ updated_at INTEGER NOT NULL
115
+ )`,
116
+ },
117
+ {
118
+ // Every guarded action resolves (org_id, app_id, LOWER(email)) on the
119
+ // request path, and the assignment write upserts on the same key. Unique
120
+ // rather than plain so a concurrent double-assign is rejected by the
121
+ // database instead of leaving two rows whose winner depends on read order.
122
+ version: 1012,
123
+ name: "app-member-roles-unique-org-app-lower-email-idx",
124
+ sql: `CREATE UNIQUE INDEX IF NOT EXISTS app_member_roles_org_app_lower_email_uidx
125
+ ON app_member_roles (org_id, app_id, LOWER(email))`,
126
+ },
105
127
  ];
@@ -13,6 +13,10 @@ import {
13
13
  FRAMEWORK_PREFIX,
14
14
  markDefaultPluginProvided,
15
15
  } from "../server/framework-request-handler.js";
16
+ import {
17
+ listAppRolesHandler,
18
+ setAppRoleHandler,
19
+ } from "./app-roles-handlers.js";
16
20
  import {
17
21
  getMyOrgHandler,
18
22
  createOrgHandler,
@@ -50,6 +54,8 @@ const ORG_PREFIX = `${FRAMEWORK_PREFIX}/org`;
50
54
  * PUT /_agent-native/org/switch — switch active org
51
55
  * GET /_agent-native/org/members — list members of active org
52
56
  * DELETE /_agent-native/org/members/:email — remove member (owner/admin only)
57
+ * GET /_agent-native/org/app-roles?appId=X — app role vocabulary + assignments
58
+ * PUT /_agent-native/org/app-roles/:email — assign/clear app role (owner/admin)
53
59
  * GET /_agent-native/org/invitations — list pending invites
54
60
  * POST /_agent-native/org/invitations — invite by email
55
61
  * POST /_agent-native/org/invitations/:id/accept — accept an invitation
@@ -82,6 +88,27 @@ export function createOrgPlugin(): NitroPluginDef {
82
88
  }),
83
89
  );
84
90
 
91
+ // /app-roles and /app-roles/:email — per-app role overlay on the roster.
92
+ app.use(
93
+ `${ORG_PREFIX}/app-roles`,
94
+ defineEventHandler(async (event: H3Event) => {
95
+ const tail = getRequestURL(event).pathname || "/";
96
+ const method = getMethod(event);
97
+ if (tail === "" || tail === "/") {
98
+ if (method !== "GET") {
99
+ setResponseStatus(event, 405);
100
+ return { error: "Method not allowed" };
101
+ }
102
+ return listAppRolesHandler(event);
103
+ }
104
+ if (method !== "PUT") {
105
+ setResponseStatus(event, 405);
106
+ return { error: "Method not allowed" };
107
+ }
108
+ return setAppRoleHandler(event);
109
+ }),
110
+ );
111
+
85
112
  // /members, /members/:email, /members/:email/role — dispatch by path-
86
113
  // tail + method in a single handler so H3's prefix-based `app.use`
87
114
  // doesn't route a DELETE for /members/alice@example.com to the
@@ -17,6 +17,21 @@ export const orgMembers = table("org_members", {
17
17
  joinedAt: integer("joined_at").notNull(),
18
18
  });
19
19
 
20
+ /**
21
+ * Per-app role assignments, keyed to an existing `org_members` row. An app role
22
+ * only narrows what a member may do inside one app; it never grants membership,
23
+ * and it never implies an org role.
24
+ */
25
+ export const appMemberRoles = table("app_member_roles", {
26
+ id: text("id").primaryKey(),
27
+ orgId: text("org_id").notNull(),
28
+ appId: text("app_id").notNull(),
29
+ email: text("email").notNull(),
30
+ role: text("role").notNull(),
31
+ updatedBy: text("updated_by").notNull(),
32
+ updatedAt: integer("updated_at").notNull(),
33
+ });
34
+
20
35
  export const orgInvitations = table("org_invitations", {
21
36
  id: text("id").primaryKey(),
22
37
  orgId: text("org_id").notNull(),
@@ -39,6 +39,21 @@ function getDocsDir(): string {
39
39
  return path.join(getDocsRoot(), "content");
40
40
  }
41
41
 
42
+ /**
43
+ * Bundled serverless deploys carry the runtime agent bundle but not the
44
+ * framework doc pages, so a miss there means "not deployed", not "no such
45
+ * doc". Say which, or the agent concludes a documented API does not exist.
46
+ */
47
+ function logMissingFrameworkDocsNote(): void {
48
+ if (fs.existsSync(getDocsDir())) return;
49
+ console.log(
50
+ "\nNote: bundled framework doc pages are not deployed here (only this " +
51
+ "app's AGENTS.md and skills are). A miss above does not mean the page " +
52
+ "does not exist — check the framework docs another way before concluding " +
53
+ "an API is missing.",
54
+ );
55
+ }
56
+
42
57
  function parseFrontmatter(raw: string): {
43
58
  data: Record<string, string>;
44
59
  body: string;
@@ -256,6 +271,7 @@ Options:
256
271
  if (!doc) {
257
272
  console.log(`Doc not found: ${parsed.slug}`);
258
273
  console.log(`Available: ${docs.map((d) => d.slug).join(", ")}`);
274
+ logMissingFrameworkDocsNote();
259
275
  return;
260
276
  }
261
277
  console.log(`# ${doc.title}\n`);
@@ -268,6 +284,7 @@ Options:
268
284
  const results = await searchDocs(parsed.query);
269
285
  if (results.length === 0) {
270
286
  console.log(`No docs found matching "${parsed.query}".`);
287
+ logMissingFrameworkDocsNote();
271
288
  return;
272
289
  }
273
290
  console.log(`Found ${results.length} doc(s) matching "${parsed.query}":\n`);
@@ -70,6 +70,16 @@ function getCorpusRoot(): string {
70
70
  );
71
71
  }
72
72
 
73
+ /**
74
+ * The corpus is a sibling of the package root, so a bundled serverless deploy
75
+ * never carries it. Callers register this tool conditionally: advertising a
76
+ * search tool whose only possible answer is "not found" costs the agent a turn
77
+ * and reads to it like the code genuinely does not exist.
78
+ */
79
+ export function hasSourceCorpus(): boolean {
80
+ return fs.existsSync(getCorpusRoot());
81
+ }
82
+
73
83
  function isProbablyTextFile(filePath: string): boolean {
74
84
  const ext = path.extname(filePath).toLowerCase();
75
85
  if (TEXT_EXTENSIONS.has(ext)) return true;
@@ -270,6 +270,9 @@ export async function createDocsScriptEntries(): Promise<
270
270
 
271
271
  try {
272
272
  const mod = await import("../../scripts/docs/source-search.js");
273
+ if (!mod.hasSourceCorpus()) {
274
+ return entries;
275
+ }
273
276
  entries["source-search"] = wrapCliScript(
274
277
  {
275
278
  description: