@agent-native/core 0.111.4 → 0.113.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (574) hide show
  1. package/corpus/README.md +4 -2
  2. package/corpus/core/CHANGELOG.md +34 -0
  3. package/corpus/core/docs/AGENTS.md +7 -0
  4. package/corpus/core/docs/content/integrations.mdx +63 -2
  5. package/corpus/core/docs/design/provider-integration-authority.md +117 -0
  6. package/corpus/core/package.json +8 -1
  7. package/corpus/core/scripts/materialize-source-corpus.mjs +9 -2
  8. package/corpus/core/src/cli/atomic-json-file.ts +324 -0
  9. package/corpus/core/src/cli/claude-code-participant.ts +515 -0
  10. package/corpus/core/src/cli/code-agent-executor.ts +35 -15
  11. package/corpus/core/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
  12. package/corpus/core/src/cli/code-agent-runs.ts +70 -36
  13. package/corpus/core/src/cli/codex-cli-participant.ts +496 -0
  14. package/corpus/core/src/cli/multi-frontier-runs.ts +1952 -0
  15. package/corpus/core/src/client/chat/ChatHistoryList.tsx +7 -404
  16. package/corpus/core/src/coding-tools/index.ts +27 -8
  17. package/corpus/core/src/embeddings/index.ts +233 -0
  18. package/corpus/core/src/index.ts +2 -0
  19. package/corpus/core/src/integrations/index.ts +10 -0
  20. package/corpus/core/src/integrations/slack-oauth.ts +1 -0
  21. package/corpus/core/src/integrations/webhook-delivery.ts +55 -0
  22. package/corpus/core/src/notifications/channels.ts +4 -10
  23. package/corpus/core/src/provider-api/actions/custom-provider-registration.ts +197 -0
  24. package/corpus/core/src/provider-api/actions/delete-staged-dataset.ts +3 -42
  25. package/corpus/core/src/provider-api/actions/list-staged-datasets.ts +3 -45
  26. package/corpus/core/src/provider-api/actions/provider-api-audit.ts +88 -0
  27. package/corpus/core/src/provider-api/actions/provider-api.ts +460 -0
  28. package/corpus/core/src/provider-api/actions/query-staged-dataset.ts +3 -140
  29. package/corpus/core/src/provider-api/actions/staged-datasets.ts +359 -0
  30. package/corpus/core/src/provider-api/gong.ts +108 -0
  31. package/corpus/core/src/scripts/docs/source-search.ts +2 -1
  32. package/corpus/core/src/search/index.ts +413 -0
  33. package/corpus/core/src/server/google-oauth-credentials.ts +80 -3
  34. package/corpus/core/src/server/index.ts +6 -0
  35. package/corpus/core/src/styles/chat-history-list.css +1 -313
  36. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
  37. package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  38. package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  39. package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  40. package/corpus/core/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  41. package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  42. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  43. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  44. package/corpus/core/src/templates/default/AGENTS.md +25 -18
  45. package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  46. package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  47. package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  48. package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  49. package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  50. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  51. package/corpus/core/src/templates/headless/AGENTS.md +7 -0
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  54. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  55. package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  56. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  57. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  58. package/corpus/core/src/templates/workspace-core/AGENTS.md +4 -0
  59. package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
  60. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -12
  61. package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  62. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +11 -2
  63. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  64. package/corpus/templates/analytics/actions/delete-staged-dataset.ts +3 -23
  65. package/corpus/templates/analytics/actions/list-staged-datasets.ts +3 -27
  66. package/corpus/templates/analytics/actions/provider-api-catalog.ts +16 -19
  67. package/corpus/templates/analytics/actions/provider-api-docs.ts +3 -5
  68. package/corpus/templates/analytics/actions/provider-api-request.ts +162 -185
  69. package/corpus/templates/analytics/actions/query-staged-dataset.ts +3 -47
  70. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +52 -161
  71. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -12
  72. package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  73. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +11 -2
  74. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  75. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +63 -160
  76. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -12
  77. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +48 -5
  78. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +46 -14
  79. package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  80. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +11 -2
  81. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  82. package/corpus/templates/brain/README.md +34 -3
  83. package/corpus/templates/brain/actions/_schemas.ts +13 -7
  84. package/corpus/templates/brain/actions/approve-proposal.ts +2 -0
  85. package/corpus/templates/brain/actions/claim-distillation.ts +24 -27
  86. package/corpus/templates/brain/actions/create-source.ts +1 -1
  87. package/corpus/templates/brain/actions/delete-staged-dataset.ts +3 -24
  88. package/corpus/templates/brain/actions/get-knowledge.ts +6 -2
  89. package/corpus/templates/brain/actions/get-pilot-report.ts +44 -8
  90. package/corpus/templates/brain/actions/import-capture.ts +21 -9
  91. package/corpus/templates/brain/actions/import-transcript.ts +42 -14
  92. package/corpus/templates/brain/actions/list-captures.ts +10 -1
  93. package/corpus/templates/brain/actions/list-distillation-queue.ts +39 -14
  94. package/corpus/templates/brain/actions/list-projects.ts +51 -0
  95. package/corpus/templates/brain/actions/list-proposals.ts +80 -4
  96. package/corpus/templates/brain/actions/list-sources.ts +17 -3
  97. package/corpus/templates/brain/actions/list-staged-datasets.ts +3 -28
  98. package/corpus/templates/brain/actions/manage-project.ts +118 -0
  99. package/corpus/templates/brain/actions/mark-capture-distilled.ts +68 -20
  100. package/corpus/templates/brain/actions/navigate.ts +1 -0
  101. package/corpus/templates/brain/actions/provider-api-catalog.ts +5 -11
  102. package/corpus/templates/brain/actions/provider-api-docs.ts +3 -5
  103. package/corpus/templates/brain/actions/provider-api-request.ts +3 -37
  104. package/corpus/templates/brain/actions/query-staged-dataset.ts +3 -48
  105. package/corpus/templates/brain/actions/reject-proposal.ts +6 -1
  106. package/corpus/templates/brain/actions/resanitize-captures.ts +159 -33
  107. package/corpus/templates/brain/actions/review-proposal.ts +2 -0
  108. package/corpus/templates/brain/actions/search-everything.ts +10 -1
  109. package/corpus/templates/brain/actions/search-knowledge.ts +1 -1
  110. package/corpus/templates/brain/actions/set-settings.ts +36 -1
  111. package/corpus/templates/brain/actions/update-proposal.ts +2 -0
  112. package/corpus/templates/brain/actions/update-source.ts +5 -1
  113. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +48 -154
  114. package/corpus/templates/brain/app/hooks/use-distillation-bridge.ts +18 -9
  115. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +3 -0
  116. package/corpus/templates/brain/app/i18n-data.ts +731 -2
  117. package/corpus/templates/brain/app/lib/brain.ts +88 -1
  118. package/corpus/templates/brain/app/lib/ingest-handoff.ts +35 -0
  119. package/corpus/templates/brain/app/routes/ops.tsx +47 -0
  120. package/corpus/templates/brain/app/routes/review.tsx +41 -2
  121. package/corpus/templates/brain/app/routes/search.tsx +33 -2
  122. package/corpus/templates/brain/app/routes/settings.tsx +158 -0
  123. package/corpus/templates/brain/app/routes/sources.tsx +31 -0
  124. package/corpus/templates/brain/changelog/2026-07-19-added-private-source-safe-semantic-search.md +6 -0
  125. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-can-now-join-configured-public-channels-befo.md +6 -0
  126. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-continue-across-the-configured-number-of.md +6 -0
  127. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-start-with-a-safe-four-week-window-and-r.md +6 -0
  128. package/corpus/templates/brain/jobs/process-ingest-queue.ts +270 -36
  129. package/corpus/templates/brain/package.json +1 -0
  130. package/corpus/templates/brain/server/db/index.ts +18 -0
  131. package/corpus/templates/brain/server/db/schema.ts +197 -1
  132. package/corpus/templates/brain/server/jobs/distillation-queue.ts +12 -5
  133. package/corpus/templates/brain/server/lib/audiences.ts +708 -0
  134. package/corpus/templates/brain/server/lib/brain-health.ts +251 -38
  135. package/corpus/templates/brain/server/lib/brain.ts +877 -50
  136. package/corpus/templates/brain/server/lib/capture-sanitization.ts +217 -53
  137. package/corpus/templates/brain/server/lib/connectors.ts +609 -72
  138. package/corpus/templates/brain/server/lib/demo.ts +17 -4
  139. package/corpus/templates/brain/server/lib/hybrid-search.ts +363 -0
  140. package/corpus/templates/brain/server/lib/ingest-queue.ts +104 -0
  141. package/corpus/templates/brain/server/lib/privacy-readiness.ts +24 -0
  142. package/corpus/templates/brain/server/lib/search-index-contracts.ts +72 -0
  143. package/corpus/templates/brain/server/lib/search-index.ts +714 -0
  144. package/corpus/templates/brain/server/lib/search.ts +120 -4
  145. package/corpus/templates/brain/server/lib/sensitivity-policy.ts +174 -0
  146. package/corpus/templates/brain/server/lib/slack-channel-membership.ts +44 -0
  147. package/corpus/templates/brain/server/lib/slack-events.ts +281 -0
  148. package/corpus/templates/brain/server/onboarding.ts +29 -0
  149. package/corpus/templates/brain/server/plugins/db.ts +204 -0
  150. package/corpus/templates/brain/server/register-secrets.ts +46 -0
  151. package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +36 -16
  152. package/corpus/templates/brain/server/routes/api/_agent-native/brain/slack-events.post.ts +28 -0
  153. package/corpus/templates/brain/shared/slack-source-config.ts +60 -0
  154. package/corpus/templates/brain/shared/types.ts +10 -0
  155. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -12
  156. package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  157. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +11 -2
  158. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  159. package/corpus/templates/calendar/actions/delete-staged-dataset.ts +3 -24
  160. package/corpus/templates/calendar/actions/list-staged-datasets.ts +3 -28
  161. package/corpus/templates/calendar/actions/provider-api-catalog.ts +5 -11
  162. package/corpus/templates/calendar/actions/provider-api-docs.ts +3 -5
  163. package/corpus/templates/calendar/actions/provider-api-request.ts +3 -37
  164. package/corpus/templates/calendar/actions/query-staged-dataset.ts +3 -48
  165. package/corpus/templates/calendar/server/handlers/gong.ts +6 -61
  166. package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -8
  167. package/corpus/templates/calendar/server/lib/google-calendar.ts +7 -45
  168. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  169. package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  170. package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  171. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  172. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  173. package/corpus/templates/chat/AGENTS.md +5 -2
  174. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  175. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -12
  176. package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  177. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +11 -2
  178. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  179. package/corpus/templates/clips/AGENTS.md +11 -0
  180. package/corpus/templates/clips/actions/export-to-brain.ts +505 -171
  181. package/corpus/templates/clips/changelog/2026-07-19-ready-clips-transcripts-can-be-backfilled-to-brain-for-a-bou.md +6 -0
  182. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +1 -1
  183. package/corpus/templates/clips/server/lib/google-calendar-client.ts +12 -22
  184. package/corpus/templates/clips/server/lib/google-calendar-oauth.ts +9 -6
  185. package/corpus/templates/clips/server/lib/slack-oauth.ts +12 -47
  186. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +2 -6
  187. package/corpus/templates/clips/server/routes/api/auth/google-calendar.get.ts +4 -4
  188. package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -12
  189. package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  190. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +11 -2
  191. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  192. package/corpus/templates/content/actions/delete-staged-dataset.ts +3 -24
  193. package/corpus/templates/content/actions/list-staged-datasets.ts +3 -28
  194. package/corpus/templates/content/actions/provider-api-catalog.ts +5 -11
  195. package/corpus/templates/content/actions/provider-api-docs.ts +3 -5
  196. package/corpus/templates/content/actions/provider-api-request.ts +3 -37
  197. package/corpus/templates/content/actions/query-staged-dataset.ts +3 -48
  198. package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -12
  199. package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  200. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +11 -2
  201. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  202. package/corpus/templates/design/actions/delete-staged-dataset.ts +4 -23
  203. package/corpus/templates/design/actions/list-staged-datasets.ts +4 -27
  204. package/corpus/templates/design/actions/provider-api-catalog.ts +15 -18
  205. package/corpus/templates/design/actions/provider-api-docs.ts +57 -48
  206. package/corpus/templates/design/actions/provider-api-request.ts +140 -164
  207. package/corpus/templates/design/actions/query-staged-dataset.ts +4 -47
  208. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -12
  209. package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  210. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +11 -2
  211. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  212. package/corpus/templates/dispatch/actions/delete-staged-dataset.ts +4 -23
  213. package/corpus/templates/dispatch/actions/list-staged-datasets.ts +4 -30
  214. package/corpus/templates/dispatch/actions/provider-api-catalog.ts +15 -18
  215. package/corpus/templates/dispatch/actions/provider-api-docs.ts +57 -48
  216. package/corpus/templates/dispatch/actions/provider-api-request.ts +138 -163
  217. package/corpus/templates/dispatch/actions/query-staged-dataset.ts +4 -47
  218. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -12
  219. package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  220. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +11 -2
  221. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  222. package/corpus/templates/forms/server/lib/integrations.ts +23 -22
  223. package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  224. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  225. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -12
  226. package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  227. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +11 -2
  228. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  229. package/corpus/templates/mail/actions/delete-staged-dataset.ts +4 -23
  230. package/corpus/templates/mail/actions/list-staged-datasets.ts +4 -27
  231. package/corpus/templates/mail/actions/provider-api-catalog.ts +15 -18
  232. package/corpus/templates/mail/actions/provider-api-docs.ts +57 -48
  233. package/corpus/templates/mail/actions/provider-api-request.ts +146 -173
  234. package/corpus/templates/mail/actions/query-staged-dataset.ts +4 -47
  235. package/corpus/templates/mail/server/handlers/gong.ts +6 -61
  236. package/corpus/templates/mail/server/lib/google-auth.ts +10 -13
  237. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -12
  238. package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  239. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +11 -2
  240. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  241. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +47 -156
  242. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -12
  243. package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  244. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +11 -2
  245. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  246. package/corpus/templates/slides/actions/delete-staged-dataset.ts +4 -23
  247. package/corpus/templates/slides/actions/list-staged-datasets.ts +4 -27
  248. package/corpus/templates/slides/actions/provider-api-catalog.ts +15 -18
  249. package/corpus/templates/slides/actions/provider-api-docs.ts +57 -48
  250. package/corpus/templates/slides/actions/provider-api-request.ts +145 -172
  251. package/corpus/templates/slides/actions/query-staged-dataset.ts +4 -47
  252. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +11 -36
  253. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -12
  254. package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +10 -0
  255. package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  256. package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +11 -2
  257. package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  258. package/corpus/toolkit/CHANGELOG.md +125 -0
  259. package/corpus/toolkit/README.md +60 -0
  260. package/corpus/toolkit/export-snapshot.json +35 -0
  261. package/corpus/toolkit/migration-manifest.json +4 -0
  262. package/corpus/toolkit/package.json +281 -0
  263. package/corpus/toolkit/scripts/finalize-build.mjs +55 -0
  264. package/corpus/toolkit/src/app-shell/header-actions.tsx +84 -0
  265. package/corpus/toolkit/src/app-shell/index.ts +1 -0
  266. package/corpus/toolkit/src/chat-history/ChatHistoryList.tsx +469 -0
  267. package/corpus/toolkit/src/chat-history/index.ts +7 -0
  268. package/corpus/toolkit/src/chat-history.css +314 -0
  269. package/corpus/toolkit/src/collab-ui/AgentPresenceChip.tsx +69 -0
  270. package/corpus/toolkit/src/collab-ui/LiveCursorOverlay.tsx +241 -0
  271. package/corpus/toolkit/src/collab-ui/PresenceBar.tsx +346 -0
  272. package/corpus/toolkit/src/collab-ui/RecentEditHighlights.tsx +225 -0
  273. package/corpus/toolkit/src/collab-ui/RemoteSelectionRings.tsx +227 -0
  274. package/corpus/toolkit/src/collab-ui/agent-identity.ts +16 -0
  275. package/corpus/toolkit/src/collab-ui/index.ts +39 -0
  276. package/corpus/toolkit/src/collab-ui/lead-client.ts +32 -0
  277. package/corpus/toolkit/src/collab-ui/types.ts +96 -0
  278. package/corpus/toolkit/src/composer/AgentComposerFrame.tsx +59 -0
  279. package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +983 -0
  280. package/corpus/toolkit/src/composer/MentionPopover.tsx +412 -0
  281. package/corpus/toolkit/src/composer/PastedTextChip.tsx +150 -0
  282. package/corpus/toolkit/src/composer/PromptComposer.tsx +718 -0
  283. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +856 -0
  284. package/corpus/toolkit/src/composer/TiptapComposer.tsx +2936 -0
  285. package/corpus/toolkit/src/composer/VoiceButton.tsx +255 -0
  286. package/corpus/toolkit/src/composer/asset-picker-url.ts +80 -0
  287. package/corpus/toolkit/src/composer/attachment-accept.ts +71 -0
  288. package/corpus/toolkit/src/composer/draft-key.ts +7 -0
  289. package/corpus/toolkit/src/composer/extensions/FileReference.tsx +55 -0
  290. package/corpus/toolkit/src/composer/extensions/MentionReference.tsx +93 -0
  291. package/corpus/toolkit/src/composer/extensions/SkillReference.tsx +56 -0
  292. package/corpus/toolkit/src/composer/index.ts +79 -0
  293. package/corpus/toolkit/src/composer/pasted-text.ts +160 -0
  294. package/corpus/toolkit/src/composer/prompt-attachments.ts +93 -0
  295. package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +64 -0
  296. package/corpus/toolkit/src/composer/realtime-voice-transcript.ts +134 -0
  297. package/corpus/toolkit/src/composer/runtime-adapters.tsx +343 -0
  298. package/corpus/toolkit/src/composer/types.ts +66 -0
  299. package/corpus/toolkit/src/composer/use-file-search.ts +64 -0
  300. package/corpus/toolkit/src/composer/use-mention-search.ts +90 -0
  301. package/corpus/toolkit/src/composer/use-skills.ts +45 -0
  302. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +2117 -0
  303. package/corpus/toolkit/src/composer/useVoiceDictation.ts +1191 -0
  304. package/corpus/toolkit/src/context-ui/ContextMeter.tsx +101 -0
  305. package/corpus/toolkit/src/context-ui/ContextSegmentRow.tsx +184 -0
  306. package/corpus/toolkit/src/context-ui/ContextTreemap.tsx +125 -0
  307. package/corpus/toolkit/src/context-ui/ContextXRayPanel.tsx +377 -0
  308. package/corpus/toolkit/src/context-ui/format.ts +42 -0
  309. package/corpus/toolkit/src/context-ui/index.ts +15 -0
  310. package/corpus/toolkit/src/context-ui/types.ts +43 -0
  311. package/corpus/toolkit/src/design-tweaks/index.ts +1 -0
  312. package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +1084 -0
  313. package/corpus/toolkit/src/editor/BubbleToolbar.tsx +253 -0
  314. package/corpus/toolkit/src/editor/CodeBlockNode.tsx +254 -0
  315. package/corpus/toolkit/src/editor/DragHandle.ts +1437 -0
  316. package/corpus/toolkit/src/editor/ImageExtension.ts +306 -0
  317. package/corpus/toolkit/src/editor/RegistryBlockContext.tsx +107 -0
  318. package/corpus/toolkit/src/editor/RegistryBlockNode.tsx +652 -0
  319. package/corpus/toolkit/src/editor/RichMarkdownEditor.tsx +81 -0
  320. package/corpus/toolkit/src/editor/RunId.ts +61 -0
  321. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +321 -0
  322. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +310 -0
  323. package/corpus/toolkit/src/editor/extensions.ts +402 -0
  324. package/corpus/toolkit/src/editor/gfmDoc.ts +92 -0
  325. package/corpus/toolkit/src/editor/index.ts +93 -0
  326. package/corpus/toolkit/src/editor/registrySlashCommands.ts +131 -0
  327. package/corpus/toolkit/src/editor/surgical-apply.ts +192 -0
  328. package/corpus/toolkit/src/editor/useCollabReconcile.ts +589 -0
  329. package/corpus/toolkit/src/editor.css +510 -0
  330. package/corpus/toolkit/src/hooks/index.ts +2 -0
  331. package/corpus/toolkit/src/hooks/use-mobile.tsx +21 -0
  332. package/corpus/toolkit/src/hooks/use-toast.ts +188 -0
  333. package/corpus/toolkit/src/index.ts +8 -0
  334. package/corpus/toolkit/src/onboarding/index.ts +1 -0
  335. package/corpus/toolkit/src/provider.tsx +47 -0
  336. package/corpus/toolkit/src/sharing/VisibilityBadge.tsx +37 -0
  337. package/corpus/toolkit/src/sharing/index.ts +4 -0
  338. package/corpus/toolkit/src/styles.css +21 -0
  339. package/corpus/toolkit/src/ui/accordion.tsx +56 -0
  340. package/corpus/toolkit/src/ui/alert-dialog.tsx +141 -0
  341. package/corpus/toolkit/src/ui/alert.tsx +59 -0
  342. package/corpus/toolkit/src/ui/aspect-ratio.tsx +5 -0
  343. package/corpus/toolkit/src/ui/avatar.tsx +48 -0
  344. package/corpus/toolkit/src/ui/badge.tsx +37 -0
  345. package/corpus/toolkit/src/ui/breadcrumb.tsx +115 -0
  346. package/corpus/toolkit/src/ui/button.tsx +78 -0
  347. package/corpus/toolkit/src/ui/calendar.tsx +214 -0
  348. package/corpus/toolkit/src/ui/card.tsx +86 -0
  349. package/corpus/toolkit/src/ui/carousel.tsx +260 -0
  350. package/corpus/toolkit/src/ui/chart.tsx +375 -0
  351. package/corpus/toolkit/src/ui/checkbox.tsx +28 -0
  352. package/corpus/toolkit/src/ui/collapsible.tsx +9 -0
  353. package/corpus/toolkit/src/ui/command.tsx +174 -0
  354. package/corpus/toolkit/src/ui/context-menu.tsx +202 -0
  355. package/corpus/toolkit/src/ui/date-picker.tsx +64 -0
  356. package/corpus/toolkit/src/ui/dialog.tsx +146 -0
  357. package/corpus/toolkit/src/ui/drawer.tsx +126 -0
  358. package/corpus/toolkit/src/ui/dropdown-menu.tsx +218 -0
  359. package/corpus/toolkit/src/ui/form.tsx +177 -0
  360. package/corpus/toolkit/src/ui/hover-card.tsx +27 -0
  361. package/corpus/toolkit/src/ui/index.ts +52 -0
  362. package/corpus/toolkit/src/ui/input-otp.tsx +69 -0
  363. package/corpus/toolkit/src/ui/input.tsx +22 -0
  364. package/corpus/toolkit/src/ui/label.tsx +24 -0
  365. package/corpus/toolkit/src/ui/logo.tsx +36 -0
  366. package/corpus/toolkit/src/ui/menubar.tsx +235 -0
  367. package/corpus/toolkit/src/ui/navigation-menu.tsx +128 -0
  368. package/corpus/toolkit/src/ui/pagination.tsx +121 -0
  369. package/corpus/toolkit/src/ui/popover.tsx +44 -0
  370. package/corpus/toolkit/src/ui/progress.tsx +26 -0
  371. package/corpus/toolkit/src/ui/radio-group.tsx +42 -0
  372. package/corpus/toolkit/src/ui/resizable.tsx +43 -0
  373. package/corpus/toolkit/src/ui/scroll-area.tsx +46 -0
  374. package/corpus/toolkit/src/ui/select.tsx +158 -0
  375. package/corpus/toolkit/src/ui/separator.tsx +29 -0
  376. package/corpus/toolkit/src/ui/sheet.tsx +160 -0
  377. package/corpus/toolkit/src/ui/sidebar.tsx +777 -0
  378. package/corpus/toolkit/src/ui/skeleton.tsx +18 -0
  379. package/corpus/toolkit/src/ui/slider.tsx +26 -0
  380. package/corpus/toolkit/src/ui/sonner.tsx +58 -0
  381. package/corpus/toolkit/src/ui/spinner.tsx +17 -0
  382. package/corpus/toolkit/src/ui/switch.tsx +27 -0
  383. package/corpus/toolkit/src/ui/table.tsx +117 -0
  384. package/corpus/toolkit/src/ui/tabs.tsx +53 -0
  385. package/corpus/toolkit/src/ui/textarea.tsx +23 -0
  386. package/corpus/toolkit/src/ui/toast.tsx +127 -0
  387. package/corpus/toolkit/src/ui/toaster.tsx +33 -0
  388. package/corpus/toolkit/src/ui/toggle-group.tsx +59 -0
  389. package/corpus/toolkit/src/ui/toggle.tsx +43 -0
  390. package/corpus/toolkit/src/ui/tooltip.tsx +53 -0
  391. package/corpus/toolkit/src/ui/use-toast.ts +3 -0
  392. package/corpus/toolkit/src/utils.ts +8 -0
  393. package/corpus/toolkit/tsconfig.json +23 -0
  394. package/dist/cli/atomic-json-file.d.ts +30 -0
  395. package/dist/cli/atomic-json-file.d.ts.map +1 -0
  396. package/dist/cli/atomic-json-file.js +240 -0
  397. package/dist/cli/atomic-json-file.js.map +1 -0
  398. package/dist/cli/claude-code-participant.d.ts +77 -0
  399. package/dist/cli/claude-code-participant.d.ts.map +1 -0
  400. package/dist/cli/claude-code-participant.js +355 -0
  401. package/dist/cli/claude-code-participant.js.map +1 -0
  402. package/dist/cli/code-agent-executor.d.ts +8 -9
  403. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  404. package/dist/cli/code-agent-executor.js +27 -13
  405. package/dist/cli/code-agent-executor.js.map +1 -1
  406. package/dist/cli/code-agent-runs.concurrent-worker.d.ts +2 -0
  407. package/dist/cli/code-agent-runs.concurrent-worker.d.ts.map +1 -0
  408. package/dist/cli/code-agent-runs.concurrent-worker.js +32 -0
  409. package/dist/cli/code-agent-runs.concurrent-worker.js.map +1 -0
  410. package/dist/cli/code-agent-runs.d.ts +5 -0
  411. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  412. package/dist/cli/code-agent-runs.js +51 -33
  413. package/dist/cli/code-agent-runs.js.map +1 -1
  414. package/dist/cli/codex-cli-participant.d.ts +77 -0
  415. package/dist/cli/codex-cli-participant.d.ts.map +1 -0
  416. package/dist/cli/codex-cli-participant.js +354 -0
  417. package/dist/cli/codex-cli-participant.js.map +1 -0
  418. package/dist/cli/multi-frontier-runs.d.ts +246 -0
  419. package/dist/cli/multi-frontier-runs.d.ts.map +1 -0
  420. package/dist/cli/multi-frontier-runs.js +1247 -0
  421. package/dist/cli/multi-frontier-runs.js.map +1 -0
  422. package/dist/client/chat/ChatHistoryList.d.ts +1 -87
  423. package/dist/client/chat/ChatHistoryList.d.ts.map +1 -1
  424. package/dist/client/chat/ChatHistoryList.js +1 -82
  425. package/dist/client/chat/ChatHistoryList.js.map +1 -1
  426. package/dist/coding-tools/index.d.ts +1 -0
  427. package/dist/coding-tools/index.d.ts.map +1 -1
  428. package/dist/coding-tools/index.js +25 -7
  429. package/dist/coding-tools/index.js.map +1 -1
  430. package/dist/collab/awareness.d.ts +2 -2
  431. package/dist/collab/awareness.d.ts.map +1 -1
  432. package/dist/embeddings/index.d.ts +24 -0
  433. package/dist/embeddings/index.d.ts.map +1 -0
  434. package/dist/embeddings/index.js +172 -0
  435. package/dist/embeddings/index.js.map +1 -0
  436. package/dist/index.d.ts +2 -0
  437. package/dist/index.d.ts.map +1 -1
  438. package/dist/index.js +2 -0
  439. package/dist/index.js.map +1 -1
  440. package/dist/integrations/index.d.ts +2 -1
  441. package/dist/integrations/index.d.ts.map +1 -1
  442. package/dist/integrations/index.js +2 -1
  443. package/dist/integrations/index.js.map +1 -1
  444. package/dist/integrations/slack-manifest.d.ts +1 -1
  445. package/dist/integrations/slack-oauth.d.ts +1 -1
  446. package/dist/integrations/slack-oauth.d.ts.map +1 -1
  447. package/dist/integrations/slack-oauth.js +1 -0
  448. package/dist/integrations/slack-oauth.js.map +1 -1
  449. package/dist/integrations/webhook-delivery.d.ts +23 -0
  450. package/dist/integrations/webhook-delivery.d.ts.map +1 -0
  451. package/dist/integrations/webhook-delivery.js +32 -0
  452. package/dist/integrations/webhook-delivery.js.map +1 -0
  453. package/dist/notifications/channels.d.ts.map +1 -1
  454. package/dist/notifications/channels.js +4 -9
  455. package/dist/notifications/channels.js.map +1 -1
  456. package/dist/notifications/routes.d.ts +3 -3
  457. package/dist/progress/routes.d.ts +1 -1
  458. package/dist/provider-api/actions/custom-provider-registration.d.ts +136 -0
  459. package/dist/provider-api/actions/custom-provider-registration.d.ts.map +1 -0
  460. package/dist/provider-api/actions/custom-provider-registration.js +154 -0
  461. package/dist/provider-api/actions/custom-provider-registration.js.map +1 -0
  462. package/dist/provider-api/actions/delete-staged-dataset.d.ts +1 -1
  463. package/dist/provider-api/actions/delete-staged-dataset.d.ts.map +1 -1
  464. package/dist/provider-api/actions/delete-staged-dataset.js +3 -34
  465. package/dist/provider-api/actions/delete-staged-dataset.js.map +1 -1
  466. package/dist/provider-api/actions/list-staged-datasets.d.ts +2 -3
  467. package/dist/provider-api/actions/list-staged-datasets.d.ts.map +1 -1
  468. package/dist/provider-api/actions/list-staged-datasets.js +3 -40
  469. package/dist/provider-api/actions/list-staged-datasets.js.map +1 -1
  470. package/dist/provider-api/actions/provider-api-audit.d.ts +7 -0
  471. package/dist/provider-api/actions/provider-api-audit.d.ts.map +1 -0
  472. package/dist/provider-api/actions/provider-api-audit.js +74 -0
  473. package/dist/provider-api/actions/provider-api-audit.js.map +1 -0
  474. package/dist/provider-api/actions/provider-api.d.ts +370 -0
  475. package/dist/provider-api/actions/provider-api.d.ts.map +1 -0
  476. package/dist/provider-api/actions/provider-api.js +280 -0
  477. package/dist/provider-api/actions/provider-api.js.map +1 -0
  478. package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
  479. package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
  480. package/dist/provider-api/actions/query-staged-dataset.js +3 -116
  481. package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
  482. package/dist/provider-api/actions/staged-datasets.d.ts +190 -0
  483. package/dist/provider-api/actions/staged-datasets.d.ts.map +1 -0
  484. package/dist/provider-api/actions/staged-datasets.js +210 -0
  485. package/dist/provider-api/actions/staged-datasets.js.map +1 -0
  486. package/dist/provider-api/gong.d.ts +44 -0
  487. package/dist/provider-api/gong.d.ts.map +1 -0
  488. package/dist/provider-api/gong.js +56 -0
  489. package/dist/provider-api/gong.js.map +1 -0
  490. package/dist/resources/handlers.d.ts +1 -1
  491. package/dist/scripts/docs/source-search.d.ts +2 -1
  492. package/dist/scripts/docs/source-search.d.ts.map +1 -1
  493. package/dist/scripts/docs/source-search.js +2 -1
  494. package/dist/scripts/docs/source-search.js.map +1 -1
  495. package/dist/search/index.d.ts +88 -0
  496. package/dist/search/index.d.ts.map +1 -0
  497. package/dist/search/index.js +267 -0
  498. package/dist/search/index.js.map +1 -0
  499. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  500. package/dist/server/google-oauth-credentials.d.ts +35 -0
  501. package/dist/server/google-oauth-credentials.d.ts.map +1 -1
  502. package/dist/server/google-oauth-credentials.js +48 -2
  503. package/dist/server/google-oauth-credentials.js.map +1 -1
  504. package/dist/server/index.d.ts +1 -1
  505. package/dist/server/index.d.ts.map +1 -1
  506. package/dist/server/index.js +1 -1
  507. package/dist/server/index.js.map +1 -1
  508. package/dist/styles/chat-history-list.css +1 -313
  509. package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  510. package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  511. package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  512. package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  513. package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  514. package/dist/templates/chat/AGENTS.md +5 -2
  515. package/dist/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  516. package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -12
  517. package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  518. package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  519. package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  520. package/dist/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  521. package/dist/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  522. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  523. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  524. package/dist/templates/default/AGENTS.md +25 -18
  525. package/dist/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  526. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  527. package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  528. package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  529. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  530. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  531. package/dist/templates/headless/AGENTS.md +7 -0
  532. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  533. package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  534. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  535. package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  536. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  537. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  538. package/dist/templates/workspace-core/AGENTS.md +4 -0
  539. package/dist/templates/workspace-root/AGENTS.md +4 -0
  540. package/docs/AGENTS.md +7 -0
  541. package/docs/content/integrations.mdx +63 -2
  542. package/docs/design/provider-integration-authority.md +117 -0
  543. package/package.json +9 -2
  544. package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  545. package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  546. package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  547. package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  548. package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  549. package/src/templates/chat/AGENTS.md +5 -2
  550. package/src/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  551. package/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
  552. package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  553. package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  554. package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  555. package/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  556. package/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  557. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  558. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  559. package/src/templates/default/AGENTS.md +25 -18
  560. package/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  561. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  562. package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  563. package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  564. package/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  565. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  566. package/src/templates/headless/AGENTS.md +7 -0
  567. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  568. package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  569. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  570. package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  571. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  572. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  573. package/src/templates/workspace-core/AGENTS.md +4 -0
  574. package/src/templates/workspace-root/AGENTS.md +4 -0
@@ -0,0 +1,983 @@
1
+ import { useComposerRuntime } from "@assistant-ui/react";
2
+ import {
3
+ IconPlus,
4
+ IconUpload,
5
+ IconBulb,
6
+ IconClock,
7
+ IconBolt,
8
+ IconTool,
9
+ IconPlugConnected,
10
+ IconPhotoPlus,
11
+ IconLoader2,
12
+ IconArrowLeft,
13
+ IconX,
14
+ } from "@tabler/icons-react";
15
+ import React, {
16
+ useState,
17
+ useRef,
18
+ useEffect,
19
+ useMemo,
20
+ useCallback,
21
+ } from "react";
22
+ import { createPortal } from "react-dom";
23
+
24
+ import { Popover, PopoverTrigger, PopoverContent } from "../ui/popover.js";
25
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip.js";
26
+ import { cn } from "../utils.js";
27
+ import {
28
+ createAssetPickerHandoffId,
29
+ isExternalAssetPickerUrl,
30
+ standaloneAssetPickerUrl,
31
+ } from "./asset-picker-url.js";
32
+ import { useComposerRuntimeAdapters } from "./runtime-adapters.js";
33
+ import type { ComposerMode } from "./types.js";
34
+
35
+ interface ComposerPlusMenuProps {
36
+ onSelectMode?: (mode: ComposerMode) => void;
37
+ onAttachmentError?: (message: string) => void;
38
+ /**
39
+ * "full" (default): full + menu with Upload File, Create Skill, Schedule Task,
40
+ * Automation, Extension, MCP Server. "upload-only": clicking + opens the file
41
+ * picker directly — no popover, no other modes. Use for prompt popovers
42
+ * (create extension, create deck, create dashboard, etc.) where the only thing
43
+ * to attach is a file.
44
+ */
45
+ mode?: "full" | "upload-only";
46
+ }
47
+
48
+ type View = "menu" | "skill-upload";
49
+
50
+ const DEFAULT_ASSETS_PICKER_URL = "https://assets.agent-native.com/picker";
51
+ const EMBED_PROTOCOL = "agent-native.embed";
52
+ const EMBED_VERSION = 1;
53
+
54
+ interface EmbedEnvelope<TPayload = unknown> {
55
+ protocol?: string;
56
+ version?: number;
57
+ type?: string;
58
+ name?: string;
59
+ payload?: TPayload;
60
+ }
61
+
62
+ interface AssetPickerPayload {
63
+ assetId?: unknown;
64
+ handoffId?: unknown;
65
+ url?: unknown;
66
+ previewUrl?: unknown;
67
+ downloadUrl?: unknown;
68
+ embedUrl?: unknown;
69
+ altText?: unknown;
70
+ title?: unknown;
71
+ prompt?: unknown;
72
+ mediaType?: unknown;
73
+ libraryId?: unknown;
74
+ }
75
+
76
+ function assetPickerUrl() {
77
+ const env =
78
+ (import.meta as ImportMeta & { env?: Record<string, string | undefined> })
79
+ .env ?? {};
80
+ return env.VITE_AGENT_NATIVE_ASSETS_PICKER_URL || DEFAULT_ASSETS_PICKER_URL;
81
+ }
82
+
83
+ function withEmbeddedParams(url: string): string {
84
+ try {
85
+ const parsed = new URL(url, window.location.href);
86
+ parsed.searchParams.set("embedded", "1");
87
+ parsed.searchParams.set("mediaType", "image");
88
+ return parsed.toString();
89
+ } catch {
90
+ const separator = url.includes("?") ? "&" : "?";
91
+ return `${url}${separator}embedded=1&mediaType=image`;
92
+ }
93
+ }
94
+
95
+ function assetPickerOrigin(url: string): string | null {
96
+ try {
97
+ return new URL(url, window.location.href).origin;
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+
103
+ function embedEnvelope(
104
+ type: "message" | "ready",
105
+ options: { name?: string; payload?: unknown } = {},
106
+ ): EmbedEnvelope {
107
+ return {
108
+ protocol: EMBED_PROTOCOL,
109
+ version: EMBED_VERSION,
110
+ type,
111
+ ...options,
112
+ };
113
+ }
114
+
115
+ function isEmbedEnvelope(value: unknown): value is EmbedEnvelope {
116
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
117
+ const candidate = value as EmbedEnvelope;
118
+ return (
119
+ candidate.protocol === EMBED_PROTOCOL &&
120
+ candidate.version === EMBED_VERSION &&
121
+ typeof candidate.type === "string"
122
+ );
123
+ }
124
+
125
+ function assetString(value: unknown): string | null {
126
+ return typeof value === "string" && value.trim() ? value.trim() : null;
127
+ }
128
+
129
+ function assetImageSource(payload: unknown): string | null {
130
+ if (!payload || typeof payload !== "object") return null;
131
+ const asset = payload as AssetPickerPayload;
132
+ return (
133
+ assetString(asset.url) ??
134
+ assetString(asset.previewUrl) ??
135
+ assetString(asset.downloadUrl) ??
136
+ assetString(asset.embedUrl)
137
+ );
138
+ }
139
+
140
+ function assetTitle(payload: unknown, url: string): string {
141
+ if (payload && typeof payload === "object") {
142
+ const title = assetString((payload as AssetPickerPayload).title);
143
+ if (title) return title;
144
+ const prompt = assetString((payload as AssetPickerPayload).prompt);
145
+ if (prompt) return prompt.slice(0, 80);
146
+ }
147
+ try {
148
+ const name = new URL(url).pathname.split("/").filter(Boolean).pop();
149
+ return name ? decodeURIComponent(name) : "Generated image";
150
+ } catch {
151
+ return "Generated image";
152
+ }
153
+ }
154
+
155
+ function assetContext(payload: unknown, url: string): string {
156
+ const lines = [`Image URL: ${url}`];
157
+ if (payload && typeof payload === "object") {
158
+ const asset = payload as AssetPickerPayload;
159
+ const assetId = assetString(asset.assetId);
160
+ const libraryId = assetString(asset.libraryId);
161
+ const prompt = assetString(asset.prompt);
162
+ const altText = assetString(asset.altText);
163
+ if (assetId) lines.push(`Asset ID: ${assetId}`);
164
+ if (libraryId) lines.push(`Library ID: ${libraryId}`);
165
+ if (prompt) lines.push(`Prompt: ${prompt}`);
166
+ if (altText) lines.push(`Alt text: ${altText}`);
167
+ }
168
+ return lines.join("\n");
169
+ }
170
+
171
+ function slugifyName(value: string): string {
172
+ return (
173
+ value
174
+ .toLowerCase()
175
+ .replace(/[^a-z0-9]+/g, "-")
176
+ .replace(/^-+|-+$/g, "") || "uploaded-skill"
177
+ );
178
+ }
179
+
180
+ function formatAttachmentError(error: unknown, fallback: string): string {
181
+ return error instanceof Error ? error.message : fallback;
182
+ }
183
+
184
+ function UploadOnlyAttachButton({
185
+ onAttachmentError,
186
+ }: Pick<ComposerPlusMenuProps, "onAttachmentError">) {
187
+ const composerRuntime = useComposerRuntime();
188
+ const inputRef = useRef<HTMLInputElement>(null);
189
+ const handleFilesSelected = async (files: FileList | null) => {
190
+ if (!files || files.length === 0) return;
191
+ try {
192
+ await Promise.all(
193
+ Array.from(files).map((file) => composerRuntime.addAttachment(file)),
194
+ );
195
+ } catch (error) {
196
+ onAttachmentError?.(
197
+ formatAttachmentError(error, "Could not upload the selected file."),
198
+ );
199
+ }
200
+ };
201
+
202
+ return (
203
+ <>
204
+ <input
205
+ ref={inputRef}
206
+ type="file"
207
+ multiple
208
+ className="hidden"
209
+ onChange={(event) => {
210
+ void handleFilesSelected(event.target.files);
211
+ event.target.value = "";
212
+ }}
213
+ />
214
+ <Tooltip>
215
+ <TooltipTrigger asChild>
216
+ <span className="inline-flex shrink-0">
217
+ <button
218
+ type="button"
219
+ className="shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent/50"
220
+ aria-label="Upload"
221
+ onClick={() => inputRef.current?.click()}
222
+ >
223
+ <IconPlus className="h-4 w-4" />
224
+ </button>
225
+ </span>
226
+ </TooltipTrigger>
227
+ <TooltipContent>Upload</TooltipContent>
228
+ </Tooltip>
229
+ </>
230
+ );
231
+ }
232
+
233
+ export function ComposerPlusMenu({
234
+ onSelectMode,
235
+ onAttachmentError,
236
+ mode = "full",
237
+ }: ComposerPlusMenuProps) {
238
+ if (mode === "upload-only") {
239
+ return <UploadOnlyAttachButton onAttachmentError={onAttachmentError} />;
240
+ }
241
+ return (
242
+ <ComposerPlusMenuFull
243
+ onSelectMode={onSelectMode}
244
+ onAttachmentError={onAttachmentError}
245
+ />
246
+ );
247
+ }
248
+
249
+ function ComposerPlusMenuFull({
250
+ onSelectMode,
251
+ onAttachmentError,
252
+ }: Pick<ComposerPlusMenuProps, "onSelectMode" | "onAttachmentError">) {
253
+ const adapters = useComposerRuntimeAdapters();
254
+ const t = adapters.translate!;
255
+ const resources = adapters.resources!;
256
+ const composerRuntime = useComposerRuntime();
257
+ const [open, setOpen] = useState(false);
258
+ const [assetsPickerOpen, setAssetsPickerOpen] = useState(false);
259
+ const [mcpDialogOpen, setMcpDialogOpen] = useState(false);
260
+ const [view, setView] = useState<View>("menu");
261
+ const showMcpIntegrations = useMemo(
262
+ () => resources.isMcpIntegrationAvailable!(),
263
+ [resources],
264
+ );
265
+
266
+ const { data: org } = resources.useOrg!();
267
+ const canCreateOrgMcp =
268
+ !org?.orgId || org.role === "owner" || org.role === "admin";
269
+ const hasOrg = !!org?.orgId;
270
+ const defaultMcpScope: "org" | "user" =
271
+ hasOrg && canCreateOrgMcp ? "org" : "user";
272
+ const createMcp = resources.useCreateMcpServer!();
273
+ const McpIntegrationDialog = resources.McpIntegrationDialog;
274
+
275
+ const fileUploadRef = useRef<HTMLInputElement>(null);
276
+ const skillFileInputRef = useRef<HTMLInputElement>(null);
277
+ const skillHoverTimerRef = useRef<number | null>(null);
278
+ const [skillUploadSlug, setSkillUploadSlug] = useState("");
279
+ const [skillUploadContent, setSkillUploadContent] = useState("");
280
+ const [skillUploadFileName, setSkillUploadFileName] = useState("");
281
+ const [skillUploadStatus, setSkillUploadStatus] = useState<{
282
+ kind: "ok" | "err";
283
+ message: string;
284
+ } | null>(null);
285
+ const [skillUploadBusy, setSkillUploadBusy] = useState(false);
286
+ const [skillFlyoutOpen, setSkillFlyoutOpen] = useState(false);
287
+ const [skillFlyoutSide, setSkillFlyoutSide] = useState<"right" | "left">(
288
+ "right",
289
+ );
290
+ const skillFlyoutCloseTimerRef = useRef<number | null>(null);
291
+ const openSkillFlyout = (rowEl?: HTMLElement | null) => {
292
+ if (skillFlyoutCloseTimerRef.current) {
293
+ window.clearTimeout(skillFlyoutCloseTimerRef.current);
294
+ skillFlyoutCloseTimerRef.current = null;
295
+ }
296
+ if (rowEl && typeof window !== "undefined") {
297
+ const rect = rowEl.getBoundingClientRect();
298
+ const FLYOUT_WIDTH = 248;
299
+ setSkillFlyoutSide(
300
+ window.innerWidth - rect.right < FLYOUT_WIDTH ? "left" : "right",
301
+ );
302
+ }
303
+ setSkillFlyoutOpen(true);
304
+ };
305
+ const scheduleSkillFlyoutClose = () => {
306
+ if (skillFlyoutCloseTimerRef.current)
307
+ window.clearTimeout(skillFlyoutCloseTimerRef.current);
308
+ skillFlyoutCloseTimerRef.current = window.setTimeout(() => {
309
+ setSkillFlyoutOpen(false);
310
+ }, 160);
311
+ };
312
+
313
+ useEffect(() => {
314
+ if (open) {
315
+ setView("menu");
316
+ setSkillUploadSlug("");
317
+ setSkillUploadContent("");
318
+ setSkillUploadFileName("");
319
+ setSkillUploadStatus(null);
320
+ setSkillUploadBusy(false);
321
+ setSkillFlyoutOpen(false);
322
+ }
323
+ }, [open]);
324
+
325
+ const handleSkillFileSelected = async (files: FileList | null) => {
326
+ if (!files || files.length === 0) return;
327
+ const file = files[0];
328
+ const text = await file.text();
329
+ const baseName = file.name.replace(/\.[^./]+$/, "");
330
+ const slug = slugifyName(
331
+ baseName.toLowerCase() === "skill" ? "uploaded-skill" : baseName,
332
+ );
333
+ setSkillUploadSlug(slug);
334
+ setSkillUploadContent(text);
335
+ setSkillUploadFileName(file.name);
336
+ setSkillUploadStatus(null);
337
+ setView("skill-upload");
338
+ };
339
+
340
+ const handleFilesSelected = async (files: FileList | null) => {
341
+ if (!files || files.length === 0) return;
342
+ try {
343
+ await Promise.all(
344
+ Array.from(files).map((file) => composerRuntime.addAttachment(file)),
345
+ );
346
+ } catch (error) {
347
+ onAttachmentError?.(
348
+ formatAttachmentError(error, "Could not upload the selected file."),
349
+ );
350
+ }
351
+ };
352
+
353
+ const submitSkillUpload = async () => {
354
+ if (skillUploadBusy) return;
355
+ const slug = slugifyName(skillUploadSlug || "uploaded-skill");
356
+ const path = `skills/${slug}/SKILL.md`;
357
+ setSkillUploadBusy(true);
358
+ setSkillUploadStatus(null);
359
+ try {
360
+ const res = await fetch(
361
+ adapters.resolvePath!("/_agent-native/resources"),
362
+ {
363
+ method: "POST",
364
+ headers: { "Content-Type": "application/json" },
365
+ body: JSON.stringify({
366
+ path,
367
+ content: skillUploadContent,
368
+ mimeType: "text/markdown",
369
+ shared: false,
370
+ }),
371
+ },
372
+ );
373
+ if (!res.ok) {
374
+ const body = await res.text().catch(() => "");
375
+ throw new Error(body || `Upload failed (${res.status})`);
376
+ }
377
+ setSkillUploadStatus({
378
+ kind: "ok",
379
+ message: `Skill "${skillUploadFileName || `${slug}/SKILL.md`}" added`,
380
+ });
381
+ window.setTimeout(() => setOpen(false), 1200);
382
+ } catch (err: any) {
383
+ setSkillUploadStatus({
384
+ kind: "err",
385
+ message: err?.message || "Failed to save skill file",
386
+ });
387
+ } finally {
388
+ setSkillUploadBusy(false);
389
+ }
390
+ };
391
+
392
+ const menuItems: {
393
+ icon: React.ReactNode;
394
+ label: string;
395
+ desc: string;
396
+ action: () => void;
397
+ hoverAction?: () => void;
398
+ }[] = [
399
+ {
400
+ icon: <IconUpload className="h-3.5 w-3.5" />,
401
+ label: "Upload File",
402
+ desc: "Images, PDFs, text/code, JSON, CSV",
403
+ action: () => {
404
+ setOpen(false);
405
+ setTimeout(() => fileUploadRef.current?.click(), 0);
406
+ },
407
+ },
408
+ {
409
+ icon: <IconPhotoPlus className="h-3.5 w-3.5" />,
410
+ label: "Generate Image",
411
+ desc: "Open the Assets image picker",
412
+ action: () => {
413
+ setOpen(false);
414
+ setAssetsPickerOpen(true);
415
+ },
416
+ },
417
+ {
418
+ icon: <IconClock className="h-3.5 w-3.5" />,
419
+ label: "Schedule Task",
420
+ desc: "Run something on a schedule",
421
+ action: () => {
422
+ onSelectMode?.("job");
423
+ setOpen(false);
424
+ },
425
+ },
426
+ {
427
+ icon: <IconBolt className="h-3.5 w-3.5" />,
428
+ label: "Create Automation",
429
+ desc: "Set up a when-X-do-Y rule",
430
+ action: () => {
431
+ onSelectMode?.("automation");
432
+ setOpen(false);
433
+ },
434
+ },
435
+ {
436
+ icon: <IconTool className="h-3.5 w-3.5" />,
437
+ label: "Create Extension",
438
+ desc: "Build a mini app extension",
439
+ action: () => {
440
+ onSelectMode?.("extension");
441
+ setOpen(false);
442
+ },
443
+ },
444
+ ...(showMcpIntegrations
445
+ ? [
446
+ {
447
+ icon: <IconPlugConnected className="h-3.5 w-3.5" />,
448
+ label: t("mcpIntegrations.menuLabel"),
449
+ desc: t("mcpIntegrations.menuDescription"),
450
+ action: () => {
451
+ setOpen(false);
452
+ setMcpDialogOpen(true);
453
+ },
454
+ },
455
+ ]
456
+ : []),
457
+ {
458
+ icon: <IconBulb className="h-3.5 w-3.5" />,
459
+ label: "Create Skill",
460
+ desc: "Teach the agent a new ability",
461
+ action: openSkillFlyout,
462
+ hoverAction: openSkillFlyout,
463
+ },
464
+ ];
465
+
466
+ return (
467
+ <>
468
+ {/* Hidden input to trigger the native file upload with explicit errors. */}
469
+ <input
470
+ ref={fileUploadRef}
471
+ type="file"
472
+ multiple
473
+ className="hidden"
474
+ onChange={(event) => {
475
+ void handleFilesSelected(event.target.files);
476
+ event.target.value = "";
477
+ }}
478
+ />
479
+ <input
480
+ ref={skillFileInputRef}
481
+ type="file"
482
+ accept=".md,text/markdown"
483
+ className="hidden"
484
+ onChange={(e) => {
485
+ void handleSkillFileSelected(e.target.files);
486
+ e.target.value = "";
487
+ }}
488
+ />
489
+
490
+ <Popover open={open} onOpenChange={setOpen}>
491
+ <Tooltip>
492
+ <TooltipTrigger asChild>
493
+ <PopoverTrigger asChild>
494
+ <button
495
+ type="button"
496
+ className="shrink-0 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent/50 disabled:opacity-30 disabled:cursor-not-allowed"
497
+ >
498
+ <IconPlus className="h-4 w-4" />
499
+ </button>
500
+ </PopoverTrigger>
501
+ </TooltipTrigger>
502
+ <TooltipContent>Add...</TooltipContent>
503
+ </Tooltip>
504
+ <PopoverContent
505
+ side="top"
506
+ align="start"
507
+ sideOffset={8}
508
+ className={cn(
509
+ "p-0 rounded-lg",
510
+ view === "skill-upload"
511
+ ? "max-h-[70vh] w-[calc(100vw-24px)] max-w-[380px] overflow-y-auto"
512
+ : "w-[260px]",
513
+ )}
514
+ style={{ fontSize: 13, lineHeight: "normal" }}
515
+ onOpenAutoFocus={(e) => e.preventDefault()}
516
+ >
517
+ {view === "menu" && (
518
+ <div className="py-1">
519
+ {menuItems.map((item) => {
520
+ const isSkill = item.label === "Create Skill";
521
+ return (
522
+ <div
523
+ key={item.label}
524
+ className={cn("relative", isSkill && "group/skill")}
525
+ onMouseEnter={(e) => {
526
+ if (isSkill) {
527
+ openSkillFlyout(e.currentTarget);
528
+ return;
529
+ }
530
+ if (!item.hoverAction) return;
531
+ if (skillHoverTimerRef.current)
532
+ window.clearTimeout(skillHoverTimerRef.current);
533
+ skillHoverTimerRef.current = window.setTimeout(() => {
534
+ item.hoverAction?.();
535
+ }, 180);
536
+ }}
537
+ onMouseLeave={() => {
538
+ if (isSkill) {
539
+ scheduleSkillFlyoutClose();
540
+ return;
541
+ }
542
+ if (skillHoverTimerRef.current) {
543
+ window.clearTimeout(skillHoverTimerRef.current);
544
+ skillHoverTimerRef.current = null;
545
+ }
546
+ }}
547
+ >
548
+ <button
549
+ type="button"
550
+ onClick={item.action}
551
+ className={cn(
552
+ "flex w-full items-center gap-2.5 px-3 py-2 text-start hover:bg-accent/50",
553
+ isSkill && skillFlyoutOpen && "bg-accent/50",
554
+ )}
555
+ >
556
+ <span className="text-muted-foreground">{item.icon}</span>
557
+ <div className="min-w-0 flex-1">
558
+ <div className="text-[12px] font-medium text-foreground">
559
+ {item.label}
560
+ </div>
561
+ <div className="mt-0.5 text-[10px] text-muted-foreground/60">
562
+ {item.desc}
563
+ </div>
564
+ </div>
565
+ {isSkill && (
566
+ <span className="ms-auto text-muted-foreground/60">
567
+
568
+ </span>
569
+ )}
570
+ </button>
571
+ {isSkill && skillFlyoutOpen && (
572
+ <div
573
+ role="menu"
574
+ onMouseEnter={() => openSkillFlyout()}
575
+ onMouseLeave={scheduleSkillFlyoutClose}
576
+ className={cn(
577
+ "absolute top-0 z-20 w-[240px] rounded-lg border border-border bg-popover py-1 shadow-md",
578
+ skillFlyoutSide === "right"
579
+ ? "left-full ml-1"
580
+ : "right-full mr-1",
581
+ )}
582
+ >
583
+ <button
584
+ type="button"
585
+ onClick={() => {
586
+ onSelectMode?.("skill");
587
+ setSkillFlyoutOpen(false);
588
+ setOpen(false);
589
+ }}
590
+ className="flex w-full items-center gap-2.5 px-3 py-2 text-start hover:bg-accent/50"
591
+ >
592
+ <span className="text-muted-foreground">
593
+ <IconBulb className="h-3.5 w-3.5" />
594
+ </span>
595
+ <div className="min-w-0">
596
+ <div className="text-[12px] font-medium text-foreground">
597
+ Create new skill
598
+ </div>
599
+ <div className="mt-0.5 text-[10px] text-muted-foreground/60">
600
+ Describe a skill and let the agent draft it
601
+ </div>
602
+ </div>
603
+ </button>
604
+ <button
605
+ type="button"
606
+ onClick={() => {
607
+ setSkillFlyoutOpen(false);
608
+ skillFileInputRef.current?.click();
609
+ }}
610
+ className="flex w-full items-center gap-2.5 px-3 py-2 text-start hover:bg-accent/50"
611
+ >
612
+ <span className="text-muted-foreground">
613
+ <IconUpload className="h-3.5 w-3.5" />
614
+ </span>
615
+ <div className="min-w-0">
616
+ <div className="text-[12px] font-medium text-foreground">
617
+ Upload skill file
618
+ </div>
619
+ <div className="mt-0.5 text-[10px] text-muted-foreground/60">
620
+ Import an existing SKILL.md file
621
+ </div>
622
+ </div>
623
+ </button>
624
+ </div>
625
+ )}
626
+ </div>
627
+ );
628
+ })}
629
+ </div>
630
+ )}
631
+
632
+ {view === "skill-upload" && (
633
+ <div className="p-3">
634
+ <button
635
+ type="button"
636
+ onClick={() => setView("menu")}
637
+ className="mb-1.5 flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground"
638
+ >
639
+ <IconArrowLeft className="h-3 w-3 rtl:-scale-x-100" />
640
+ Back
641
+ </button>
642
+ <label className="mb-1 block text-[11px] font-semibold text-foreground">
643
+ Upload skill file
644
+ </label>
645
+ <p className="mb-2 text-[10px] leading-relaxed text-muted-foreground/60">
646
+ Review the content from{" "}
647
+ <span className="font-mono">
648
+ {skillUploadFileName || "the selected file"}
649
+ </span>{" "}
650
+ before saving.
651
+ </p>
652
+ <label className="mb-1 block text-[10px] font-medium text-muted-foreground">
653
+ Skill name
654
+ </label>
655
+ <input
656
+ value={skillUploadSlug}
657
+ onChange={(e) => setSkillUploadSlug(e.target.value)}
658
+ className="mb-2 w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-[12px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent"
659
+ placeholder="my-skill"
660
+ />
661
+ <p className="mb-2 text-[10px] text-muted-foreground/60">
662
+ Saved at{" "}
663
+ <span className="font-mono">
664
+ skills/{slugifyName(skillUploadSlug || "uploaded-skill")}
665
+ /SKILL.md
666
+ </span>
667
+ </p>
668
+ <label className="mb-1 block text-[10px] font-medium text-muted-foreground">
669
+ Content
670
+ </label>
671
+ <textarea
672
+ value={skillUploadContent}
673
+ onChange={(e) => setSkillUploadContent(e.target.value)}
674
+ rows={10}
675
+ className="w-full rounded-md border border-border bg-background px-2.5 py-1.5 font-mono text-[11px] leading-relaxed text-foreground outline-none focus:ring-1 focus:ring-accent"
676
+ />
677
+ {skillUploadStatus && (
678
+ <div
679
+ className={cn(
680
+ "mt-2 text-[11px] leading-snug",
681
+ skillUploadStatus.kind === "ok"
682
+ ? "text-green-600 dark:text-green-400"
683
+ : "text-red-600 dark:text-red-400",
684
+ )}
685
+ >
686
+ {skillUploadStatus.message}
687
+ </div>
688
+ )}
689
+ <div className="mt-2.5 flex justify-end gap-2">
690
+ <button
691
+ type="button"
692
+ onClick={() => setView("menu")}
693
+ className="rounded-md px-3 py-1.5 text-[12px] font-medium text-muted-foreground hover:bg-accent/40"
694
+ >
695
+ Cancel
696
+ </button>
697
+ <button
698
+ type="button"
699
+ onClick={submitSkillUpload}
700
+ disabled={
701
+ skillUploadBusy ||
702
+ !skillUploadContent.trim() ||
703
+ !skillUploadSlug.trim()
704
+ }
705
+ className="rounded-md bg-accent px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40 disabled:pointer-events-none"
706
+ >
707
+ {skillUploadBusy ? (
708
+ <IconLoader2 className="h-3 w-3 animate-spin" />
709
+ ) : (
710
+ "Save"
711
+ )}
712
+ </button>
713
+ </div>
714
+ </div>
715
+ )}
716
+ </PopoverContent>
717
+ </Popover>
718
+ {McpIntegrationDialog ? (
719
+ <McpIntegrationDialog
720
+ open={mcpDialogOpen}
721
+ onOpenChange={setMcpDialogOpen}
722
+ defaultScope={defaultMcpScope}
723
+ canCreateOrgMcp={canCreateOrgMcp}
724
+ hasOrg={hasOrg}
725
+ onCreateMcpServer={(args: unknown) => createMcp.mutateAsync(args)}
726
+ />
727
+ ) : null}
728
+ <AssetsPickerModal
729
+ open={assetsPickerOpen}
730
+ onOpenChange={setAssetsPickerOpen}
731
+ />
732
+ </>
733
+ );
734
+ }
735
+
736
+ function AssetsPickerModal({
737
+ open,
738
+ onOpenChange,
739
+ }: {
740
+ open: boolean;
741
+ onOpenChange: (open: boolean) => void;
742
+ }) {
743
+ const adapters = useComposerRuntimeAdapters();
744
+ const iframeRef = useRef<HTMLIFrameElement | null>(null);
745
+ const standaloneWindowRef = useRef<Window | null>(null);
746
+ const [pickerReady, setPickerReady] = useState(false);
747
+ const [standaloneHandoffId, setStandaloneHandoffId] = useState<string | null>(
748
+ null,
749
+ );
750
+ const sourceUrl = useMemo(() => assetPickerUrl(), []);
751
+ const externalPicker = useMemo(
752
+ () =>
753
+ typeof window !== "undefined" &&
754
+ isExternalAssetPickerUrl(sourceUrl, window.location.origin),
755
+ [sourceUrl],
756
+ );
757
+ const standaloneUrl = useMemo(
758
+ () =>
759
+ standaloneAssetPickerUrl(
760
+ sourceUrl,
761
+ typeof window !== "undefined" ? window.location.href : undefined,
762
+ {
763
+ handoffId: standaloneHandoffId ?? undefined,
764
+ returnOrigin:
765
+ typeof window !== "undefined" ? window.location.origin : undefined,
766
+ },
767
+ ),
768
+ [sourceUrl, standaloneHandoffId],
769
+ );
770
+ const iframeUrl = useMemo(() => withEmbeddedParams(sourceUrl), [sourceUrl]);
771
+ const targetOrigin = useMemo(() => assetPickerOrigin(iframeUrl), [iframeUrl]);
772
+ const configurePicker = useCallback(() => {
773
+ if (!targetOrigin) return;
774
+ iframeRef.current?.contentWindow?.postMessage(
775
+ embedEnvelope("message", {
776
+ name: "configure",
777
+ payload: { mediaType: "image", count: 3 },
778
+ }),
779
+ targetOrigin,
780
+ );
781
+ }, [targetOrigin]);
782
+
783
+ useEffect(() => {
784
+ if (open) {
785
+ setPickerReady(false);
786
+ if (externalPicker) {
787
+ standaloneWindowRef.current = null;
788
+ setStandaloneHandoffId(createAssetPickerHandoffId());
789
+ } else {
790
+ setStandaloneHandoffId(null);
791
+ }
792
+ return;
793
+ }
794
+ if (!standaloneWindowRef.current) setStandaloneHandoffId(null);
795
+ }, [externalPicker, iframeUrl, open]);
796
+
797
+ useEffect(() => {
798
+ if (
799
+ !targetOrigin ||
800
+ (!open && !standaloneWindowRef.current) ||
801
+ (externalPicker && !standaloneHandoffId)
802
+ )
803
+ return;
804
+
805
+ const handleMessage = (event: MessageEvent) => {
806
+ const expectedSource = externalPicker
807
+ ? standaloneWindowRef.current
808
+ : iframeRef.current?.contentWindow;
809
+ if (!expectedSource || event.source !== expectedSource) return;
810
+ if (event.origin !== targetOrigin) return;
811
+ if (!isEmbedEnvelope(event.data)) return;
812
+
813
+ if (event.data.type === "ready") {
814
+ setPickerReady(true);
815
+ configurePicker();
816
+ return;
817
+ }
818
+
819
+ if (event.data.type !== "message") return;
820
+ if (externalPicker) {
821
+ const payload = event.data.payload;
822
+ const handoffId =
823
+ payload && typeof payload === "object"
824
+ ? assetString((payload as AssetPickerPayload).handoffId)
825
+ : null;
826
+ if (handoffId !== standaloneHandoffId) return;
827
+ }
828
+ if (event.data.name === "close") {
829
+ onOpenChange(false);
830
+ return;
831
+ }
832
+ if (
833
+ event.data.name !== "chooseImage" &&
834
+ event.data.name !== "chooseAsset"
835
+ )
836
+ return;
837
+
838
+ const url = assetImageSource(event.data.payload);
839
+ if (!url) return;
840
+ const title = assetTitle(event.data.payload, url);
841
+ const assetId =
842
+ event.data.payload && typeof event.data.payload === "object"
843
+ ? assetString((event.data.payload as AssetPickerPayload).assetId)
844
+ : null;
845
+ adapters.agentChat!.setContextItem!({
846
+ key: `asset-image:${assetId ?? url}`,
847
+ title: `Image: ${title}`,
848
+ context: assetContext(event.data.payload, url),
849
+ });
850
+ standaloneWindowRef.current = null;
851
+ setStandaloneHandoffId(null);
852
+ onOpenChange(false);
853
+ };
854
+
855
+ const handleKey = (event: KeyboardEvent) => {
856
+ if (event.key === "Escape") onOpenChange(false);
857
+ };
858
+
859
+ window.addEventListener("message", handleMessage);
860
+ window.addEventListener("keydown", handleKey);
861
+ return () => {
862
+ window.removeEventListener("message", handleMessage);
863
+ window.removeEventListener("keydown", handleKey);
864
+ };
865
+ }, [
866
+ configurePicker,
867
+ adapters,
868
+ externalPicker,
869
+ onOpenChange,
870
+ open,
871
+ standaloneHandoffId,
872
+ targetOrigin,
873
+ ]);
874
+
875
+ const openStandalonePicker = useCallback(
876
+ (event: React.MouseEvent<HTMLAnchorElement>) => {
877
+ if (!standaloneHandoffId) return;
878
+ event.preventDefault();
879
+ const pickerWindow = window.open(standaloneUrl, "_blank");
880
+ if (!pickerWindow) return;
881
+ standaloneWindowRef.current = pickerWindow;
882
+ onOpenChange(false);
883
+ },
884
+ [onOpenChange, standaloneHandoffId, standaloneUrl],
885
+ );
886
+
887
+ if (!open || typeof document === "undefined") return null;
888
+
889
+ return createPortal(
890
+ <div
891
+ className="fixed inset-0 z-[280] flex items-center justify-center bg-black/50 p-3"
892
+ role="dialog"
893
+ aria-modal="true"
894
+ aria-labelledby="composer-assets-picker-title"
895
+ onMouseDown={(event) => {
896
+ if (event.target === event.currentTarget) onOpenChange(false);
897
+ }}
898
+ >
899
+ <div className="flex h-[min(86vh,760px)] w-[min(96vw,1040px)] flex-col overflow-hidden rounded-xl border border-border bg-background shadow-2xl">
900
+ <div className="flex h-12 shrink-0 items-center justify-between border-b border-border px-4">
901
+ <div
902
+ id="composer-assets-picker-title"
903
+ className="text-sm font-medium text-foreground"
904
+ >
905
+ Generate image
906
+ </div>
907
+ <button
908
+ type="button"
909
+ onClick={() => onOpenChange(false)}
910
+ className="flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
911
+ aria-label="Close image picker"
912
+ >
913
+ <IconX className="h-4 w-4" />
914
+ </button>
915
+ </div>
916
+ {externalPicker ? (
917
+ <div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-3 p-8 text-center">
918
+ <div className="max-w-md text-sm text-muted-foreground">
919
+ Open Assets in a new tab to sign in and choose an image securely.
920
+ </div>
921
+ <a
922
+ href={standaloneUrl}
923
+ target="_blank"
924
+ onClick={openStandalonePicker}
925
+ className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground hover:bg-primary/90"
926
+ >
927
+ Open Assets image picker
928
+ </a>
929
+ </div>
930
+ ) : targetOrigin ? (
931
+ <div className="relative min-h-0 flex-1 overflow-hidden bg-background">
932
+ {!pickerReady && <AssetsPickerLoadingSkeleton />}
933
+ <iframe
934
+ ref={iframeRef}
935
+ src={iframeUrl}
936
+ title="Assets image picker"
937
+ className={cn(
938
+ "absolute inset-0 h-full w-full border-0 bg-background transition-opacity duration-150",
939
+ pickerReady ? "opacity-100" : "pointer-events-none opacity-0",
940
+ )}
941
+ sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-downloads"
942
+ allow="clipboard-read; clipboard-write; microphone; fullscreen"
943
+ referrerPolicy="strict-origin-when-cross-origin"
944
+ onLoad={() => {
945
+ configurePicker();
946
+ setPickerReady(true);
947
+ }}
948
+ />
949
+ </div>
950
+ ) : (
951
+ <div className="flex min-h-0 flex-1 items-center justify-center p-8 text-center text-sm text-muted-foreground">
952
+ The configured image picker URL is not valid.
953
+ </div>
954
+ )}
955
+ </div>
956
+ </div>,
957
+ document.body,
958
+ );
959
+ }
960
+
961
+ function AssetsPickerLoadingSkeleton() {
962
+ return (
963
+ <div
964
+ className="absolute inset-0 flex flex-col gap-5 p-5"
965
+ role="status"
966
+ aria-label="Loading Assets picker"
967
+ >
968
+ <div className="flex items-center gap-3">
969
+ <div className="h-9 flex-1 animate-pulse rounded-md bg-muted" />
970
+ <div className="h-9 w-24 animate-pulse rounded-md bg-muted" />
971
+ </div>
972
+ <div className="grid min-h-0 flex-1 grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
973
+ {Array.from({ length: 8 }).map((_, index) => (
974
+ <div key={index} className="flex min-w-0 flex-col gap-2">
975
+ <div className="aspect-square w-full animate-pulse rounded-lg bg-muted" />
976
+ <div className="h-3 w-3/4 animate-pulse rounded bg-muted" />
977
+ <div className="h-3 w-1/2 animate-pulse rounded bg-muted" />
978
+ </div>
979
+ ))}
980
+ </div>
981
+ </div>
982
+ );
983
+ }