@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
@@ -14,7 +14,12 @@ export const METER_IDLE_HEIGHT = 0.14;
14
14
  export const METER_LEVEL_DECAY = 0.82;
15
15
  /** Floor a new sample must clear, so the meter falls smoothly instead of snapping. */
16
16
  export const METER_ATTACK_DECAY = 0.55;
17
- export const METER_SAMPLE_MS = 50;
17
+ /**
18
+ * How long the meter waits for the next capture buffer before decaying on its
19
+ * own. Capture drives the bars; this only covers pause / teardown, where the
20
+ * events stop and a frozen tall bar would claim someone is still talking.
21
+ */
22
+ export const METER_IDLE_MS = 120;
18
23
 
19
24
  /** Fold an incoming 0-1 audio level into the current level. */
20
25
  export function nextMeterLevel(current: number, incoming: number): number {
@@ -23,6 +28,30 @@ export function nextMeterLevel(current: number, incoming: number): number {
23
28
  return Math.max(current * METER_ATTACK_DECAY, clamped);
24
29
  }
25
30
 
31
+ export type MeterSource = "mic" | "system";
32
+ export type MeterSourceLevels = Record<MeterSource, number>;
33
+
34
+ export const EMPTY_METER_SOURCES: MeterSourceLevels = { mic: 0, system: 0 };
35
+
36
+ /**
37
+ * Fold one capture buffer into the per-source levels. Mic and system audio
38
+ * interleave on a single event, so each stream has to decay on its own frames
39
+ * only — one shared level lets a silent mic halve the level of whoever is
40
+ * actually talking on every other event, which pins the meter to its floor.
41
+ */
42
+ export function foldMeterSources(
43
+ levels: MeterSourceLevels,
44
+ source: MeterSource,
45
+ incoming: number,
46
+ ): MeterSourceLevels {
47
+ return { ...levels, [source]: nextMeterLevel(levels[source], incoming) };
48
+ }
49
+
50
+ /** What the bars show: whichever side of the conversation is louder. */
51
+ export function combinedMeterLevel(levels: MeterSourceLevels): number {
52
+ return Math.max(levels.mic, levels.system);
53
+ }
54
+
26
55
  /** One decay tick. Snaps to silence below a threshold so bars settle at rest. */
27
56
  export function decayMeterLevel(current: number): number {
28
57
  const decayed = current * METER_LEVEL_DECAY;
@@ -46,6 +75,6 @@ export function advanceMeterLevels(levels: number[], sample: number): number[] {
46
75
  export function meterBarHeight(level: number, barIndex: number): number {
47
76
  const gain = METER_BAR_GAINS[barIndex] ?? 1;
48
77
  const safe = Number.isFinite(level) ? Math.max(0, Math.min(1, level)) : 0;
49
- const shaped = safe > 0 ? Math.min(1, Math.pow(safe, 0.52) * 1.08) : 0;
78
+ const shaped = safe > 0 ? Math.min(1, Math.pow(safe, 0.45) * 1.25) : 0;
50
79
  return (METER_IDLE_HEIGHT + shaped * gain * (1 - METER_IDLE_HEIGHT)) * 100;
51
80
  }
@@ -138,6 +138,10 @@ const CLOUD_RECORDING_VIDEO_BITRATE_BPS = 8_000_000;
138
138
  const CLOUD_RECORDING_AUDIO_BITRATE_BPS = 128_000;
139
139
  const TRANSCRIPT_SAVE_TIMEOUT_MS = 8_000;
140
140
  const FINALIZING_RESULT_STORAGE_KEY = "clips-finalizing-result";
141
+ const NO_SPEECH_TRANSCRIPT_FAILURE =
142
+ "No speech was captured during this recording. If you spoke or played system audio, check System Audio, Microphone input, Speech Recognition permission, and the selected mic, then retry transcription.";
143
+ const TRANSCRIPTION_START_FAILURE =
144
+ "macOS Speech recognition could not start for this recording. Check Speech Recognition, System Audio, and Microphone permissions, then retry transcription.";
141
145
 
142
146
  function isMacPlatform(): boolean {
143
147
  if (typeof navigator === "undefined") return false;
@@ -2117,6 +2121,51 @@ async function tryStartRewindFullscreenRecording(
2117
2121
  const hasAudio = includeMic || includeSystemAudio;
2118
2122
  let id = folderName;
2119
2123
  let uploadMode: UploadMode = "buffered";
2124
+ let transcriptionCapture: TranscriptionCapture | null = null;
2125
+ let transcriptionAborted = false;
2126
+ let transcriptFailureSaved = false;
2127
+ const saveTranscriptFailure = async (
2128
+ failureReason: string,
2129
+ ): Promise<boolean> => {
2130
+ if (localOnly || !hasAudio || transcriptFailureSaved || !id) return false;
2131
+ transcriptFailureSaved = true;
2132
+ return saveRecordingTranscriptFailure(
2133
+ params.serverUrl,
2134
+ id,
2135
+ failureReason,
2136
+ params.authToken,
2137
+ );
2138
+ };
2139
+ // Whisper always subscribes to the shared Rewind producer's microphone leg,
2140
+ // so a mic-off clip can only fall back to a competing physical capture. Leave
2141
+ // those to server-side transcription instead of fighting the live producer.
2142
+ const canTranscribeLocally = !localOnly && includeMic;
2143
+ const startRewindTranscription = async () => {
2144
+ if (!canTranscribeLocally || transcriptionCapture) return;
2145
+ // Runs after `rewind_clip_prepare`, so the Rewind producer already carries
2146
+ // mic (+system) and whisper attaches to it instead of opening its own
2147
+ // ScreenCaptureKit stream — which would mute the clip's own audio legs.
2148
+ // This runs inside the countdown's Promise.all: a transcription failure
2149
+ // must never reject there, or it would abort the recording itself.
2150
+ const capture = await startTranscriptionCapture(
2151
+ { deviceId: params.micId, label: params.micLabel },
2152
+ includeSystemAudio,
2153
+ { voiceProcessing: false },
2154
+ ).catch((err) => {
2155
+ console.warn("[clips-recorder] rewind transcription start failed:", err);
2156
+ return null;
2157
+ });
2158
+ // A countdown cancel can land while the engine was still starting; the
2159
+ // catch below already ran, so tear this down instead of leaking capture.
2160
+ if (transcriptionAborted) {
2161
+ await capture?.cancel().catch(() => {});
2162
+ return;
2163
+ }
2164
+ transcriptionCapture = capture;
2165
+ if (!capture && shouldSaveLocalTranscriptionStartupFailure()) {
2166
+ void saveTranscriptFailure(TRANSCRIPTION_START_FAILURE);
2167
+ }
2168
+ };
2120
2169
  await invoke("show_preparing");
2121
2170
  const recordingPromise = localOnly
2122
2171
  ? Promise.resolve<{ id: string; uploadMode: UploadMode }>({
@@ -2165,7 +2214,12 @@ async function tryStartRewindFullscreenRecording(
2165
2214
  async countdown() {
2166
2215
  console.log("[rewind-latency] countdown shown after preparation");
2167
2216
  await invoke("hide_preparing").catch(() => {});
2168
- await runRecordingCountdown(true);
2217
+ // Overlap whisper startup with the countdown so the capture boundary
2218
+ // never waits on it, but have both settled before activation.
2219
+ await Promise.all([
2220
+ runRecordingCountdown(true),
2221
+ startRewindTranscription(),
2222
+ ]);
2169
2223
  console.log("[rewind-latency] countdown completed");
2170
2224
  },
2171
2225
  async activate(preparedRecording) {
@@ -2174,6 +2228,13 @@ async function tryStartRewindFullscreenRecording(
2174
2228
  console.log(
2175
2229
  `[rewind-latency] countdown completion to start acknowledgement ${Math.round(performance.now() - activationStarted)}ms`,
2176
2230
  );
2231
+ // Rebase transcript segment timestamps onto the real recording start.
2232
+ await transcriptionCapture?.resetTimeline().catch((err) => {
2233
+ console.warn(
2234
+ "[clips-recorder] transcription timeline reset failed:",
2235
+ err,
2236
+ );
2237
+ });
2177
2238
  return preparedRecording;
2178
2239
  },
2179
2240
  onActivated() {
@@ -2194,6 +2255,13 @@ async function tryStartRewindFullscreenRecording(
2194
2255
  }
2195
2256
  } catch (err) {
2196
2257
  await invoke("hide_preparing").catch(() => {});
2258
+ transcriptionAborted = true;
2259
+ // Cast: `transcriptionCapture` is only assigned inside the
2260
+ // `startRewindTranscription` closure, which TS's control-flow analysis
2261
+ // cannot see past — it narrows this read to `never`.
2262
+ await (transcriptionCapture as TranscriptionCapture | null)
2263
+ ?.cancel()
2264
+ .catch(() => {});
2197
2265
  if (id) forgetRewindClipOrigin(id);
2198
2266
  await invoke("rewind_clip_cancel").catch(() => {});
2199
2267
  audioCue.cleanup();
@@ -2300,6 +2368,25 @@ async function tryStartRewindFullscreenRecording(
2300
2368
  `[rewind-latency] stop command dispatched in ${Math.round(performance.now() - stopStarted)}ms`,
2301
2369
  );
2302
2370
  uploadPromise.catch(() => {});
2371
+ // Stop transcription only after the clip stop is dispatched (Rust
2372
+ // measures duration when it runs, and stop() blocks on a settle
2373
+ // window), then persist it alongside the upload.
2374
+ const capturedTranscript = await transcriptionCapture
2375
+ ?.stop()
2376
+ .catch((err) => {
2377
+ console.warn("[clips-recorder] transcript stop failed:", err);
2378
+ return null;
2379
+ });
2380
+ const transcriptSavePromise = capturedTranscript?.text.trim()
2381
+ ? saveRecordingTranscript(
2382
+ params.serverUrl,
2383
+ id,
2384
+ capturedTranscript,
2385
+ params.authToken,
2386
+ )
2387
+ : hasAudio
2388
+ ? saveTranscriptFailure(NO_SPEECH_TRANSCRIPT_FAILURE)
2389
+ : Promise.resolve(true);
2303
2390
  await invoke("hide_recording_chrome").catch(() => {});
2304
2391
  if (wantsCamera) await invoke("close_bubble").catch(() => {});
2305
2392
  await openNativeUploadUrl(
@@ -2315,6 +2402,19 @@ async function tryStartRewindFullscreenRecording(
2315
2402
  authToken: params.authToken,
2316
2403
  });
2317
2404
  }
2405
+ if (
2406
+ !(await transcriptSavePromise) &&
2407
+ capturedTranscript?.text.trim()
2408
+ ) {
2409
+ // The first write races the upload; retry once after finalize so
2410
+ // a pre-ready action request can't strand a captured transcript.
2411
+ void saveRecordingTranscript(
2412
+ params.serverUrl,
2413
+ id,
2414
+ capturedTranscript,
2415
+ params.authToken,
2416
+ );
2417
+ }
2318
2418
  return { recordingId: uploaded.recordingId, viewUrl };
2319
2419
  } catch (err) {
2320
2420
  if (
@@ -2346,6 +2446,10 @@ async function tryStartRewindFullscreenRecording(
2346
2446
  cancelPromise = (async () => {
2347
2447
  stopped = true;
2348
2448
  cleanupUi();
2449
+ transcriptionAborted = true;
2450
+ void transcriptionCapture?.cancel().catch((err) => {
2451
+ console.warn("[clips-recorder] transcription cancel failed:", err);
2452
+ });
2349
2453
  await invoke("rewind_clip_cancel").catch(() => {});
2350
2454
  audioCue.cleanup();
2351
2455
  await invoke("hide_overlays").catch(() => {});
@@ -2376,6 +2480,11 @@ async function tryStartRewindFullscreenRecording(
2376
2480
  pausedAt = null;
2377
2481
  }
2378
2482
  pauseRequestedAt = null;
2483
+ // The Rewind producer keeps running while a clip is paused, so the
2484
+ // transcript would otherwise keep collecting speech the clip never saw.
2485
+ void (
2486
+ paused ? transcriptionCapture?.pause() : transcriptionCapture?.resume()
2487
+ )?.catch(() => {});
2379
2488
  emitState(paused);
2380
2489
  },
2381
2490
  onError(_err, _attemptedPaused) {
@@ -2459,9 +2568,7 @@ async function startNativeFullscreenRecording(
2459
2568
  !transcriptionCapture &&
2460
2569
  shouldSaveLocalTranscriptionStartupFailure()
2461
2570
  ) {
2462
- void saveTranscriptFailure(
2463
- "macOS Speech recognition could not start for this recording. Check Speech Recognition, System Audio, and Microphone permissions, then retry transcription.",
2464
- );
2571
+ void saveTranscriptFailure(TRANSCRIPTION_START_FAILURE);
2465
2572
  }
2466
2573
  };
2467
2574
 
@@ -2965,9 +3072,7 @@ async function startNativeFullscreenRecording(
2965
3072
  params.authToken,
2966
3073
  )
2967
3074
  : wantsRecordedAudio
2968
- ? saveTranscriptFailure(
2969
- "No speech was captured during this recording. If you spoke or played system audio, check System Audio, Microphone input, Speech Recognition permission, and the selected mic, then retry transcription.",
2970
- )
3075
+ ? saveTranscriptFailure(NO_SPEECH_TRANSCRIPT_FAILURE)
2971
3076
  : Promise.resolve(true);
2972
3077
 
2973
3078
  // The finalizing window owns the whole stop -> optimized upload ->
@@ -4127,9 +4232,7 @@ async function startRecordingInner(
4127
4232
  !transcriptionCapture &&
4128
4233
  shouldSaveLocalTranscriptionStartupFailure()
4129
4234
  ) {
4130
- void saveTranscriptFailure(
4131
- "macOS Speech recognition could not start for this recording. Check Speech Recognition, System Audio, and Microphone permissions, then retry transcription.",
4132
- );
4235
+ void saveTranscriptFailure(TRANSCRIPTION_START_FAILURE);
4133
4236
  }
4134
4237
 
4135
4238
  // 6. Bubble + toolbar visibility are owned by the popover's session
@@ -4275,9 +4378,7 @@ async function startRecordingInner(
4275
4378
  params.authToken,
4276
4379
  );
4277
4380
  } else if (wantsRecordedAudio) {
4278
- await saveTranscriptFailure(
4279
- "No speech was captured during this recording. If you spoke or played system audio, check System Audio, Microphone input, Speech Recognition permission, and the selected mic, then retry transcription.",
4280
- );
4381
+ await saveTranscriptFailure(NO_SPEECH_TRANSCRIPT_FAILURE);
4281
4382
  }
4282
4383
  }
4283
4384
  if (popoverOwnsCamera) {
@@ -44,18 +44,27 @@ function setStatus(next: UpdateStatus) {
44
44
  }
45
45
 
46
46
  async function runCheck() {
47
- if (cachedStatus.state === "downloaded") return;
48
47
  if (checkInFlight) return checkInFlight;
49
48
 
49
+ // A staged update keeps checking on the normal cadence. Stopping here is what
50
+ // made "Restart to update" land on a binary that was already out of date and
51
+ // immediately ask to restart again — every check runs against the *running*
52
+ // version, so it re-offers the staged version until a newer one ships.
53
+ const staged =
54
+ cachedStatus.state === "downloaded" ? cachedStatus.version : null;
55
+
50
56
  lastCheckStartedAt = Date.now();
51
57
  checkInFlight = (async () => {
52
58
  try {
53
- setStatus({ state: "checking" });
59
+ if (!staged) setStatus({ state: "checking" });
54
60
  const update = await check();
55
61
  if (!update) {
56
- setStatus({ state: "not-available" });
62
+ // The endpoint answering "current" while a newer bundle sits staged is
63
+ // a contradiction, not a reason to drop the download. Keep it staged.
64
+ if (!staged) setStatus({ state: "not-available" });
57
65
  return;
58
66
  }
67
+ if (staged && update.version === staged) return;
59
68
  pendingUpdate = update;
60
69
  const version = update.version;
61
70
  const notes = update.body ?? undefined;
@@ -98,7 +107,6 @@ async function runCheck() {
98
107
 
99
108
  function maybeCheckForUpdate() {
100
109
  if (!canRunUpdateChecks()) return;
101
- if (cachedStatus.state === "downloaded") return;
102
110
  if (
103
111
  checkInFlight ||
104
112
  Date.now() - lastCheckStartedAt < UPDATE_FOCUS_CHECK_MIN_INTERVAL_MS
@@ -3,12 +3,16 @@ import { useEffect, useRef, useState } from "react";
3
3
 
4
4
  import {
5
5
  advanceMeterLevels,
6
+ combinedMeterLevel,
6
7
  decayMeterLevel,
8
+ EMPTY_METER_SOURCES,
9
+ foldMeterSources,
7
10
  METER_BAR_COUNT,
8
11
  METER_BAR_GAINS,
9
- METER_SAMPLE_MS,
12
+ METER_IDLE_MS,
10
13
  meterBarHeight,
11
- nextMeterLevel,
14
+ type MeterSource,
15
+ type MeterSourceLevels,
12
16
  } from "../lib/audio-meter";
13
17
 
14
18
  interface LiveAudioBarsProps {
@@ -28,24 +32,33 @@ export function LiveAudioBars({
28
32
  const [levels, setLevels] = useState<number[]>(() =>
29
33
  new Array(METER_BAR_COUNT).fill(0),
30
34
  );
31
- const levelRef = useRef(0);
35
+ const sourcesRef = useRef<MeterSourceLevels>(EMPTY_METER_SOURCES);
36
+ const lastEventRef = useRef(0);
32
37
 
33
38
  useEffect(() => {
34
39
  let stopped = false;
35
40
  let unlisten: (() => void) | null = null;
36
41
 
37
- const sampleTimer = window.setInterval(() => {
38
- const current = levelRef.current;
39
- levelRef.current = decayMeterLevel(current);
40
- setLevels((prev) => advanceMeterLevels(prev, current));
41
- }, METER_SAMPLE_MS);
42
-
43
- listen<{ level?: number }>("voice:audio-level", (event) => {
44
- levelRef.current = nextMeterLevel(
45
- levelRef.current,
46
- Number(event.payload?.level),
42
+ const push = () =>
43
+ setLevels((prev) =>
44
+ advanceMeterLevels(prev, combinedMeterLevel(sourcesRef.current)),
47
45
  );
48
- })
46
+
47
+ // Drive the bars straight off the capture events rather than resampling on
48
+ // a timer — both taps already emit at ~25 Hz, and a timer tick landing
49
+ // between them just showed a decayed copy of a live level.
50
+ listen<{ level?: number; source?: MeterSource }>(
51
+ "voice:audio-level",
52
+ (event) => {
53
+ sourcesRef.current = foldMeterSources(
54
+ sourcesRef.current,
55
+ event.payload?.source === "system" ? "system" : "mic",
56
+ Number(event.payload?.level),
57
+ );
58
+ lastEventRef.current = Date.now();
59
+ push();
60
+ },
61
+ )
49
62
  .then((cleanup) => {
50
63
  if (stopped) {
51
64
  cleanup();
@@ -55,9 +68,21 @@ export function LiveAudioBars({
55
68
  })
56
69
  .catch(() => {});
57
70
 
71
+ // Capture buffers keep arriving through silence, so they settle the meter on
72
+ // their own. This only covers pause / teardown, where the events stop.
73
+ const idleTimer = window.setInterval(() => {
74
+ if (Date.now() - lastEventRef.current < METER_IDLE_MS) return;
75
+ if (combinedMeterLevel(sourcesRef.current) === 0) return;
76
+ sourcesRef.current = {
77
+ mic: decayMeterLevel(sourcesRef.current.mic),
78
+ system: decayMeterLevel(sourcesRef.current.system),
79
+ };
80
+ push();
81
+ }, METER_IDLE_MS);
82
+
58
83
  return () => {
59
84
  stopped = true;
60
- window.clearInterval(sampleTimer);
85
+ window.clearInterval(idleTimer);
61
86
  unlisten?.();
62
87
  };
63
88
  }, []);
@@ -824,14 +824,14 @@ body[data-clips-route="recording-pill"] #root {
824
824
 
825
825
  .live-audio-bars-compact {
826
826
  width: 24px;
827
- height: 22px;
828
- gap: 3px;
827
+ height: 24px;
828
+ gap: 3.5px;
829
829
  }
830
830
 
831
831
  .live-audio-bar {
832
832
  display: block;
833
- width: 2.5px;
834
- min-height: 2.5px;
833
+ width: 3.5px;
834
+ min-height: 3.5px;
835
835
  border-radius: 999px;
836
836
  background: rgba(132, 204, 22, 0.98);
837
837
  box-shadow: 0 0 5px rgba(132, 204, 22, 0.35);
@@ -5499,7 +5499,10 @@ body[data-clips-route="recording-pill"] #root {
5499
5499
  .pill-inner-compact {
5500
5500
  width: fit-content;
5501
5501
  height: fit-content;
5502
- border-radius: 18px;
5502
+ /* Stadium, not a squircle — the collapsed capsule reads as a pill at any
5503
+ height. A fixed radius on the ~44x64 vertical form looks like a rounded
5504
+ rectangle. */
5505
+ border-radius: 999px;
5503
5506
  cursor: pointer;
5504
5507
  transition:
5505
5508
  background 120ms ease,
@@ -5574,7 +5577,7 @@ body[data-clips-route="recording-pill"] #root {
5574
5577
  gap: 6px;
5575
5578
  height: auto;
5576
5579
  width: auto;
5577
- padding: 12px 10px 10px;
5580
+ padding: 12px 10px;
5578
5581
  cursor: pointer;
5579
5582
  }
5580
5583
 
@@ -5589,7 +5592,7 @@ body[data-clips-route="recording-pill"] #root {
5589
5592
  }
5590
5593
 
5591
5594
  .pill-vertical .pill-wave-meter {
5592
- height: 22px;
5595
+ height: 24px;
5593
5596
  width: 24px;
5594
5597
  }
5595
5598
 
@@ -86,7 +86,8 @@ const PILL_DETACHED_W_LOGICAL: u32 = 180;
86
86
  const PILL_DETACHED_H_LOGICAL: u32 = 40;
87
87
  const PILL_DETACHED_TOP_MARGIN_LOGICAL: u32 = 24;
88
88
  const PILL_DETACHED_RIGHT_MARGIN_LOGICAL: u32 = 24;
89
- const PILL_RIGHT_MARGIN_LOGICAL: u32 = 24;
89
+ /// Gap between the visible capsule and the right screen edge, logical px.
90
+ const PILL_RIGHT_MARGIN_LOGICAL: u32 = 28;
90
91
  const OVERLAY_SHADOW_GUTTER_LOGICAL: f64 = 18.0;
91
92
 
92
93
  #[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
@@ -107,6 +108,15 @@ fn overlay_shadow_gutter_physical(app: &AppHandle) -> u32 {
107
108
  (OVERLAY_SHADOW_GUTTER_LOGICAL * scale_factor(app).max(1.0)).round() as u32
108
109
  }
109
110
 
111
+ /// Screen-edge margin for the *visible* capsule. The window is padded out with
112
+ /// a transparent shadow gutter, so that gutter comes off the window margin —
113
+ /// measured from the window frame the pill floats a whole gutter further from
114
+ /// the edge than the constant says.
115
+ fn edge_margin_physical(app: &AppHandle, logical: u32) -> i32 {
116
+ let margin = (logical as f64 * scale_factor(app)) as i32;
117
+ (margin - overlay_shadow_gutter_physical(app) as i32).max(0)
118
+ }
119
+
110
120
  /// Persist the last-known pill position so the next `show` re-opens at the
111
121
  /// user's chosen spot.
112
122
  fn pill_position_path(app: &AppHandle) -> Option<PathBuf> {
@@ -236,8 +246,7 @@ fn default_bottom_center(app: &AppHandle, w: u32, h: u32) -> (i32, i32) {
236
246
  }
237
247
 
238
248
  fn default_center_right(app: &AppHandle, w: u32, h: u32) -> (i32, i32) {
239
- let scale = scale_factor(app);
240
- let right_margin = (PILL_RIGHT_MARGIN_LOGICAL as f64 * scale) as i32;
249
+ let right_margin = edge_margin_physical(app, PILL_RIGHT_MARGIN_LOGICAL);
241
250
  let (mx, my, mw, mh) = tray_monitor_physical_rect(app);
242
251
  let x = (mx + mw as i32 - w as i32 - right_margin).max(mx);
243
252
  // Center whatever is actually on screen — meetings now open collapsed, so
@@ -283,9 +292,8 @@ fn pill_size_physical(app: &AppHandle, expanded: bool) -> (u32, u32) {
283
292
 
284
293
  /// Default top-right anchor (physical px) for detached mode.
285
294
  fn default_top_right(app: &AppHandle, w: u32, _h: u32) -> (i32, i32) {
286
- let scale = scale_factor(app);
287
- let top_margin = (PILL_DETACHED_TOP_MARGIN_LOGICAL as f64 * scale) as i32;
288
- let right_margin = (PILL_DETACHED_RIGHT_MARGIN_LOGICAL as f64 * scale) as i32;
295
+ let top_margin = edge_margin_physical(app, PILL_DETACHED_TOP_MARGIN_LOGICAL);
296
+ let right_margin = edge_margin_physical(app, PILL_DETACHED_RIGHT_MARGIN_LOGICAL);
289
297
  let (mx, my, mw, _mh) = tray_monitor_physical_rect(app);
290
298
  let x = (mx + mw as i32 - w as i32 - right_margin).max(mx);
291
299
  let y = (my + top_margin).max(my);
@@ -398,15 +406,14 @@ pub async fn recording_pill_show(
398
406
  );
399
407
 
400
408
  if let Some(existing) = app.get_webview_window(PILL_LABEL) {
401
- // Already alive — re-emit context and bring it back into view.
402
- let prev_size = existing.outer_size().ok();
403
- let prev_pos = existing.outer_position().ok();
404
- let previous = match (prev_pos, prev_size) {
405
- (Some(p), Some(s)) => Some((p.x, p.y, s.width, s.height)),
406
- _ => None,
407
- };
409
+ // Already alive — re-emit context and bring it back into view. Re-anchor
410
+ // from the saved/default position for this mode, never from where the
411
+ // window happens to sit: the pill window is reused across sessions, so
412
+ // carrying the previous rect over pins a meeting pill to the last clip
413
+ // recording's bottom-center spot. `previous` exists for expand/collapse,
414
+ // where the anchor must not move.
408
415
  let expanded = PILL_EXPANDED.load(Ordering::Relaxed);
409
- let (w, h, x, y) = anchored_rect(&app, expanded, previous);
416
+ let (w, h, x, y) = anchored_rect(&app, expanded, None);
410
417
  let _ = existing.set_size(tauri::Size::Physical(PhysicalSize::new(w, h)));
411
418
  let _ = existing.set_position(PhysicalPosition::new(x, y));
412
419
  use tauri::Emitter;
@@ -1,5 +1,15 @@
1
1
  export const STALE_PENDING_TRANSCRIPT_MS = 5 * 60 * 1000;
2
2
 
3
+ /**
4
+ * How often a worker that is still transcribing must re-stamp its pending row.
5
+ *
6
+ * `updatedAt` is the only liveness signal a pending row carries, so this must
7
+ * stay well under STALE_PENDING_TRANSCRIPT_MS: a run whose slowest single step
8
+ * (media fetch, ffmpeg extraction, provider call) outlives the heartbeat is
9
+ * indistinguishable from a worker that died.
10
+ */
11
+ export const PENDING_TRANSCRIPT_HEARTBEAT_MS = 60 * 1000;
12
+
3
13
  export const STALE_PENDING_TRANSCRIPT_REASON =
4
14
  "Transcription stopped before it finished. Retry transcription to start a fresh attempt.";
5
15
 
@@ -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
@@ -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: