@openhands/agent-canvas 1.15.0 → 1.17.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 (1277) hide show
  1. package/README.md +15 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{acp-providers-BKvmTf82.js → acp-providers-DrjNsPOH.js} +1 -1
  4. package/build/assets/active-backend-context-rZiLJRUc.js +2 -0
  5. package/build/assets/{agent-context-settings-D3PjSTU1.js → agent-context-settings-CJUrcBut.js} +1 -1
  6. package/build/assets/agent-profiles-service.api-Biieml2L.js +1 -0
  7. package/build/assets/agent-profiles-settings-x6RvPsq7.js +1 -0
  8. package/build/assets/agent-server-client-options-BikiNwTa.js +1 -0
  9. package/build/assets/agent-server-conversation-service.api-DYFsNHKf.js +88 -0
  10. package/build/assets/agent-settings-BJyovpJ_.js +1 -0
  11. package/build/assets/agent-settings-CpY3zObk.js +2 -0
  12. package/build/assets/alert-banner-vARz1idS.js +1 -0
  13. package/build/assets/{api-error-message-BJyfWp6p.js → api-error-message-Ve79qDcN.js} +1 -1
  14. package/build/assets/api-key-entry-screen-CsMuBhoP.js +1 -0
  15. package/build/assets/{app-settings-CbHqzQus.js → app-settings-BaJ7nfHt.js} +1 -1
  16. package/build/assets/automation-detail-CVoGDrKq.js +3 -0
  17. package/build/assets/{automation-export-BRopEHMu.js → automation-export-DxY47m3T.js} +1 -1
  18. package/build/assets/automation-git-sync-CRMP9Bbm.js +1 -0
  19. package/build/assets/automation-setup-route-072beLz1.js +3 -0
  20. package/build/assets/{automation-templates-C_0FZgLG.js → automation-templates-4C4PM8K7.js} +1 -1
  21. package/build/assets/automations-list-xuqTSQL4.js +1 -0
  22. package/build/assets/{back-nav-button-Dm9tIjVf.js → back-nav-button-B8tH0BEp.js} +1 -1
  23. package/build/assets/backend-form-modal-0aJmWdsa.js +1 -0
  24. package/build/assets/{backend-synced-settings-badge-3fOwg6Bd.js → backend-synced-settings-badge-B7rKWBir.js} +1 -1
  25. package/build/assets/{badge-D4SqUCsR.js → badge-CcGx2TaV.js} +1 -1
  26. package/build/assets/{base-modal-C7_BDl_U.js → base-modal-DTULovNc.js} +1 -1
  27. package/build/assets/bot-CLMxGFaq.js +1 -0
  28. package/build/assets/{brand-button-Dxv6PDeN.js → brand-button-DxPO4Lf8.js} +1 -1
  29. package/build/assets/browser-C-_qBasq.js +46 -0
  30. package/build/assets/{browser-tab-BAl4ckJE.js → browser-tab-DZanIw5m.js} +1 -1
  31. package/build/assets/canvas-extension-page-o_cWgE9B.js +1 -0
  32. package/build/assets/canvas-extensions-DSgNlCha.js +1 -0
  33. package/build/assets/canvas-extensions-runtime-DdnkgJXV.js +1 -0
  34. package/build/assets/{chat-send-button-DWwR2cgI.js → chat-send-button-B3SFSIg5.js} +1 -1
  35. package/build/assets/check-fDEfgSzj.js +1 -0
  36. package/build/assets/chevron-down-CFrWHqu8.js +1 -0
  37. package/build/assets/chevron-right-C5roOrJu.js +1 -0
  38. package/build/assets/circle-check-ClTzJTdb.js +1 -0
  39. package/build/assets/{circle-plus-check-toggle-CBh_qomE.js → circle-plus-check-toggle-Fr-h-mbL.js} +1 -1
  40. package/build/assets/{cloud-funnel-analytics-287iIERo.js → cloud-funnel-analytics-BQanUEEC.js} +1 -1
  41. package/build/assets/{cog-C4rrw-ZK.js → cog-VBlRo6iw.js} +1 -1
  42. package/build/assets/command-menu-BT3Ia5Ru.js +1 -0
  43. package/build/assets/commits-tab-2jhtaohl.js +1 -0
  44. package/build/assets/{condenser-settings-BcNmYR4s.js → condenser-settings-eVKFMvap.js} +1 -1
  45. package/build/assets/confirmation-modal-gIFHeZt6.js +1 -0
  46. package/build/assets/{context-menu-list-item-lVYETtY5.js → context-menu-list-item-DDROe12Z.js} +1 -1
  47. package/build/assets/conversation-BnjJSFf5.js +93 -0
  48. package/build/assets/conversation-DZ578rOX.js +1 -0
  49. package/build/assets/conversation-panel-DZ578rOX.js +1 -0
  50. package/build/assets/conversation-state-store-D1_G_9I-.js +1 -0
  51. package/build/assets/{conversation-store-Ca3ghXap.js → conversation-store-DVVfkCsk.js} +1 -1
  52. package/build/assets/{conversation-tab-empty-state-HJtkmHZj.js → conversation-tab-empty-state-DKlzAz5c.js} +1 -1
  53. package/build/assets/{copy-to-clipboard-button-Cjkswvnj.js → copy-to-clipboard-button-B3w0X7WH.js} +1 -1
  54. package/build/assets/{device-verify-EZhHhBT7.js → device-verify-BfkCzyFB.js} +1 -1
  55. package/build/assets/{dropdown-classes-DJe-E6MC.js → dropdown-classes-B9kJVDIk.js} +1 -1
  56. package/build/assets/edit-automation-modal-BwhJ39JT.js +1 -0
  57. package/build/assets/empty-state-RZz0OfnY.js +1 -0
  58. package/build/assets/{entry.client-C7Bi2tkU.js → entry.client-Ds8LVp-E.js} +2 -2
  59. package/build/assets/enum-filter-dropdown-C4pW0ORd.js +1 -0
  60. package/build/assets/{environment-switch-overlay-CEKrFiMw.js → environment-switch-overlay-P2ZAuzov.js} +1 -1
  61. package/build/assets/{error-state-BALXiBNy.js → error-state-B_5cgNPJ.js} +1 -1
  62. package/build/assets/extensions-hub-DsaUr4ca.js +1 -0
  63. package/build/assets/extensions-navigation-CRMZd-FE.js +1 -0
  64. package/build/assets/external-link-DQtv7eTh.js +1 -0
  65. package/build/assets/files-tab-CCgAcVdc.js +1 -0
  66. package/build/assets/{form-control-classes-CGw40z5h.js → form-control-classes-B-z85REi.js} +1 -1
  67. package/build/assets/format-model-name-CasAP3nd.js +1 -0
  68. package/build/assets/get-git-path-EHrSDxTM.js +1 -0
  69. package/build/assets/git-repo-dropdown-D2WEQ97X.js +1 -0
  70. package/build/assets/{git-repo-dropdown-ai8qaNkD.js → git-repo-dropdown-DVeCUUXG.js} +1 -1
  71. package/build/assets/{highlighted-source-view-C3sxykQi.js → highlighted-source-view-DjHO3SdW.js} +1 -1
  72. package/build/assets/home-BorS_vdS.js +1 -0
  73. package/build/assets/home-aIMbHw5M.js +1 -0
  74. package/build/assets/home-automation-run-tooltip-CpHNFiFl.js +1 -0
  75. package/build/assets/home-store-DyXz0w6d.js +1 -0
  76. package/build/assets/hooks-service-C6dpRtZM.js +1 -0
  77. package/build/assets/index-home-zKFuKK2B.js +1 -0
  78. package/build/assets/info-CTIdjttz.js +1 -0
  79. package/build/assets/install-server-modal-DUiZMc8u.js +1 -0
  80. package/build/assets/kebab-menu-item-content-mm04SBD3.js +1 -0
  81. package/build/assets/{key-Cn5za3rg.js → key-CfXbJa8w.js} +1 -1
  82. package/build/assets/{launch-C1ieiOGe.js → launch-BSIf9KEg.js} +1 -1
  83. package/build/assets/list-filter-CNpqTWVY.js +1 -0
  84. package/build/assets/{list-todo-Cuu-STFc.js → list-todo-ByXMdY4W.js} +1 -1
  85. package/build/assets/llm-not-configured-banner-z_QRSeV0.js +27 -0
  86. package/build/assets/llm-settings-B-5AHMiX.js +1 -0
  87. package/build/assets/llm-settings-B3SjHDmA.js +1 -0
  88. package/build/assets/llm-subscription-service-aDNBQ7Gq.js +1 -0
  89. package/build/assets/loader-circle-Dex47vVW.js +1 -0
  90. package/build/assets/{loading-spinner-0L3ZTToZ.js → loading-spinner-DfHI77BA.js} +1 -1
  91. package/build/assets/manage-backends-modal-q1NdFisr.js +1 -0
  92. package/build/assets/manifest-8920b615.js +1 -0
  93. package/build/assets/{manifest-subpage-layout-BGhiUQbA.js → manifest-subpage-layout-BQQy5ipR.js} +1 -1
  94. package/build/assets/{map-provider-B9LTm23I.js → map-provider-DXvssjw9.js} +1 -1
  95. package/build/assets/markdown-renderer-rivLvYh-.js +8 -0
  96. package/build/assets/{mcp-CKbR4Vft.js → mcp-CEXDhQWn.js} +1 -1
  97. package/build/assets/{mcp-marketplace-utils-C13fFccR.js → mcp-marketplace-utils-Cn_NIMWM.js} +1 -1
  98. package/build/assets/mcp-page-LGYIICMG.js +9 -0
  99. package/build/assets/messages-DUSeANAh.js +41 -0
  100. package/build/assets/{modal-body-CMOc2tLl.js → modal-body-DJw4xBki.js} +1 -1
  101. package/build/assets/{modal-classes-BAb4Y_BG.js → modal-classes-Dg0ngBPO.js} +1 -1
  102. package/build/assets/{modal-close-button-CkJ0Ktjj.js → modal-close-button-B9j4nasm.js} +1 -1
  103. package/build/assets/{mouse-pointer-click-CsgRs_-r.js → mouse-pointer-click-DEhsSUpu.js} +1 -1
  104. package/build/assets/onboarding-BjFebfs5.js +1 -0
  105. package/build/assets/onboarding-modal-DDTjOpyd.js +1 -0
  106. package/build/assets/organization-service.api-Cyg1Tn03.js +1 -0
  107. package/build/assets/pencil-D5c47kyN.js +1 -0
  108. package/build/assets/pin-C7vMagvf.js +1 -0
  109. package/build/assets/planner-tab-D8f2Y3rn.js +1 -0
  110. package/build/assets/plug-CeaqeF6u.js +1 -0
  111. package/build/assets/plugins-management-service-Bh_pBuf-.js +1 -0
  112. package/build/assets/plus-B3wvU06-.js +1 -0
  113. package/build/assets/profiles-service.api-BbOK0EkF.js +1 -0
  114. package/build/assets/providers-2gIIkz2N.js +1 -0
  115. package/build/assets/proxy-CBgMd2zf.js +1 -0
  116. package/build/assets/{puzzle-A9pGxxai.js → puzzle-DdAqbUrb.js} +1 -1
  117. package/build/assets/recommended-automations-launcher-YLAQTp4s.js +1 -0
  118. package/build/assets/refresh-cw-DEQdPCpH.js +1 -0
  119. package/build/assets/root-Bye_XmEL.js +2 -0
  120. package/build/assets/root-Cnwwzgo0.css +1 -0
  121. package/build/assets/root-layout-CSAwxBGM.js +2 -0
  122. package/build/assets/schema-field-CK0xYNbO.js +1 -0
  123. package/build/assets/scroll-fade-state-Dt_fx2kf.js +1 -0
  124. package/build/assets/sdk-section-page-Da73Rs_o.js +1 -0
  125. package/build/assets/secret-form-CB2VwYYg.js +1 -0
  126. package/build/assets/secrets-service-Cr8g8xuv.js +1 -0
  127. package/build/assets/{secrets-settings-BizaTEr1.js → secrets-settings-Bh0udeKv.js} +1 -1
  128. package/build/assets/settings-CKqk8ZnH.js +1 -0
  129. package/build/assets/settings-DmyIgRxO.js +1 -0
  130. package/build/assets/{settings-dropdown-input-tM0A0ipy.js → settings-dropdown-input-DVpyD87C.js} +1 -1
  131. package/build/assets/settings-index-BSJUFmjE.js +1 -0
  132. package/build/assets/{settings-input-C03KUoSC.js → settings-input-C0dd8Utu.js} +1 -1
  133. package/build/assets/{settings-list-classes-BJ0g8rWj.js → settings-list-classes-Ds4jPBHK.js} +1 -1
  134. package/build/assets/{settings-modal-AzEF1QPP.js → settings-modal-yStgEh2o.js} +1 -1
  135. package/build/assets/settings-service.api-9_xdv_mh.js +1 -0
  136. package/build/assets/{settings-switch-CQE0EGC-.js → settings-switch-B78Khmyz.js} +1 -1
  137. package/build/assets/{settings-utils-ClYOkbbI.js → settings-utils-8GIIgOQL.js} +1 -1
  138. package/build/assets/shared-conversation-DM96X4KV.js +1 -0
  139. package/build/assets/shield-check-DO7wa_eu.js +1 -0
  140. package/build/assets/{sidebar-layout-D3hE08wK.js → sidebar-layout-EPfWxk11.js} +1 -1
  141. package/build/assets/sidebar-mobile-menu-toggle-D5GR5xT9.js +1 -0
  142. package/build/assets/sidebar-nav-link-_t4_Ef-9.js +1 -0
  143. package/build/assets/{sidebar-onboarding-checklist-storage-3qYflszY.js → sidebar-onboarding-checklist-storage-DbDX-Wvj.js} +1 -1
  144. package/build/assets/sidebar-store-CSDFK0W5.js +1 -0
  145. package/build/assets/{skill-card-pill-row-CFxjoqxn.js → skill-card-pill-row-989y15jV.js} +1 -1
  146. package/build/assets/skill-enablement-DXeaImBz.js +1 -0
  147. package/build/assets/skill-filter-BBeZ4JEP.js +2 -0
  148. package/build/assets/{skill-icon-badge-CPQJ6A-I.js → skill-icon-badge-BDhQ-tQ0.js} +1 -1
  149. package/build/assets/skill-scope-Dz3kPH3P.js +1 -0
  150. package/build/assets/skills-plugins-e3CYGdUJ.js +1 -0
  151. package/build/assets/skills-settings-3e-FuHw_.js +1 -0
  152. package/build/assets/styled-tooltip-Pd22BIvr.js +1 -0
  153. package/build/assets/{task-list-tab-04GNQuii.js → task-list-tab-3Jg8ywch.js} +1 -1
  154. package/build/assets/{terminal-Cj0iHIW7.js → terminal-onXx0_Qz.js} +1 -1
  155. package/build/assets/toggle-switch-lwQtxDcm.js +1 -0
  156. package/build/assets/trash-2-ZhKuTLiz.js +1 -0
  157. package/build/assets/trash-B6OIe4yX.js +1 -0
  158. package/build/assets/triangle-alert-BajLPpQg.js +1 -0
  159. package/build/assets/{typography-SgFy466J.js → typography-HLLg_FAn.js} +1 -1
  160. package/build/assets/usage-tab-Cm-9j3UK.js +1 -0
  161. package/build/assets/use-acp-credential-form-BARcvjYE.js +1 -0
  162. package/build/assets/use-activate-agent-profile-BLKML6vQ.js +1 -0
  163. package/build/assets/use-agent-profiles-1P91KSXz.js +1 -0
  164. package/build/assets/{use-automation-health-BxXdzZMP.js → use-automation-health-BZkQNEA8.js} +1 -1
  165. package/build/assets/use-automation-permissions-B_RpvL-s.js +1 -0
  166. package/build/assets/{use-automations-zYb0dUny.js → use-automations-Cg0PDIZr.js} +1 -1
  167. package/build/assets/{use-can-manage-org-profiles-BWMahtUh.js → use-can-manage-org-profiles-BbN6KfDr.js} +1 -1
  168. package/build/assets/use-canvas-extensions-CFUpHat6.js +1 -0
  169. package/build/assets/{use-cloud-current-user-id-CPHMDG-C.js → use-cloud-current-user-id-Dk0rjr63.js} +1 -1
  170. package/build/assets/use-config-DpafU_QP.js +1 -0
  171. package/build/assets/use-create-conversation-1YgmAHt-.js +1 -0
  172. package/build/assets/use-create-secret-D8p9ybQj.js +1 -0
  173. package/build/assets/{use-get-secrets-BT4ihRwj.js → use-get-secrets-CQ4b0qK8.js} +1 -1
  174. package/build/assets/use-is-authed-BRPjCRKM.js +1 -0
  175. package/build/assets/{use-launch-skill-in-chat-BYjYihBP.js → use-launch-skill-in-chat-DeYa1tFM.js} +1 -1
  176. package/build/assets/use-llm-configured-DGm8FiRR.js +1 -0
  177. package/build/assets/use-llm-profiles-BTTtpW4z.js +1 -0
  178. package/build/assets/use-manifest-capabilities-AOWGcu0j.js +1 -0
  179. package/build/assets/use-onboarding-completion-CoINurkm.js +1 -0
  180. package/build/assets/use-pinned-home-route-D6krwXjy.js +1 -0
  181. package/build/assets/use-plugins-marketplace-mLsIDhBv.js +1 -0
  182. package/build/assets/use-save-agent-profile-CxyO9YG-.js +1 -0
  183. package/build/assets/{use-save-settings-CUZW6yXP.js → use-save-settings-3U5K9-8x.js} +1 -1
  184. package/build/assets/use-settings-Dn00UISA.js +1 -0
  185. package/build/assets/use-settings-nav-items-vrjxTqcu.js +16 -0
  186. package/build/assets/use-tracking-B9prBsrq.js +3 -0
  187. package/build/assets/{use-user-conversation-DT0_QCnL.js → use-user-conversation-ZWlXtbZb.js} +1 -1
  188. package/build/assets/{useInfiniteQuery-CgeUQPfs.js → useInfiniteQuery-BzrRqjLg.js} +1 -1
  189. package/build/assets/{useMutation-DFnzkf6f.js → useMutation-CJuK90oc.js} +1 -1
  190. package/build/assets/{useQueries-BhbrGEf1.js → useQueries-CykmpT-a.js} +1 -1
  191. package/build/assets/{useQuery-BXWbZBZl.js → useQuery-S6j94er1.js} +1 -1
  192. package/build/assets/utils-BOco8mxR.js +1 -0
  193. package/build/assets/{vendor~conversation-panel~conversation-CTvDDiXg.js → vendor~conversation-panel~conversation-4eJ0tml3.js} +1 -1
  194. package/build/assets/{vendor~entry.client-BFE7Llz9.js → vendor~entry.client-CoCfUXUF.js} +1 -1
  195. package/build/assets/{vendor~entry.client-BaKt_PIp.js → vendor~entry.client-DA2izTjq.js} +1 -1
  196. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~b0x1wrp2-Z0lly3FE.js +2 -0
  197. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~czjee3a3-BfkNGovw.js +185 -0
  198. package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew3u153i-rfV8mQ9m.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew9ih1s8-DnDoaYNi.js} +1 -1
  199. package/build/assets/{vendor~index-home~home~conversation-panel~conversation-BTyukeM-.js → vendor~index-home~home~conversation-panel~conversation-CSFLzmTN.js} +1 -1
  200. package/build/assets/vendor~root-layout-n8XCEXG8.js +1 -0
  201. package/build/assets/{vendor~root-layout~conversation-panel~conversation-BYAWJwQX.js → vendor~root-layout~conversation-panel~conversation-3Idg1_U3.js} +1 -1
  202. package/build/assets/{vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-CHaLEBVR.js → vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-DkPlXWl9.js} +1 -1
  203. package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation-DgQ564nC.js +1 -0
  204. package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation~mcp~automations-list~aut~ibfbmr9f-CxhwKuAd.js +1 -0
  205. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-Dtu7BpYk.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~inibp4qe-CnsDDzeE.js} +2 -2
  206. package/build/assets/vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iyia17eu-CV850oio.js +1 -0
  207. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-DonTKfQD.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-BPjI-atc.js} +125 -15
  208. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~jz4f6vo2-BgxDvUcf.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~jz4f6vo2-C7HiY4Ae.js} +42 -3
  209. package/build/assets/{verification-settings-Bj6bo097.js → verification-settings-u2POabjH.js} +1 -1
  210. package/build/assets/{wrench-hi7Q13dF.js → wrench-CkgI4rxA.js} +1 -1
  211. package/build/assets/x-By6qCmtY.js +1 -0
  212. package/build/index.html +4 -4
  213. package/build/locales/ar/openhands.json +94 -25
  214. package/build/locales/ca/openhands.json +94 -25
  215. package/build/locales/de/openhands.json +94 -25
  216. package/build/locales/en/openhands.json +94 -25
  217. package/build/locales/es/openhands.json +94 -25
  218. package/build/locales/fr/openhands.json +94 -25
  219. package/build/locales/it/openhands.json +94 -25
  220. package/build/locales/ja/openhands.json +94 -25
  221. package/build/locales/ko-KR/openhands.json +94 -25
  222. package/build/locales/no/openhands.json +94 -25
  223. package/build/locales/pt/openhands.json +94 -25
  224. package/build/locales/tr/openhands.json +94 -25
  225. package/build/locales/uk/openhands.json +94 -25
  226. package/build/locales/zh-CN/openhands.json +94 -25
  227. package/build/locales/zh-TW/openhands.json +94 -25
  228. package/config/defaults.json +9 -5
  229. package/dist/api/agent-profiles-service/profile-field-support.d.ts +26 -0
  230. package/dist/api/agent-server-adapter.cjs +3 -3
  231. package/dist/api/agent-server-adapter.cjs.map +1 -1
  232. package/dist/api/agent-server-adapter.d.ts +72 -12
  233. package/dist/api/agent-server-adapter.js +249 -145
  234. package/dist/api/agent-server-adapter.js.map +1 -1
  235. package/dist/api/agent-server-compatibility.cjs +1 -1
  236. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  237. package/dist/api/agent-server-compatibility.d.ts +21 -0
  238. package/dist/api/agent-server-compatibility.js +52 -38
  239. package/dist/api/agent-server-compatibility.js.map +1 -1
  240. package/dist/api/agent-server-config.cjs +1 -1
  241. package/dist/api/agent-server-config.cjs.map +1 -1
  242. package/dist/api/agent-server-config.d.ts +32 -0
  243. package/dist/api/agent-server-config.js +21 -5
  244. package/dist/api/agent-server-config.js.map +1 -1
  245. package/dist/api/automation-service/automation-service.api.cjs +1 -1
  246. package/dist/api/automation-service/automation-service.api.cjs.map +1 -1
  247. package/dist/api/automation-service/automation-service.api.d.ts +2 -2
  248. package/dist/api/automation-service/automation-service.api.js +7 -7
  249. package/dist/api/automation-service/automation-service.api.js.map +1 -1
  250. package/dist/api/canvas-extensions-service.cjs +1 -0
  251. package/dist/api/canvas-extensions-service.d.ts +22 -0
  252. package/dist/api/canvas-extensions-service.js +4 -0
  253. package/dist/api/cloud/conversation-service.api.cjs +1 -1
  254. package/dist/api/cloud/conversation-service.api.cjs.map +1 -1
  255. package/dist/api/cloud/conversation-service.api.d.ts +6 -0
  256. package/dist/api/cloud/conversation-service.api.js +14 -6
  257. package/dist/api/cloud/conversation-service.api.js.map +1 -1
  258. package/dist/api/cloud/provider-connections-service.api.cjs +2 -0
  259. package/dist/api/cloud/provider-connections-service.api.cjs.map +1 -0
  260. package/dist/api/cloud/provider-connections-service.api.d.ts +5 -0
  261. package/dist/api/cloud/provider-connections-service.api.js +50 -0
  262. package/dist/api/cloud/provider-connections-service.api.js.map +1 -0
  263. package/dist/api/cloud/skills-service.api.cjs +1 -1
  264. package/dist/api/cloud/skills-service.api.cjs.map +1 -1
  265. package/dist/api/cloud/skills-service.api.d.ts +10 -0
  266. package/dist/api/cloud/skills-service.api.js +11 -1
  267. package/dist/api/cloud/skills-service.api.js.map +1 -1
  268. package/dist/api/conversation-file-upload.api.cjs +1 -1
  269. package/dist/api/conversation-file-upload.api.js +10 -10
  270. package/dist/api/conversation-metadata-store.cjs +1 -1
  271. package/dist/api/conversation-metadata-store.cjs.map +1 -1
  272. package/dist/api/conversation-metadata-store.d.ts +24 -0
  273. package/dist/api/conversation-metadata-store.js +10 -2
  274. package/dist/api/conversation-metadata-store.js.map +1 -1
  275. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  276. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  277. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +31 -0
  278. package/dist/api/conversation-service/agent-server-conversation-service.api.js +188 -128
  279. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  280. package/dist/api/event-service/event-service.api.cjs +1 -1
  281. package/dist/api/event-service/event-service.api.js +12 -12
  282. package/dist/api/git-service/agent-server-git-service.api.cjs +1 -1
  283. package/dist/api/git-service/agent-server-git-service.api.js +8 -8
  284. package/dist/api/hooks-service.cjs +2 -0
  285. package/dist/api/hooks-service.cjs.map +1 -0
  286. package/dist/api/hooks-service.d.ts +13 -0
  287. package/dist/api/hooks-service.js +20 -0
  288. package/dist/api/hooks-service.js.map +1 -0
  289. package/dist/api/provider-connections-service/provider-connections-service.api.cjs +1 -1
  290. package/dist/api/provider-connections-service/provider-connections-service.api.cjs.map +1 -1
  291. package/dist/api/provider-connections-service/provider-connections-service.api.js +25 -16
  292. package/dist/api/provider-connections-service/provider-connections-service.api.js.map +1 -1
  293. package/dist/api/runtime-service/agent-server-runtime-service.cjs +1 -1
  294. package/dist/api/runtime-service/agent-server-runtime-service.js +8 -8
  295. package/dist/api/settings-service/settings-service.api.cjs +1 -1
  296. package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
  297. package/dist/api/settings-service/settings-service.api.d.ts +5 -2
  298. package/dist/api/settings-service/settings-service.api.js +20 -11
  299. package/dist/api/settings-service/settings-service.api.js.map +1 -1
  300. package/dist/api/skills-service.cjs +1 -1
  301. package/dist/api/skills-service.cjs.map +1 -1
  302. package/dist/api/skills-service.d.ts +7 -0
  303. package/dist/api/skills-service.js +11 -8
  304. package/dist/api/skills-service.js.map +1 -1
  305. package/dist/components/conversation-events/chat/event-message-components/critic-result-display.cjs +1 -1
  306. package/dist/components/conversation-events/chat/event-message-components/critic-result-display.js +5 -5
  307. package/dist/components/conversation-events/chat/event-message-components/event-group.cjs +1 -1
  308. package/dist/components/conversation-events/chat/event-message-components/event-group.cjs.map +1 -1
  309. package/dist/components/conversation-events/chat/event-message-components/event-group.js +40 -36
  310. package/dist/components/conversation-events/chat/event-message-components/event-group.js.map +1 -1
  311. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.cjs +1 -1
  312. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.cjs.map +1 -1
  313. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.js +2 -1
  314. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.js.map +1 -1
  315. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs +1 -1
  316. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs.map +1 -1
  317. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.d.ts +1 -1
  318. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js +28 -28
  319. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js.map +1 -1
  320. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.cjs +1 -1
  321. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.cjs.map +1 -1
  322. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.js +2 -1
  323. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.js.map +1 -1
  324. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.cjs +1 -1
  325. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.cjs.map +1 -1
  326. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.d.ts +1 -1
  327. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.js +37 -37
  328. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.js.map +1 -1
  329. package/dist/components/conversation-events/chat/event-message.cjs +2 -2
  330. package/dist/components/conversation-events/chat/event-message.cjs.map +1 -1
  331. package/dist/components/conversation-events/chat/event-message.js +79 -70
  332. package/dist/components/conversation-events/chat/event-message.js.map +1 -1
  333. package/dist/components/conversation-events/chat/messages.cjs +1 -1
  334. package/dist/components/conversation-events/chat/messages.cjs.map +1 -1
  335. package/dist/components/conversation-events/chat/messages.js +28 -27
  336. package/dist/components/conversation-events/chat/messages.js.map +1 -1
  337. package/dist/components/features/automations/automation-card.cjs +1 -1
  338. package/dist/components/features/automations/automation-card.cjs.map +1 -1
  339. package/dist/components/features/automations/automation-card.js +118 -102
  340. package/dist/components/features/automations/automation-card.js.map +1 -1
  341. package/dist/components/features/automations/automation-list-row.cjs +1 -1
  342. package/dist/components/features/automations/automation-list-row.cjs.map +1 -1
  343. package/dist/components/features/automations/automation-list-row.js +88 -77
  344. package/dist/components/features/automations/automation-list-row.js.map +1 -1
  345. package/dist/components/features/automations/detail/detail-header.d.ts +3 -1
  346. package/dist/components/features/automations/detail/run-phase.cjs +2 -0
  347. package/dist/components/features/automations/detail/run-phase.cjs.map +1 -0
  348. package/dist/components/features/automations/detail/run-phase.d.ts +89 -0
  349. package/dist/components/features/automations/detail/run-phase.js +72 -0
  350. package/dist/components/features/automations/detail/run-phase.js.map +1 -0
  351. package/dist/components/features/automations/detail/run-status-badge.cjs +1 -1
  352. package/dist/components/features/automations/detail/run-status-badge.cjs.map +1 -1
  353. package/dist/components/features/automations/detail/run-status-badge.d.ts +2 -2
  354. package/dist/components/features/automations/detail/run-status-badge.js +82 -53
  355. package/dist/components/features/automations/detail/run-status-badge.js.map +1 -1
  356. package/dist/components/features/automations/kebab-menu.cjs +1 -1
  357. package/dist/components/features/automations/kebab-menu.cjs.map +1 -1
  358. package/dist/components/features/automations/kebab-menu.js +9 -5
  359. package/dist/components/features/automations/kebab-menu.js.map +1 -1
  360. package/dist/components/features/automations/recommended-automations-launcher.cjs +1 -1
  361. package/dist/components/features/automations/recommended-automations-launcher.cjs.map +1 -1
  362. package/dist/components/features/automations/recommended-automations-launcher.js +12 -12
  363. package/dist/components/features/automations/recommended-automations-launcher.js.map +1 -1
  364. package/dist/components/features/automations/recommended-automations-section.cjs +1 -1
  365. package/dist/components/features/automations/recommended-automations-section.js +9 -9
  366. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  367. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  368. package/dist/components/features/backends/backend-form-modal.js +5 -11
  369. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  370. package/dist/components/features/backends/backend-selector.cjs +1 -1
  371. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  372. package/dist/components/features/backends/backend-selector.js +133 -122
  373. package/dist/components/features/backends/backend-selector.js.map +1 -1
  374. package/dist/components/features/backends/manage-backends-modal.cjs +1 -1
  375. package/dist/components/features/backends/manage-backends-modal.cjs.map +1 -1
  376. package/dist/components/features/backends/manage-backends-modal.js +29 -29
  377. package/dist/components/features/backends/manage-backends-modal.js.map +1 -1
  378. package/dist/components/features/canvas-extensions/add-canvas-extension-modal.d.ts +6 -0
  379. package/dist/components/features/canvas-extensions/canvas-extension-card.d.ts +9 -0
  380. package/dist/components/features/canvas-extensions/canvas-extensions-runtime.cjs +2 -0
  381. package/dist/components/features/canvas-extensions/canvas-extensions-runtime.cjs.map +1 -0
  382. package/dist/components/features/canvas-extensions/canvas-extensions-runtime.d.ts +23 -0
  383. package/dist/components/features/canvas-extensions/canvas-extensions-runtime.js +19 -0
  384. package/dist/components/features/canvas-extensions/canvas-extensions-runtime.js.map +1 -0
  385. package/dist/components/features/chat/change-agent-button.cjs +1 -1
  386. package/dist/components/features/chat/change-agent-button.js +4 -4
  387. package/dist/components/features/chat/chat-interface.cjs +2 -2
  388. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  389. package/dist/components/features/chat/chat-interface.js +107 -104
  390. package/dist/components/features/chat/chat-interface.js.map +1 -1
  391. package/dist/components/features/chat/chat-markdown-path-code.cjs +3 -0
  392. package/dist/components/features/chat/chat-markdown-path-code.cjs.map +1 -0
  393. package/dist/components/features/chat/chat-markdown-path-code.d.ts +27 -0
  394. package/dist/components/features/chat/chat-markdown-path-code.js +72 -0
  395. package/dist/components/features/chat/chat-markdown-path-code.js.map +1 -0
  396. package/dist/components/features/chat/chat-message.cjs +1 -1
  397. package/dist/components/features/chat/chat-message.cjs.map +1 -1
  398. package/dist/components/features/chat/chat-message.d.ts +2 -1
  399. package/dist/components/features/chat/chat-message.js +88 -79
  400. package/dist/components/features/chat/chat-message.js.map +1 -1
  401. package/dist/components/features/chat/components/context-window-meter.cjs +1 -1
  402. package/dist/components/features/chat/components/context-window-meter.js +4 -4
  403. package/dist/components/features/chat/confirmation-mode-enabled.cjs +1 -1
  404. package/dist/components/features/chat/confirmation-mode-enabled.js +4 -4
  405. package/dist/components/features/chat/generic-event-message.cjs +1 -1
  406. package/dist/components/features/chat/generic-event-message.cjs.map +1 -1
  407. package/dist/components/features/chat/generic-event-message.d.ts +2 -1
  408. package/dist/components/features/chat/generic-event-message.js +41 -22
  409. package/dist/components/features/chat/generic-event-message.js.map +1 -1
  410. package/dist/components/features/chat/interactive-chat-box.cjs +1 -1
  411. package/dist/components/features/chat/interactive-chat-box.cjs.map +1 -1
  412. package/dist/components/features/chat/interactive-chat-box.js +20 -19
  413. package/dist/components/features/chat/interactive-chat-box.js.map +1 -1
  414. package/dist/components/features/chat/path-component.cjs +1 -1
  415. package/dist/components/features/chat/path-component.cjs.map +1 -1
  416. package/dist/components/features/chat/path-component.d.ts +8 -2
  417. package/dist/components/features/chat/path-component.js +33 -21
  418. package/dist/components/features/chat/path-component.js.map +1 -1
  419. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs +1 -1
  420. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs.map +1 -1
  421. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js +38 -43
  422. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js.map +1 -1
  423. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs +1 -1
  424. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs.map +1 -1
  425. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.d.ts +8 -5
  426. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js +22 -16
  427. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js.map +1 -1
  428. package/dist/components/features/chat/user-message-body.cjs +1 -1
  429. package/dist/components/features/chat/user-message-body.cjs.map +1 -1
  430. package/dist/components/features/chat/user-message-body.d.ts +4 -0
  431. package/dist/components/features/chat/user-message-body.js +30 -24
  432. package/dist/components/features/chat/user-message-body.js.map +1 -1
  433. package/dist/components/features/conversation/conversation-overview-git-section.cjs +1 -1
  434. package/dist/components/features/conversation/conversation-overview-git-section.js +4 -4
  435. package/dist/components/features/conversation/conversation-overview-skills-panel.cjs +1 -1
  436. package/dist/components/features/conversation/conversation-overview-skills-panel.cjs.map +1 -1
  437. package/dist/components/features/conversation/conversation-overview-skills-panel.js +63 -81
  438. package/dist/components/features/conversation/conversation-overview-skills-panel.js.map +1 -1
  439. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs +1 -1
  440. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs.map +1 -1
  441. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js +77 -78
  442. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js.map +1 -1
  443. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs +1 -1
  444. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs.map +1 -1
  445. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js +96 -96
  446. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js.map +1 -1
  447. package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.cjs +1 -1
  448. package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.cjs.map +1 -1
  449. package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.d.ts +1 -1
  450. package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.js +3 -3
  451. package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.js.map +1 -1
  452. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.cjs +2 -0
  453. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.cjs.map +1 -0
  454. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.d.ts +21 -0
  455. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.js +239 -0
  456. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.js.map +1 -0
  457. package/dist/components/features/conversation-panel/conversation-active-tag-filters.cjs +2 -0
  458. package/dist/components/features/conversation-panel/conversation-active-tag-filters.cjs.map +1 -0
  459. package/dist/components/features/conversation-panel/conversation-active-tag-filters.d.ts +28 -0
  460. package/dist/components/features/conversation-panel/conversation-active-tag-filters.js +69 -0
  461. package/dist/components/features/conversation-panel/conversation-active-tag-filters.js.map +1 -0
  462. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs +1 -1
  463. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs.map +1 -1
  464. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.d.ts +2 -1
  465. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js +19 -18
  466. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js.map +1 -1
  467. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs +1 -1
  468. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs.map +1 -1
  469. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.d.ts +2 -1
  470. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js +91 -80
  471. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js.map +1 -1
  472. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs +1 -1
  473. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs.map +1 -1
  474. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js +1 -1
  475. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js.map +1 -1
  476. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs +1 -1
  477. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs.map +1 -1
  478. package/dist/components/features/conversation-panel/conversation-card/conversation-card.d.ts +7 -1
  479. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js +87 -81
  480. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js.map +1 -1
  481. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs +1 -1
  482. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs.map +1 -1
  483. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.d.ts +2 -1
  484. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js +1 -1
  485. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js.map +1 -1
  486. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs +1 -1
  487. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs.map +1 -1
  488. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.d.ts +2 -1
  489. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js +2 -1
  490. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js.map +1 -1
  491. package/dist/components/features/conversation-panel/conversation-layouts-menu.cjs +2 -0
  492. package/dist/components/features/conversation-panel/conversation-layouts-menu.cjs.map +1 -0
  493. package/dist/components/features/conversation-panel/conversation-layouts-menu.d.ts +29 -0
  494. package/dist/components/features/conversation-panel/conversation-layouts-menu.js +177 -0
  495. package/dist/components/features/conversation-panel/conversation-layouts-menu.js.map +1 -0
  496. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs +1 -1
  497. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs.map +1 -1
  498. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.d.ts +37 -0
  499. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js +85 -45
  500. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js.map +1 -1
  501. package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
  502. package/dist/components/features/conversation-panel/conversation-panel.cjs.map +1 -1
  503. package/dist/components/features/conversation-panel/conversation-panel.js +296 -290
  504. package/dist/components/features/conversation-panel/conversation-panel.js.map +1 -1
  505. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.cjs +2 -0
  506. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.cjs.map +1 -0
  507. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.d.ts +22 -0
  508. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.js +153 -0
  509. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.js.map +1 -0
  510. package/dist/components/features/conversation-panel/hide-older-conversations-row.cjs +2 -0
  511. package/dist/components/features/conversation-panel/hide-older-conversations-row.cjs.map +1 -0
  512. package/dist/components/features/conversation-panel/hide-older-conversations-row.d.ts +6 -0
  513. package/dist/components/features/conversation-panel/hide-older-conversations-row.js +65 -0
  514. package/dist/components/features/conversation-panel/hide-older-conversations-row.js.map +1 -0
  515. package/dist/components/features/conversation-panel/menu-row.cjs +1 -1
  516. package/dist/components/features/conversation-panel/menu-row.cjs.map +1 -1
  517. package/dist/components/features/conversation-panel/menu-row.d.ts +11 -1
  518. package/dist/components/features/conversation-panel/menu-row.js +27 -16
  519. package/dist/components/features/conversation-panel/menu-row.js.map +1 -1
  520. package/dist/components/features/conversation-panel/skills-modal.cjs +1 -1
  521. package/dist/components/features/conversation-panel/skills-modal.cjs.map +1 -1
  522. package/dist/components/features/conversation-panel/skills-modal.js +29 -28
  523. package/dist/components/features/conversation-panel/skills-modal.js.map +1 -1
  524. package/dist/components/features/files-tab/file-content-viewer.cjs +1 -1
  525. package/dist/components/features/files-tab/file-content-viewer.js +5 -5
  526. package/dist/components/features/home/featured-automations/automation-health-indicator.cjs +1 -1
  527. package/dist/components/features/home/featured-automations/automation-health-indicator.cjs.map +1 -1
  528. package/dist/components/features/home/featured-automations/automation-health-indicator.js +1 -1
  529. package/dist/components/features/home/featured-automations/automation-health-indicator.js.map +1 -1
  530. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.cjs +1 -1
  531. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.cjs.map +1 -1
  532. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.d.ts +4 -3
  533. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.js +22 -25
  534. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.js.map +1 -1
  535. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.cjs +1 -1
  536. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.cjs.map +1 -1
  537. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.d.ts +1 -1
  538. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.js +15 -15
  539. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.js.map +1 -1
  540. package/dist/components/features/home/featured-automations/automation-run-health.cjs +1 -1
  541. package/dist/components/features/home/featured-automations/automation-run-health.cjs.map +1 -1
  542. package/dist/components/features/home/featured-automations/automation-run-health.d.ts +5 -3
  543. package/dist/components/features/home/featured-automations/automation-run-health.js +30 -23
  544. package/dist/components/features/home/featured-automations/automation-run-health.js.map +1 -1
  545. package/dist/components/features/home/featured-automations/home-automation-activity.d.ts +3 -2
  546. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs +1 -1
  547. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs.map +1 -1
  548. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.d.ts +1 -1
  549. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js +59 -37
  550. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js.map +1 -1
  551. package/dist/components/features/manifest/manifest-form-field.d.ts +3 -1
  552. package/dist/components/features/manifest/manifest-review-step.d.ts +3 -1
  553. package/dist/components/features/onboarding/onboarding-host.d.ts +3 -4
  554. package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +5 -9
  555. package/dist/components/features/settings/acp-auth-status-banner.d.ts +23 -6
  556. package/dist/components/features/settings/cloud-settings-link.cjs +1 -1
  557. package/dist/components/features/settings/cloud-settings-link.cjs.map +1 -1
  558. package/dist/components/features/settings/cloud-settings-link.js +5 -3
  559. package/dist/components/features/settings/cloud-settings-link.js.map +1 -1
  560. package/dist/components/features/settings/integrations-settings-link.cjs +1 -1
  561. package/dist/components/features/settings/integrations-settings-link.cjs.map +1 -1
  562. package/dist/components/features/settings/integrations-settings-link.d.ts +3 -1
  563. package/dist/components/features/settings/integrations-settings-link.js +20 -17
  564. package/dist/components/features/settings/integrations-settings-link.js.map +1 -1
  565. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.cjs +1 -1
  566. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.js +2 -1
  567. package/dist/components/features/settings/llm-profiles/profiles-body.d.ts +2 -2
  568. package/dist/components/features/settings/llm-profiles/provider-connection-modal.cjs +1 -1
  569. package/dist/components/features/settings/llm-profiles/provider-connection-modal.js +3 -0
  570. package/dist/components/features/settings/sdk-settings/field-help.cjs +1 -1
  571. package/dist/components/features/settings/sdk-settings/field-help.cjs.map +1 -1
  572. package/dist/components/features/settings/sdk-settings/field-help.d.ts +1 -1
  573. package/dist/components/features/settings/sdk-settings/field-help.js +1 -1
  574. package/dist/components/features/settings/sdk-settings/field-help.js.map +1 -1
  575. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs +1 -1
  576. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs.map +1 -1
  577. package/dist/components/features/settings/sdk-settings/sdk-section-page.d.ts +8 -7
  578. package/dist/components/features/settings/sdk-settings/sdk-section-page.js +172 -142
  579. package/dist/components/features/settings/sdk-settings/sdk-section-page.js.map +1 -1
  580. package/dist/components/features/settings/secrets-settings/secret-form.cjs +1 -1
  581. package/dist/components/features/settings/secrets-settings/secret-form.cjs.map +1 -1
  582. package/dist/components/features/settings/secrets-settings/secret-form.js +38 -25
  583. package/dist/components/features/settings/secrets-settings/secret-form.js.map +1 -1
  584. package/dist/components/features/settings/settings-dropdown-input.cjs +1 -1
  585. package/dist/components/features/settings/settings-dropdown-input.js +6 -6
  586. package/dist/components/features/sidebar/sidebar-nav-link.cjs +1 -1
  587. package/dist/components/features/sidebar/sidebar-nav-link.cjs.map +1 -1
  588. package/dist/components/features/sidebar/sidebar-nav-link.d.ts +10 -1
  589. package/dist/components/features/sidebar/sidebar-nav-link.js +54 -36
  590. package/dist/components/features/sidebar/sidebar-nav-link.js.map +1 -1
  591. package/dist/components/features/sidebar/sidebar-rail-body.cjs +1 -1
  592. package/dist/components/features/sidebar/sidebar-rail-body.cjs.map +1 -1
  593. package/dist/components/features/sidebar/sidebar-rail-body.d.ts +3 -1
  594. package/dist/components/features/sidebar/sidebar-rail-body.js +98 -58
  595. package/dist/components/features/sidebar/sidebar-rail-body.js.map +1 -1
  596. package/dist/components/features/sidebar/sidebar.cjs +1 -1
  597. package/dist/components/features/sidebar/sidebar.cjs.map +1 -1
  598. package/dist/components/features/sidebar/sidebar.js +44 -43
  599. package/dist/components/features/sidebar/sidebar.js.map +1 -1
  600. package/dist/components/features/sidebar/use-sidebar-onboarding-checklist.cjs +1 -1
  601. package/dist/components/features/sidebar/use-sidebar-onboarding-checklist.js +3 -3
  602. package/dist/components/features/skills/build-skill-pills.cjs +1 -1
  603. package/dist/components/features/skills/build-skill-pills.cjs.map +1 -1
  604. package/dist/components/features/skills/build-skill-pills.js +60 -52
  605. package/dist/components/features/skills/build-skill-pills.js.map +1 -1
  606. package/dist/components/features/skills/extensions-navigation.cjs +1 -1
  607. package/dist/components/features/skills/extensions-navigation.cjs.map +1 -1
  608. package/dist/components/features/skills/extensions-navigation.js +54 -43
  609. package/dist/components/features/skills/extensions-navigation.js.map +1 -1
  610. package/dist/components/features/skills/skill-filter.cjs +1 -1
  611. package/dist/components/features/skills/skill-filter.cjs.map +1 -1
  612. package/dist/components/features/skills/skill-filter.d.ts +8 -3
  613. package/dist/components/features/skills/skill-filter.js +69 -52
  614. package/dist/components/features/skills/skill-filter.js.map +1 -1
  615. package/dist/components/shared/buttons/styled-tooltip.cjs +1 -1
  616. package/dist/components/shared/buttons/styled-tooltip.cjs.map +1 -1
  617. package/dist/components/shared/buttons/styled-tooltip.d.ts +2 -1
  618. package/dist/components/shared/buttons/styled-tooltip.js +2 -1
  619. package/dist/components/shared/buttons/styled-tooltip.js.map +1 -1
  620. package/dist/components/shared/filters/enum-filter-dropdown.cjs +1 -1
  621. package/dist/components/shared/filters/enum-filter-dropdown.cjs.map +1 -1
  622. package/dist/components/shared/filters/enum-filter-dropdown.d.ts +5 -1
  623. package/dist/components/shared/filters/enum-filter-dropdown.js +14 -14
  624. package/dist/components/shared/filters/enum-filter-dropdown.js.map +1 -1
  625. package/dist/components/shared/modals/confirmation-modal.cjs +1 -1
  626. package/dist/components/shared/modals/confirmation-modal.cjs.map +1 -1
  627. package/dist/components/shared/modals/confirmation-modal.d.ts +2 -1
  628. package/dist/components/shared/modals/confirmation-modal.js +8 -8
  629. package/dist/components/shared/modals/confirmation-modal.js.map +1 -1
  630. package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
  631. package/dist/components/shared/modals/settings/model-selector.js +27 -27
  632. package/dist/components/shared/modals/settings/settings-form.cjs +1 -1
  633. package/dist/components/shared/modals/settings/settings-form.js +8 -8
  634. package/dist/config/defaults.cjs +1 -1
  635. package/dist/config/defaults.cjs.map +1 -1
  636. package/dist/config/defaults.js +9 -5
  637. package/dist/config/defaults.js.map +1 -1
  638. package/dist/constants/settings-nav.d.ts +8 -0
  639. package/dist/contexts/conversation-websocket-context.cjs +3 -3
  640. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  641. package/dist/contexts/conversation-websocket-context.d.ts +5 -0
  642. package/dist/contexts/conversation-websocket-context.js +194 -198
  643. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  644. package/dist/contexts/websocket-provider-wrapper.cjs +1 -1
  645. package/dist/contexts/websocket-provider-wrapper.cjs.map +1 -1
  646. package/dist/contexts/websocket-provider-wrapper.js +31 -15
  647. package/dist/contexts/websocket-provider-wrapper.js.map +1 -1
  648. package/dist/extensions/canvas-extension-module-loader.d.ts +11 -0
  649. package/dist/hooks/chat/record-model-switch-message.cjs +1 -1
  650. package/dist/hooks/chat/record-model-switch-message.cjs.map +1 -1
  651. package/dist/hooks/chat/record-model-switch-message.d.ts +31 -0
  652. package/dist/hooks/chat/record-model-switch-message.js +25 -12
  653. package/dist/hooks/chat/record-model-switch-message.js.map +1 -1
  654. package/dist/hooks/chat/use-plan-mode-interceptor.cjs +2 -0
  655. package/dist/hooks/chat/use-plan-mode-interceptor.cjs.map +1 -0
  656. package/dist/hooks/chat/use-plan-mode-interceptor.d.ts +16 -0
  657. package/dist/hooks/chat/use-plan-mode-interceptor.js +43 -0
  658. package/dist/hooks/chat/use-plan-mode-interceptor.js.map +1 -0
  659. package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
  660. package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
  661. package/dist/hooks/mutation/use-create-conversation.js +5 -1
  662. package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
  663. package/dist/hooks/mutation/use-manage-canvas-extensions.d.ts +12 -0
  664. package/dist/hooks/mutation/use-read-conversation-file.cjs +1 -1
  665. package/dist/hooks/mutation/use-read-conversation-file.cjs.map +1 -1
  666. package/dist/hooks/mutation/use-read-conversation-file.js +11 -4
  667. package/dist/hooks/mutation/use-read-conversation-file.js.map +1 -1
  668. package/dist/hooks/mutation/use-save-settings.cjs +1 -1
  669. package/dist/hooks/mutation/use-save-settings.js +3 -3
  670. package/dist/hooks/mutation/use-switch-llm-profile.cjs +1 -1
  671. package/dist/hooks/mutation/use-switch-llm-profile.cjs.map +1 -1
  672. package/dist/hooks/mutation/use-switch-llm-profile.js +13 -25
  673. package/dist/hooks/mutation/use-switch-llm-profile.js.map +1 -1
  674. package/dist/hooks/mutation/use-update-conversation-tags.cjs +2 -0
  675. package/dist/hooks/mutation/use-update-conversation-tags.cjs.map +1 -0
  676. package/dist/hooks/mutation/use-update-conversation-tags.d.ts +8 -0
  677. package/dist/hooks/mutation/use-update-conversation-tags.js +45 -0
  678. package/dist/hooks/mutation/use-update-conversation-tags.js.map +1 -0
  679. package/dist/hooks/query/concurrency-limiter.cjs +2 -0
  680. package/dist/hooks/query/concurrency-limiter.cjs.map +1 -0
  681. package/dist/hooks/query/concurrency-limiter.d.ts +22 -0
  682. package/dist/hooks/query/concurrency-limiter.js +29 -0
  683. package/dist/hooks/query/concurrency-limiter.js.map +1 -0
  684. package/dist/hooks/query/query-keys.cjs +1 -1
  685. package/dist/hooks/query/query-keys.cjs.map +1 -1
  686. package/dist/hooks/query/query-keys.d.ts +10 -0
  687. package/dist/hooks/query/query-keys.js +2 -2
  688. package/dist/hooks/query/query-keys.js.map +1 -1
  689. package/dist/hooks/query/use-active-conversation.cjs +1 -1
  690. package/dist/hooks/query/use-active-conversation.js +4 -4
  691. package/dist/hooks/query/use-automation-sdk-version.cjs +1 -1
  692. package/dist/hooks/query/use-automation-sdk-version.cjs.map +1 -1
  693. package/dist/hooks/query/use-automation-sdk-version.d.ts +0 -1
  694. package/dist/hooks/query/use-automation-sdk-version.js +30 -55
  695. package/dist/hooks/query/use-automation-sdk-version.js.map +1 -1
  696. package/dist/hooks/query/use-automations.cjs.map +1 -1
  697. package/dist/hooks/query/use-automations.js.map +1 -1
  698. package/dist/hooks/query/use-backends-health.cjs +1 -1
  699. package/dist/hooks/query/use-backends-health.cjs.map +1 -1
  700. package/dist/hooks/query/use-backends-health.d.ts +1 -1
  701. package/dist/hooks/query/use-backends-health.js +50 -64
  702. package/dist/hooks/query/use-backends-health.js.map +1 -1
  703. package/dist/hooks/query/use-canvas-extensions.cjs +1 -0
  704. package/dist/hooks/query/use-canvas-extensions.d.ts +5 -0
  705. package/dist/hooks/query/use-canvas-extensions.js +3 -0
  706. package/dist/hooks/query/use-commit-changes.cjs +1 -1
  707. package/dist/hooks/query/use-commit-changes.js +4 -4
  708. package/dist/hooks/query/use-conversation-skills.cjs +1 -1
  709. package/dist/hooks/query/use-conversation-skills.cjs.map +1 -1
  710. package/dist/hooks/query/use-conversation-skills.d.ts +14 -5
  711. package/dist/hooks/query/use-conversation-skills.js +20 -4
  712. package/dist/hooks/query/use-conversation-skills.js.map +1 -1
  713. package/dist/hooks/query/use-latest-automation-runs.cjs +1 -1
  714. package/dist/hooks/query/use-latest-automation-runs.js +1 -0
  715. package/dist/hooks/query/use-paginated-conversations.cjs +1 -1
  716. package/dist/hooks/query/use-paginated-conversations.cjs.map +1 -1
  717. package/dist/hooks/query/use-paginated-conversations.js +19 -16
  718. package/dist/hooks/query/use-paginated-conversations.js.map +1 -1
  719. package/dist/hooks/query/use-provider-connections.cjs +1 -1
  720. package/dist/hooks/query/use-provider-connections.cjs.map +1 -1
  721. package/dist/hooks/query/use-provider-connections.d.ts +6 -3
  722. package/dist/hooks/query/use-provider-connections.js +7 -3
  723. package/dist/hooks/query/use-provider-connections.js.map +1 -1
  724. package/dist/hooks/query/use-search-providers.cjs +1 -1
  725. package/dist/hooks/query/use-search-providers.cjs.map +1 -1
  726. package/dist/hooks/query/use-search-providers.js +17 -10
  727. package/dist/hooks/query/use-search-providers.js.map +1 -1
  728. package/dist/hooks/query/use-sub-conversations.cjs +1 -1
  729. package/dist/hooks/query/use-sub-conversations.cjs.map +1 -1
  730. package/dist/hooks/query/use-sub-conversations.js +16 -16
  731. package/dist/hooks/query/use-sub-conversations.js.map +1 -1
  732. package/dist/hooks/query/use-unified-get-git-changes.cjs +1 -1
  733. package/dist/hooks/query/use-unified-get-git-changes.js +4 -4
  734. package/dist/hooks/query/use-unified-git-commits.cjs +1 -1
  735. package/dist/hooks/query/use-unified-git-commits.js +4 -4
  736. package/dist/hooks/query/use-unified-git-diff.cjs +1 -1
  737. package/dist/hooks/query/use-unified-git-diff.js +4 -4
  738. package/dist/hooks/query/use-unified-vscode-url.cjs +1 -1
  739. package/dist/hooks/query/use-unified-vscode-url.cjs.map +1 -1
  740. package/dist/hooks/query/use-unified-vscode-url.d.ts +1 -0
  741. package/dist/hooks/query/use-unified-vscode-url.js +40 -26
  742. package/dist/hooks/query/use-unified-vscode-url.js.map +1 -1
  743. package/dist/hooks/query/use-workspace-file-content.cjs +1 -1
  744. package/dist/hooks/query/use-workspace-file-content.js +5 -5
  745. package/dist/hooks/query/use-workspace-files.cjs +1 -1
  746. package/dist/hooks/query/use-workspace-files.js +6 -6
  747. package/dist/hooks/use-acp-credential-form.d.ts +6 -0
  748. package/dist/hooks/use-acp-model-context.cjs +1 -1
  749. package/dist/hooks/use-acp-model-context.js +3 -3
  750. package/dist/hooks/use-agent-state.cjs +1 -1
  751. package/dist/hooks/use-agent-state.cjs.map +1 -1
  752. package/dist/hooks/use-agent-state.d.ts +17 -1
  753. package/dist/hooks/use-agent-state.js +19 -9
  754. package/dist/hooks/use-agent-state.js.map +1 -1
  755. package/dist/hooks/use-automation-permissions.cjs +2 -0
  756. package/dist/hooks/use-automation-permissions.cjs.map +1 -0
  757. package/dist/hooks/use-automation-permissions.d.ts +43 -0
  758. package/dist/hooks/use-automation-permissions.js +52 -0
  759. package/dist/hooks/use-automation-permissions.js.map +1 -0
  760. package/dist/hooks/use-chat-input-model-state.cjs +1 -1
  761. package/dist/hooks/use-chat-input-model-state.js +3 -3
  762. package/dist/hooks/use-conversation-overview-git-diff-stats.cjs +1 -1
  763. package/dist/hooks/use-conversation-overview-git-diff-stats.js +4 -4
  764. package/dist/hooks/use-handle-plan-click.cjs +1 -1
  765. package/dist/hooks/use-handle-plan-click.cjs.map +1 -1
  766. package/dist/hooks/use-handle-plan-click.d.ts +3 -1
  767. package/dist/hooks/use-handle-plan-click.js +85 -26
  768. package/dist/hooks/use-handle-plan-click.js.map +1 -1
  769. package/dist/hooks/use-home-automation-actions.cjs +1 -1
  770. package/dist/hooks/use-home-automation-actions.cjs.map +1 -1
  771. package/dist/hooks/use-home-automation-actions.js +1 -0
  772. package/dist/hooks/use-home-automation-actions.js.map +1 -1
  773. package/dist/hooks/use-llm-configured.cjs +1 -1
  774. package/dist/hooks/use-llm-configured.js +3 -3
  775. package/dist/hooks/use-manifest-preflight.d.ts +1 -1
  776. package/dist/hooks/use-pinned-home-route.cjs +2 -0
  777. package/dist/hooks/use-pinned-home-route.cjs.map +1 -0
  778. package/dist/hooks/use-pinned-home-route.d.ts +33 -0
  779. package/dist/hooks/use-pinned-home-route.js +34 -0
  780. package/dist/hooks/use-pinned-home-route.js.map +1 -0
  781. package/dist/hooks/use-skill-enablement.cjs +2 -0
  782. package/dist/hooks/use-skill-enablement.cjs.map +1 -0
  783. package/dist/hooks/use-skill-enablement.d.ts +14 -0
  784. package/dist/hooks/use-skill-enablement.js +80 -0
  785. package/dist/hooks/use-skill-enablement.js.map +1 -0
  786. package/dist/hooks/use-unified-websocket-status.cjs +1 -1
  787. package/dist/hooks/use-unified-websocket-status.cjs.map +1 -1
  788. package/dist/hooks/use-unified-websocket-status.d.ts +7 -0
  789. package/dist/hooks/use-unified-websocket-status.js +5 -1
  790. package/dist/hooks/use-unified-websocket-status.js.map +1 -1
  791. package/dist/i18n/declaration.cjs +1 -1
  792. package/dist/i18n/declaration.cjs.map +1 -1
  793. package/dist/i18n/declaration.d.ts +83 -14
  794. package/dist/i18n/declaration.js +1 -1
  795. package/dist/i18n/declaration.js.map +1 -1
  796. package/dist/i18n/translation.cjs +3 -3
  797. package/dist/i18n/translation.cjs.map +1 -1
  798. package/dist/i18n/translation.js +1575 -402
  799. package/dist/i18n/translation.js.map +1 -1
  800. package/dist/index.cjs +1 -1
  801. package/dist/index.js +15 -14
  802. package/dist/lib/index.cjs +1 -1
  803. package/dist/lib/index.d.ts +1 -0
  804. package/dist/lib/index.js +1 -0
  805. package/dist/locales/ar/openhands.json +94 -25
  806. package/dist/locales/ca/openhands.json +94 -25
  807. package/dist/locales/de/openhands.json +94 -25
  808. package/dist/locales/en/openhands.json +94 -25
  809. package/dist/locales/es/openhands.json +94 -25
  810. package/dist/locales/fr/openhands.json +94 -25
  811. package/dist/locales/it/openhands.json +94 -25
  812. package/dist/locales/ja/openhands.json +94 -25
  813. package/dist/locales/ko-KR/openhands.json +94 -25
  814. package/dist/locales/no/openhands.json +94 -25
  815. package/dist/locales/pt/openhands.json +94 -25
  816. package/dist/locales/tr/openhands.json +94 -25
  817. package/dist/locales/uk/openhands.json +94 -25
  818. package/dist/locales/zh-CN/openhands.json +94 -25
  819. package/dist/locales/zh-TW/openhands.json +94 -25
  820. package/dist/manifests/automation-insights.cjs.map +1 -1
  821. package/dist/manifests/automation-insights.d.ts +10 -2
  822. package/dist/manifests/automation-insights.js.map +1 -1
  823. package/dist/manifests/automation-interface.cjs +1 -1
  824. package/dist/manifests/automation-interface.cjs.map +1 -1
  825. package/dist/manifests/automation-interface.d.ts +6 -0
  826. package/dist/manifests/automation-interface.js +11 -8
  827. package/dist/manifests/automation-interface.js.map +1 -1
  828. package/dist/manifests/automation-setup.cjs +1 -1
  829. package/dist/manifests/automation-setup.cjs.map +1 -1
  830. package/dist/manifests/automation-setup.d.ts +9 -16
  831. package/dist/manifests/automation-setup.js +17 -7
  832. package/dist/manifests/automation-setup.js.map +1 -1
  833. package/dist/manifests/manifest-actions.d.ts +1 -1
  834. package/dist/manifests/manifest-capabilities.d.ts +3 -1
  835. package/dist/manifests/manifest-local-validation.cjs +2 -0
  836. package/dist/manifests/manifest-local-validation.cjs.map +1 -0
  837. package/dist/manifests/manifest-local-validation.d.ts +15 -11
  838. package/dist/manifests/manifest-local-validation.js +8 -0
  839. package/dist/manifests/manifest-local-validation.js.map +1 -0
  840. package/dist/manifests/manifest-template.cjs +2 -0
  841. package/dist/manifests/manifest-template.cjs.map +1 -0
  842. package/dist/manifests/manifest-template.d.ts +4 -4
  843. package/dist/manifests/manifest-template.js +12 -0
  844. package/dist/manifests/manifest-template.js.map +1 -0
  845. package/dist/manifests/manifest-validation.cjs +1 -1
  846. package/dist/manifests/manifest-validation.cjs.map +1 -1
  847. package/dist/manifests/manifest-validation.js +106 -28
  848. package/dist/manifests/manifest-validation.js.map +1 -1
  849. package/dist/manifests/types.cjs.map +1 -1
  850. package/dist/manifests/types.d.ts +39 -4
  851. package/dist/manifests/types.js.map +1 -1
  852. package/dist/node_modules/@openhands/extensions/automations/bundle-index.cjs +45 -4
  853. package/dist/node_modules/@openhands/extensions/automations/bundle-index.cjs.map +1 -1
  854. package/dist/node_modules/@openhands/extensions/automations/bundle-index.js +1 -1
  855. package/dist/node_modules/@openhands/extensions/automations/bundle-index.js.map +1 -1
  856. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.cjs +8 -0
  857. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.cjs.map +1 -0
  858. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.js +221 -0
  859. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.js.map +1 -0
  860. package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.cjs +1 -1
  861. package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.cjs.map +1 -1
  862. package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.js +5 -0
  863. package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.js.map +1 -1
  864. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.cjs +2 -2
  865. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.cjs.map +1 -1
  866. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.js +6 -1
  867. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.js.map +1 -1
  868. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.cjs +2 -2
  869. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.cjs.map +1 -1
  870. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.js +6 -1
  871. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.js.map +1 -1
  872. package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.cjs +1 -1
  873. package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.cjs.map +1 -1
  874. package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.js +5 -0
  875. package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.js.map +1 -1
  876. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.cjs +9 -0
  877. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.cjs.map +1 -0
  878. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.js +21 -0
  879. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.js.map +1 -0
  880. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.cjs +9 -0
  881. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.cjs.map +1 -0
  882. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.js +21 -0
  883. package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.js.map +1 -0
  884. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.cjs +9 -0
  885. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.cjs.map +1 -0
  886. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.js +21 -0
  887. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.js.map +1 -0
  888. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.cjs +9 -0
  889. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.cjs.map +1 -0
  890. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.js +21 -0
  891. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.js.map +1 -0
  892. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.cjs +9 -0
  893. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.cjs.map +1 -0
  894. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.js +21 -0
  895. package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.js.map +1 -0
  896. package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.cjs +1 -1
  897. package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.cjs.map +1 -1
  898. package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.js +5 -0
  899. package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.js.map +1 -1
  900. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.cjs +30 -0
  901. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.cjs.map +1 -0
  902. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.js +65 -0
  903. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.js.map +1 -0
  904. package/dist/node_modules/@openhands/extensions/automations/catalog-index.cjs +1 -1
  905. package/dist/node_modules/@openhands/extensions/automations/catalog-index.cjs.map +1 -1
  906. package/dist/node_modules/@openhands/extensions/automations/catalog-index.js +28 -14
  907. package/dist/node_modules/@openhands/extensions/automations/catalog-index.js.map +1 -1
  908. package/dist/node_modules/@openhands/extensions/automations/interface.cjs +1 -1
  909. package/dist/node_modules/@openhands/extensions/automations/interface.cjs.map +1 -1
  910. package/dist/node_modules/@openhands/extensions/automations/interface.js +2 -1
  911. package/dist/node_modules/@openhands/extensions/automations/interface.js.map +1 -1
  912. package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.cjs +1 -1
  913. package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.cjs.map +1 -1
  914. package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.js +2 -2
  915. package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.js.map +1 -1
  916. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.cjs +2 -0
  917. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.cjs.map +1 -0
  918. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.js +59 -0
  919. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.js.map +1 -0
  920. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs +1 -1
  921. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs.map +1 -1
  922. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js +1 -1
  923. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js.map +1 -1
  924. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.cjs +1 -1
  925. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.cjs.map +1 -1
  926. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.js +74 -72
  927. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.js.map +1 -1
  928. package/dist/node_modules/@openhands/extensions/skills/index.cjs +42 -3
  929. package/dist/node_modules/@openhands/extensions/skills/index.cjs.map +1 -1
  930. package/dist/node_modules/@openhands/extensions/skills/index.js +22 -3
  931. package/dist/node_modules/@openhands/extensions/skills/index.js.map +1 -1
  932. package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-conversation.cjs +1 -1
  933. package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-conversation.js +1 -1
  934. package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.cjs +1 -1
  935. package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.cjs.map +1 -1
  936. package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.js +8 -7
  937. package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.js.map +1 -1
  938. package/dist/node_modules/@openhands/typescript-client/dist/index.cjs +1 -1
  939. package/dist/node_modules/@openhands/typescript-client/dist/index.js +2 -2
  940. package/dist/node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs +1 -1
  941. package/dist/node_modules/@tanstack/react-query/build/modern/QueryClientProvider.js +1 -1
  942. package/dist/node_modules/lucide-react/dist/esm/icons/blocks.cjs +2 -0
  943. package/dist/node_modules/lucide-react/dist/esm/icons/blocks.cjs.map +1 -0
  944. package/dist/node_modules/lucide-react/dist/esm/icons/blocks.js +16 -0
  945. package/dist/node_modules/lucide-react/dist/esm/icons/blocks.js.map +1 -0
  946. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.cjs +2 -0
  947. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.cjs.map +1 -0
  948. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.js +21 -0
  949. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.js.map +1 -0
  950. package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.cjs +2 -0
  951. package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.cjs.map +1 -0
  952. package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.js +23 -0
  953. package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.js.map +1 -0
  954. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.cjs +2 -0
  955. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.cjs.map +1 -0
  956. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.js +23 -0
  957. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.js.map +1 -0
  958. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.cjs +2 -0
  959. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.cjs.map +1 -0
  960. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.js +43 -0
  961. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.js.map +1 -0
  962. package/dist/node_modules/lucide-react/dist/esm/icons/tags.cjs +2 -0
  963. package/dist/node_modules/lucide-react/dist/esm/icons/tags.cjs.map +1 -0
  964. package/dist/node_modules/lucide-react/dist/esm/icons/tags.js +22 -0
  965. package/dist/node_modules/lucide-react/dist/esm/icons/tags.js.map +1 -0
  966. package/dist/package.cjs +1 -1
  967. package/dist/package.cjs.map +1 -1
  968. package/dist/package.js +5 -5
  969. package/dist/package.js.map +1 -1
  970. package/dist/routes/agent-settings.d.ts +12 -0
  971. package/dist/routes/app-settings.cjs +1 -1
  972. package/dist/routes/app-settings.cjs.map +1 -1
  973. package/dist/routes/app-settings.js +11 -11
  974. package/dist/routes/files-tab.cjs +1 -1
  975. package/dist/routes/files-tab.cjs.map +1 -1
  976. package/dist/routes/files-tab.js +14 -14
  977. package/dist/routes/files-tab.js.map +1 -1
  978. package/dist/routes/llm-settings.cjs +1 -1
  979. package/dist/routes/llm-settings.cjs.map +1 -1
  980. package/dist/routes/llm-settings.d.ts +3 -1
  981. package/dist/routes/llm-settings.js +100 -91
  982. package/dist/routes/llm-settings.js.map +1 -1
  983. package/dist/routes/planner-tab.cjs +1 -1
  984. package/dist/routes/planner-tab.cjs.map +1 -1
  985. package/dist/routes/planner-tab.js +44 -28
  986. package/dist/routes/planner-tab.js.map +1 -1
  987. package/dist/services/canvas-ui.cjs +1 -1
  988. package/dist/services/canvas-ui.cjs.map +1 -1
  989. package/dist/services/canvas-ui.d.ts +5 -0
  990. package/dist/services/canvas-ui.js +24 -10
  991. package/dist/services/canvas-ui.js.map +1 -1
  992. package/dist/services/settings.cjs +1 -1
  993. package/dist/services/settings.cjs.map +1 -1
  994. package/dist/services/settings.js +3 -2
  995. package/dist/services/settings.js.map +1 -1
  996. package/dist/services/telemetry.cjs +1 -1
  997. package/dist/services/telemetry.cjs.map +1 -1
  998. package/dist/services/telemetry.d.ts +5 -0
  999. package/dist/services/telemetry.js +122 -115
  1000. package/dist/services/telemetry.js.map +1 -1
  1001. package/dist/stores/conversation-panel-preferences-store.cjs +1 -1
  1002. package/dist/stores/conversation-panel-preferences-store.cjs.map +1 -1
  1003. package/dist/stores/conversation-panel-preferences-store.d.ts +32 -5
  1004. package/dist/stores/conversation-panel-preferences-store.js +29 -12
  1005. package/dist/stores/conversation-panel-preferences-store.js.map +1 -1
  1006. package/dist/stores/conversation-state-store.cjs +1 -1
  1007. package/dist/stores/conversation-state-store.cjs.map +1 -1
  1008. package/dist/stores/conversation-state-store.d.ts +9 -3
  1009. package/dist/stores/conversation-state-store.js +6 -3
  1010. package/dist/stores/conversation-state-store.js.map +1 -1
  1011. package/dist/stores/conversation-store.cjs +1 -1
  1012. package/dist/stores/conversation-store.cjs.map +1 -1
  1013. package/dist/stores/conversation-store.d.ts +2 -0
  1014. package/dist/stores/conversation-store.js +3 -0
  1015. package/dist/stores/conversation-store.js.map +1 -1
  1016. package/dist/stores/model-store.cjs +1 -1
  1017. package/dist/stores/model-store.cjs.map +1 -1
  1018. package/dist/stores/model-store.d.ts +7 -0
  1019. package/dist/stores/model-store.js +4 -0
  1020. package/dist/stores/model-store.js.map +1 -1
  1021. package/dist/types/automation.cjs.map +1 -1
  1022. package/dist/types/automation.d.ts +66 -1
  1023. package/dist/types/automation.js.map +1 -1
  1024. package/dist/types/canvas-extension.cjs +2 -0
  1025. package/dist/types/canvas-extension.cjs.map +1 -0
  1026. package/dist/types/canvas-extension.d.ts +77 -0
  1027. package/dist/types/canvas-extension.js +6 -0
  1028. package/dist/types/canvas-extension.js.map +1 -0
  1029. package/dist/types/settings.d.ts +7 -0
  1030. package/dist/ui/context-menu.d.ts +1 -1
  1031. package/dist/ui/help-link.cjs +1 -1
  1032. package/dist/ui/help-link.cjs.map +1 -1
  1033. package/dist/ui/help-link.d.ts +6 -1
  1034. package/dist/ui/help-link.js +32 -18
  1035. package/dist/ui/help-link.js.map +1 -1
  1036. package/dist/ui/toggle-switch.cjs +1 -1
  1037. package/dist/ui/toggle-switch.cjs.map +1 -1
  1038. package/dist/ui/toggle-switch.d.ts +4 -1
  1039. package/dist/ui/toggle-switch.js +4 -3
  1040. package/dist/ui/toggle-switch.js.map +1 -1
  1041. package/dist/utils/acp-auth-display.d.ts +35 -0
  1042. package/dist/utils/automation-catalog.cjs +1 -1
  1043. package/dist/utils/automation-catalog.cjs.map +1 -1
  1044. package/dist/utils/automation-catalog.d.ts +12 -1
  1045. package/dist/utils/automation-catalog.js +42 -12
  1046. package/dist/utils/automation-catalog.js.map +1 -1
  1047. package/dist/utils/automation-run-display.cjs +2 -0
  1048. package/dist/utils/automation-run-display.cjs.map +1 -0
  1049. package/dist/utils/automation-run-display.d.ts +20 -0
  1050. package/dist/utils/automation-run-display.js +85 -0
  1051. package/dist/utils/automation-run-display.js.map +1 -0
  1052. package/dist/utils/automation-schedule.cjs +1 -1
  1053. package/dist/utils/automation-schedule.cjs.map +1 -1
  1054. package/dist/utils/automation-schedule.d.ts +16 -0
  1055. package/dist/utils/automation-schedule.js +2 -0
  1056. package/dist/utils/automation-schedule.js.map +1 -1
  1057. package/dist/utils/cache-utils.cjs +1 -1
  1058. package/dist/utils/cache-utils.js +5 -5
  1059. package/dist/utils/constants.cjs +1 -1
  1060. package/dist/utils/constants.cjs.map +1 -1
  1061. package/dist/utils/constants.d.ts +4 -0
  1062. package/dist/utils/constants.js +23 -3
  1063. package/dist/utils/constants.js.map +1 -1
  1064. package/dist/utils/format-event-timestamp.cjs +2 -0
  1065. package/dist/utils/format-event-timestamp.cjs.map +1 -0
  1066. package/dist/utils/format-event-timestamp.d.ts +1 -0
  1067. package/dist/utils/format-event-timestamp.js +13 -0
  1068. package/dist/utils/format-event-timestamp.js.map +1 -0
  1069. package/dist/utils/format-model-name.cjs +1 -1
  1070. package/dist/utils/format-model-name.cjs.map +1 -1
  1071. package/dist/utils/format-model-name.d.ts +7 -3
  1072. package/dist/utils/format-model-name.js +6 -11
  1073. package/dist/utils/format-model-name.js.map +1 -1
  1074. package/dist/utils/format-relative-time.cjs +1 -1
  1075. package/dist/utils/format-relative-time.cjs.map +1 -1
  1076. package/dist/utils/format-relative-time.d.ts +7 -0
  1077. package/dist/utils/format-relative-time.js +7 -2
  1078. package/dist/utils/format-relative-time.js.map +1 -1
  1079. package/dist/utils/git-diff-stats.cjs +2 -2
  1080. package/dist/utils/git-diff-stats.cjs.map +1 -1
  1081. package/dist/utils/git-diff-stats.d.ts +4 -0
  1082. package/dist/utils/git-diff-stats.js +12 -2
  1083. package/dist/utils/git-diff-stats.js.map +1 -1
  1084. package/dist/utils/path-utils.cjs +1 -1
  1085. package/dist/utils/path-utils.cjs.map +1 -1
  1086. package/dist/utils/path-utils.d.ts +15 -3
  1087. package/dist/utils/path-utils.js +29 -3
  1088. package/dist/utils/path-utils.js.map +1 -1
  1089. package/dist/utils/plan-file.cjs +4 -0
  1090. package/dist/utils/plan-file.cjs.map +1 -0
  1091. package/dist/utils/plan-file.d.ts +18 -0
  1092. package/dist/utils/plan-file.js +58 -0
  1093. package/dist/utils/plan-file.js.map +1 -0
  1094. package/dist/utils/recommended-automation-rail.cjs +1 -1
  1095. package/dist/utils/recommended-automation-rail.cjs.map +1 -1
  1096. package/dist/utils/recommended-automation-rail.js +1 -0
  1097. package/dist/utils/recommended-automation-rail.js.map +1 -1
  1098. package/dist/utils/scroll-fade-state.cjs +1 -1
  1099. package/dist/utils/scroll-fade-state.cjs.map +1 -1
  1100. package/dist/utils/scroll-fade-state.d.ts +6 -0
  1101. package/dist/utils/scroll-fade-state.js +8 -1
  1102. package/dist/utils/scroll-fade-state.js.map +1 -1
  1103. package/dist/utils/sdk-settings-schema.cjs +1 -1
  1104. package/dist/utils/sdk-settings-schema.cjs.map +1 -1
  1105. package/dist/utils/sdk-settings-schema.d.ts +1 -0
  1106. package/dist/utils/sdk-settings-schema.js +25 -22
  1107. package/dist/utils/sdk-settings-schema.js.map +1 -1
  1108. package/dist/utils/skill-enablement.cjs +2 -0
  1109. package/dist/utils/skill-enablement.cjs.map +1 -0
  1110. package/dist/utils/skill-enablement.d.ts +51 -0
  1111. package/dist/utils/skill-enablement.js +31 -0
  1112. package/dist/utils/skill-enablement.js.map +1 -0
  1113. package/dist/utils/utils.cjs +1 -1
  1114. package/dist/utils/utils.cjs.map +1 -1
  1115. package/dist/utils/utils.js +11 -11
  1116. package/dist/utils/utils.js.map +1 -1
  1117. package/dist/utils/vscode-origin.cjs +2 -0
  1118. package/dist/utils/vscode-origin.cjs.map +1 -0
  1119. package/dist/utils/vscode-origin.d.ts +55 -0
  1120. package/dist/utils/vscode-origin.js +27 -0
  1121. package/dist/utils/vscode-origin.js.map +1 -0
  1122. package/dist/utils/workspace-mode.d.ts +4 -0
  1123. package/package.json +5 -5
  1124. package/scripts/check-sdk-version-sync.mjs +6 -6
  1125. package/scripts/check-translation-completeness.cjs +1 -0
  1126. package/scripts/dev-extra-backend.mjs +28 -9
  1127. package/scripts/dev-safe.mjs +81 -8
  1128. package/scripts/dev-static.mjs +35 -37
  1129. package/scripts/dev-with-automation.mjs +83 -2
  1130. package/scripts/ingress.mjs +27 -1
  1131. package/scripts/static-server.mjs +107 -2
  1132. package/tools/canvas_ui_tool.py +19 -0
  1133. package/build/assets/active-backend-context-DKFSMbn3.js +0 -2
  1134. package/build/assets/agent-profiles-settings-D4cY5U2T.js +0 -1
  1135. package/build/assets/agent-server-conversation-service.api-DozDKIW_.js +0 -86
  1136. package/build/assets/agent-settings-351vXWc9.js +0 -2
  1137. package/build/assets/agent-settings-Ce7BDZ6I.js +0 -1
  1138. package/build/assets/alert-banner-Kyy1YWZU.js +0 -1
  1139. package/build/assets/api-key-entry-screen-INIxqAMs.js +0 -1
  1140. package/build/assets/automation-detail-DXDhUwPq.js +0 -3
  1141. package/build/assets/automation-git-sync-CzyS4cn1.js +0 -1
  1142. package/build/assets/automation-setup-route-FRr05f_g.js +0 -1
  1143. package/build/assets/automations-list-CERuQWE6.js +0 -1
  1144. package/build/assets/backend-form-modal-DK78-jn1.js +0 -1
  1145. package/build/assets/bot-C-0Wu7vQ.js +0 -1
  1146. package/build/assets/browser-11etCQ04.js +0 -7
  1147. package/build/assets/check-Cf2YoT90.js +0 -1
  1148. package/build/assets/chevron-down-C3OQ-ehx.js +0 -1
  1149. package/build/assets/chevron-right-CN1uUiJ1.js +0 -1
  1150. package/build/assets/circle-check-g5yeGMd-.js +0 -1
  1151. package/build/assets/command-menu-BZQ8S3Ke.js +0 -1
  1152. package/build/assets/commits-tab-DZtJgaDM.js +0 -1
  1153. package/build/assets/confirmation-modal-Bk4JRcCM.js +0 -1
  1154. package/build/assets/conversation-CchMzpGl.js +0 -1
  1155. package/build/assets/conversation-Dpzftz57.js +0 -93
  1156. package/build/assets/conversation-panel-CchMzpGl.js +0 -1
  1157. package/build/assets/conversation-state-store-a9d2fxRc.js +0 -1
  1158. package/build/assets/edit-automation-modal-D-urtv8n.js +0 -1
  1159. package/build/assets/empty-state-CvaVahVr.js +0 -1
  1160. package/build/assets/enum-filter-dropdown-DliX_Lo0.js +0 -1
  1161. package/build/assets/extensions-hub-CFduxBnk.js +0 -1
  1162. package/build/assets/extensions-navigation-BeC224yX.js +0 -1
  1163. package/build/assets/external-link-DFpYunfj.js +0 -1
  1164. package/build/assets/files-tab-CL3J9b3p.js +0 -1
  1165. package/build/assets/format-model-name-BNPKVsIq.js +0 -1
  1166. package/build/assets/get-git-path-DlAJa_i6.js +0 -1
  1167. package/build/assets/git-repo-dropdown-DGMEhz7d.js +0 -1
  1168. package/build/assets/home-BCGlNJCA.js +0 -1
  1169. package/build/assets/home-DB1pRqTC.js +0 -1
  1170. package/build/assets/home-automation-run-tooltip-CooFQRIG.js +0 -1
  1171. package/build/assets/home-store-Dl1dPE58.js +0 -1
  1172. package/build/assets/index-home-DB1pRqTC.js +0 -1
  1173. package/build/assets/info-uYSVzX86.js +0 -1
  1174. package/build/assets/install-server-modal-BbJdhzet.js +0 -1
  1175. package/build/assets/kebab-menu-item-content-BYokkRHi.js +0 -1
  1176. package/build/assets/list-filter-OFkpRrmi.js +0 -1
  1177. package/build/assets/llm-not-configured-banner-BqqYnJ1t.js +0 -27
  1178. package/build/assets/llm-settings-CnDbbmVW.js +0 -1
  1179. package/build/assets/llm-settings-S8Yjg__1.js +0 -1
  1180. package/build/assets/llm-subscription-service-BpPfzW4x.js +0 -1
  1181. package/build/assets/loader-circle-nvrwthvj.js +0 -1
  1182. package/build/assets/manage-backends-modal-BCUPswyl.js +0 -1
  1183. package/build/assets/manifest-58597dde.js +0 -1
  1184. package/build/assets/markdown-renderer-Cb2sBWtV.js +0 -8
  1185. package/build/assets/mcp-page-Bm4K2NQq.js +0 -9
  1186. package/build/assets/messages-BTN5thLe.js +0 -40
  1187. package/build/assets/onboarding-Cx_kaiD1.js +0 -1
  1188. package/build/assets/onboarding-modal-vMwn7ihv.js +0 -1
  1189. package/build/assets/organization-service.api-C8XpQ0pM.js +0 -1
  1190. package/build/assets/pencil-Clh1Wjvh.js +0 -1
  1191. package/build/assets/planner-tab-DQvDU3ap.js +0 -1
  1192. package/build/assets/plug-BuQhHSSG.js +0 -1
  1193. package/build/assets/plugins-management-service-BjYeF0b0.js +0 -1
  1194. package/build/assets/plus-CEKne-Yp.js +0 -1
  1195. package/build/assets/profiles-service.api-QB-T_u-g.js +0 -1
  1196. package/build/assets/providers-FfSZ5TXP.js +0 -1
  1197. package/build/assets/proxy-tcFL2yTQ.js +0 -1
  1198. package/build/assets/recommended-automations-launcher-Bw8uC4V6.js +0 -1
  1199. package/build/assets/refresh-cw-Bh69DyYO.js +0 -1
  1200. package/build/assets/root-BLeQfBEq.css +0 -1
  1201. package/build/assets/root-ZKKYPR1Z.js +0 -2
  1202. package/build/assets/root-layout-luJoE9IG.js +0 -2
  1203. package/build/assets/schema-field-Csw5fhtN.js +0 -1
  1204. package/build/assets/scroll-fade-state-Dw-i4SGq.js +0 -1
  1205. package/build/assets/sdk-section-page-HTffvm7Q.js +0 -1
  1206. package/build/assets/secret-form-HTUbYw5o.js +0 -1
  1207. package/build/assets/secrets-service-BTor9uAU.js +0 -1
  1208. package/build/assets/settings-BlmrkwRi.js +0 -1
  1209. package/build/assets/settings-DcNPxz0v.js +0 -1
  1210. package/build/assets/settings-index-CKq0b0Vg.js +0 -1
  1211. package/build/assets/settings-service.api-B2agVfjL.js +0 -1
  1212. package/build/assets/shared-conversation-B55pCyBG.js +0 -1
  1213. package/build/assets/shield-check-CFDViW82.js +0 -1
  1214. package/build/assets/sidebar-mobile-menu-toggle-BOtyJHwE.js +0 -1
  1215. package/build/assets/sidebar-nav-link-CPzhyHQg.js +0 -1
  1216. package/build/assets/sidebar-store-DtpU7zaI.js +0 -1
  1217. package/build/assets/skill-filter-CWZ5kjcg.js +0 -2
  1218. package/build/assets/skill-scope-CV1dkAbW.js +0 -1
  1219. package/build/assets/skills-plugins-DGHULTq6.js +0 -1
  1220. package/build/assets/skills-settings-CM1lFv3e.js +0 -1
  1221. package/build/assets/styled-tooltip-Dl06rkpY.js +0 -1
  1222. package/build/assets/toggle-switch-Fd_QGz0r.js +0 -1
  1223. package/build/assets/trash-2-Dl8_RGz0.js +0 -1
  1224. package/build/assets/trash-_RMt7Ayc.js +0 -1
  1225. package/build/assets/triangle-alert-SM-q4NU_.js +0 -1
  1226. package/build/assets/usage-tab-BcfWV6db.js +0 -1
  1227. package/build/assets/use-acp-credential-form-BsSW5NHG.js +0 -1
  1228. package/build/assets/use-activate-agent-profile-B2NS8nA5.js +0 -1
  1229. package/build/assets/use-agent-profiles-DWBPC0Si.js +0 -1
  1230. package/build/assets/use-config-Bft35DOK.js +0 -1
  1231. package/build/assets/use-create-conversation-DwN5vv2f.js +0 -1
  1232. package/build/assets/use-create-secret-BSxJMxHp.js +0 -1
  1233. package/build/assets/use-has-permission-B3C8rQmR.js +0 -1
  1234. package/build/assets/use-is-authed-CFGkY3-Z.js +0 -1
  1235. package/build/assets/use-llm-configured-BI6obxsc.js +0 -1
  1236. package/build/assets/use-llm-profiles-djGAhTWL.js +0 -1
  1237. package/build/assets/use-manifest-capabilities-3z0-1G59.js +0 -1
  1238. package/build/assets/use-onboarding-completion-BXpyUSsJ.js +0 -1
  1239. package/build/assets/use-plugins-marketplace-DI4_pK56.js +0 -1
  1240. package/build/assets/use-save-agent-profile-DdBSTdpN.js +0 -1
  1241. package/build/assets/use-settings-C5VhGpqd.js +0 -1
  1242. package/build/assets/use-settings-nav-items-DHaUu1Dg.js +0 -16
  1243. package/build/assets/use-tracking-D5t9otIF.js +0 -3
  1244. package/build/assets/utils-DaCIEEQP.js +0 -1
  1245. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kepq2zme-ZixYGy6E.js +0 -186
  1246. package/build/assets/vendor~index-home~home~conversation-panel~conversation~alert-banner-B3qC_oj8.js +0 -1
  1247. package/build/assets/vendor~root-layout-BlUaZ18v.js +0 -1
  1248. package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation~mcp~automations-list~aut~ibfbmr9f-B2OLKL7N.js +0 -1
  1249. package/build/assets/vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iw7io29p-Dz-MAyNb.js +0 -1
  1250. package/build/assets/x-DkgltWa0.js +0 -1
  1251. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs +0 -2
  1252. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs.map +0 -1
  1253. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.d.ts +0 -35
  1254. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js +0 -247
  1255. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js.map +0 -1
  1256. package/dist/components/features/tips/random-tip.d.ts +0 -2
  1257. package/dist/hooks/query/use-skills.cjs +0 -2
  1258. package/dist/hooks/query/use-skills.cjs.map +0 -1
  1259. package/dist/hooks/query/use-skills.d.ts +0 -7
  1260. package/dist/hooks/query/use-skills.js +0 -13
  1261. package/dist/hooks/query/use-skills.js.map +0 -1
  1262. package/dist/hooks/use-has-permission.cjs +0 -2
  1263. package/dist/hooks/use-has-permission.cjs.map +0 -1
  1264. package/dist/hooks/use-has-permission.d.ts +0 -1
  1265. package/dist/hooks/use-has-permission.js +0 -8
  1266. package/dist/hooks/use-has-permission.js.map +0 -1
  1267. package/dist/node_modules/lucide-react/dist/esm/icons/eye.cjs +0 -2
  1268. package/dist/node_modules/lucide-react/dist/esm/icons/eye.cjs.map +0 -1
  1269. package/dist/node_modules/lucide-react/dist/esm/icons/eye.js +0 -14
  1270. package/dist/node_modules/lucide-react/dist/esm/icons/eye.js.map +0 -1
  1271. package/dist/utils/tips.d.ts +0 -7
  1272. package/dist/utils/workspace-relative-path.cjs +0 -2
  1273. package/dist/utils/workspace-relative-path.cjs.map +0 -1
  1274. package/dist/utils/workspace-relative-path.d.ts +0 -10
  1275. package/dist/utils/workspace-relative-path.js +0 -22
  1276. package/dist/utils/workspace-relative-path.js.map +0 -1
  1277. /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ox0jfsa0-cHFhpUBq.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~i58xkef6-cHFhpUBq.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"automation-insights.js","names":[],"sources":["../../src/manifests/automation-insights.ts"],"sourcesContent":["/**\n * The dashboard's automation knowledge, and the only module beside\n * `automation-setup.ts` that has any.\n *\n * The interface manifest's dashboard surface names metrics, filter predicates,\n * and sort comparators from closed sets; the one implementation of each name\n * is here, over the automation service's list and runs responses, so the rest\n * of `src/manifests/` stays about composition rather than about automations.\n *\n * Summaries are derived from the newest page of runs (the sample the runs\n * hook fetches); `total` alone is the response's lifetime count. Success rate\n * and durations consider only COMPLETED and FAILED runs — a cancelled or\n * skipped run says nothing about either.\n */\n\nimport {\n AutomationRunStatus,\n type Automation,\n type AutomationRun,\n type AutomationRunsResponse,\n} from \"#/types/automation\";\nimport type {\n DashboardSortValue,\n DashboardStatusValue,\n DashboardTriggerValue,\n InterfaceListInsights,\n OverviewMetric,\n} from \"./types\";\n\nexport type AutomationHealth =\n | \"healthy\"\n | \"failing\"\n | \"running\"\n | \"disabled\"\n | \"never-run\"\n | \"unknown\";\n\n/** Which caption of the manifest's `insights.health` block names each state. */\nexport const HEALTH_LABEL_KEYS: Record<\n AutomationHealth,\n keyof InterfaceListInsights[\"health\"]\n> = {\n healthy: \"healthy\",\n failing: \"failing\",\n running: \"running\",\n disabled: \"disabled\",\n \"never-run\": \"neverRun\",\n unknown: \"checking\",\n};\n\nexport interface AutomationRunSummary {\n /** Lifetime run count, from the response — not the sample's length. */\n total: number;\n latestRun: AutomationRun | null;\n /** Newest-first sample used by the list sparkline (same page as the summary). */\n recentRuns: AutomationRun[];\n /** COMPLETED over COMPLETED+FAILED in the sample. Null with no terminal runs. */\n recentSuccessRate: number | null;\n /** Mean completed_at − started_at over the sample's terminal runs. */\n averageDurationMs: number | null;\n}\n\n/** One automation's summary alongside its query state. */\nexport interface RunSummaryState {\n summary: AutomationRunSummary | null;\n isLoading: boolean;\n isError: boolean;\n}\n\ntype Summaries = ReadonlyMap<string, RunSummaryState>;\n\nconst TERMINAL_STATUSES = new Set<AutomationRunStatus>([\n AutomationRunStatus.COMPLETED,\n AutomationRunStatus.FAILED,\n]);\n\nexport function summarizeAutomationRuns(\n response: AutomationRunsResponse,\n): AutomationRunSummary {\n const terminal = response.runs.filter((run) =>\n TERMINAL_STATUSES.has(run.status),\n );\n const completed = terminal.filter(\n (run) => run.status === AutomationRunStatus.COMPLETED,\n ).length;\n\n const durations = terminal.flatMap((run) => {\n if (!run.completed_at) return [];\n const ms =\n new Date(run.completed_at).getTime() - new Date(run.started_at).getTime();\n return Number.isFinite(ms) && ms >= 0 ? [ms] : [];\n });\n\n return {\n total: response.total,\n latestRun: response.runs[0] ?? null,\n recentRuns: response.runs,\n recentSuccessRate:\n terminal.length === 0 ? null : completed / terminal.length,\n averageDurationMs:\n durations.length === 0\n ? null\n : durations.reduce((sum, ms) => sum + ms, 0) / durations.length,\n };\n}\n\n/**\n * A disabled automation is disabled no matter its history; while the summary\n * is unsettled nothing is claimed; after that the latest run speaks.\n */\nexport function deriveAutomationHealth(\n automation: Automation,\n state: RunSummaryState | undefined,\n): AutomationHealth {\n if (!automation.enabled) return \"disabled\";\n if (!state || state.isError || state.isLoading || !state.summary) {\n return \"unknown\";\n }\n const latest = state.summary.latestRun;\n if (!latest) return \"never-run\";\n if (latest.status === AutomationRunStatus.FAILED) return \"failing\";\n if (\n latest.status === AutomationRunStatus.PENDING ||\n latest.status === AutomationRunStatus.RUNNING\n ) {\n return \"running\";\n }\n return \"healthy\";\n}\n\n/** \"—\" unknown, seconds under a minute, minutes under an hour, else \"1.5h\". */\nexport function formatCompactDuration(ms: number | null): string {\n if (ms === null || !Number.isFinite(ms)) return \"—\";\n if (ms < 60_000) return `${Math.max(1, Math.round(ms / 1000))}s`;\n if (ms < 3_600_000) return `${Math.max(1, Math.round(ms / 60_000))}m`;\n return `${(ms / 3_600_000).toFixed(1)}h`;\n}\n\n/** The list page's search predicate: name, prompt, repository, or model. */\nexport function matchesAutomationSearch(\n automation: Automation,\n query: string,\n): boolean {\n const q = query.toLowerCase();\n if (!q) return true;\n return (\n automation.name.toLowerCase().includes(q) ||\n (automation.prompt ?? \"\").toLowerCase().includes(q) ||\n automation.repository?.toLowerCase().includes(q) === true ||\n automation.model?.toLowerCase().includes(q) === true\n );\n}\n\nconst STATUS_PREDICATES: Record<\n Exclude<DashboardStatusValue, \"all\">,\n (automation: Automation, health: AutomationHealth) => boolean\n> = {\n active: (automation) => automation.enabled,\n failing: (_automation, health) => health === \"failing\",\n disabled: (automation) => !automation.enabled,\n};\n\nconst TRIGGER_PREDICATES: Record<\n Exclude<DashboardTriggerValue, \"all\">,\n (automation: Automation) => boolean\n> = {\n // The backend emits several scheduled-trigger aliases, so \"event\" is the\n // one exact kind and everything else is a schedule.\n event: (automation) => automation.trigger.type === \"event\",\n schedule: (automation) => automation.trigger.type !== \"event\",\n};\n\nfunction runCount(automation: Automation, byId: Summaries): number {\n return byId.get(automation.id)?.summary?.total ?? 0;\n}\n\nfunction lastRunTime(automation: Automation, byId: Summaries): number {\n const startedAt =\n byId.get(automation.id)?.summary?.latestRun?.started_at ??\n automation.last_triggered_at;\n if (!startedAt) return 0;\n const time = new Date(startedAt).getTime();\n return Number.isFinite(time) ? time : 0;\n}\n\nconst SORT_COMPARATORS: Record<\n DashboardSortValue,\n (a: Automation, b: Automation, byId: Summaries) => number\n> = {\n \"last-run\": (a, b, byId) => lastRunTime(b, byId) - lastRunTime(a, byId),\n runs: (a, b, byId) => runCount(b, byId) - runCount(a, byId),\n name: (a, b) => a.name.localeCompare(b.name),\n};\n\nexport interface DashboardViewState {\n search: string;\n status: DashboardStatusValue;\n trigger: DashboardTriggerValue;\n sort: DashboardSortValue;\n}\n\n/** The automations the dashboard shows, filtered and ordered. */\nexport function applyDashboardView(\n automations: readonly Automation[],\n view: DashboardViewState,\n byId: Summaries,\n): Automation[] {\n return automations\n .filter((automation) => {\n if (!matchesAutomationSearch(automation, view.search)) return false;\n if (\n view.status !== \"all\" &&\n !STATUS_PREDICATES[view.status](\n automation,\n deriveAutomationHealth(automation, byId.get(automation.id)),\n )\n ) {\n return false;\n }\n if (\n view.trigger !== \"all\" &&\n !TRIGGER_PREDICATES[view.trigger](automation)\n ) {\n return false;\n }\n return true;\n })\n .sort((a, b) => SORT_COMPARATORS[view.sort](a, b, byId));\n}\n\nexport interface OverviewTileValue {\n /** The formatted value the tile displays. */\n display: string;\n /** True when the metric's value is zero, which swaps in `zeroDetail`. */\n isZero: boolean;\n /** Values the tile's copy may substitute via `{{name}}`. */\n placeholderValues: Record<string, string | number>;\n}\n\nfunction loadedSummaries(\n automations: readonly Automation[],\n byId: Summaries,\n): AutomationRunSummary[] {\n return automations.flatMap((automation) => {\n const state = byId.get(automation.id);\n return state?.summary && !state.isLoading && !state.isError\n ? [state.summary]\n : [];\n });\n}\n\nconst TILE_COMPUTATIONS: Record<\n OverviewMetric,\n (automations: readonly Automation[], byId: Summaries) => OverviewTileValue\n> = {\n automations: (automations) => ({\n display: automations.length.toLocaleString(),\n isZero: automations.length === 0,\n placeholderValues: {\n active: automations.filter((automation) => automation.enabled).length,\n },\n }),\n \"needs-attention\": (automations, byId) => {\n const failing = automations.filter(\n (automation) =>\n deriveAutomationHealth(automation, byId.get(automation.id)) ===\n \"failing\",\n ).length;\n return {\n display: failing.toLocaleString(),\n isZero: failing === 0,\n placeholderValues: {},\n };\n },\n \"total-runs\": (automations, byId) => {\n const loaded = loadedSummaries(automations, byId);\n if (loaded.length === 0 && automations.length > 0) {\n return { display: \"—\", isZero: false, placeholderValues: {} };\n }\n const total = loaded.reduce((sum, summary) => sum + summary.total, 0);\n return {\n display: total.toLocaleString(),\n isZero: total === 0,\n placeholderValues: {},\n };\n },\n \"average-duration\": (automations, byId) => {\n const durations = loadedSummaries(automations, byId).flatMap((summary) =>\n summary.averageDurationMs === null ? [] : [summary.averageDurationMs],\n );\n const mean =\n durations.length === 0\n ? null\n : durations.reduce((sum, ms) => sum + ms, 0) / durations.length;\n return {\n display: formatCompactDuration(mean),\n isZero: false,\n placeholderValues: {},\n };\n },\n};\n\n/** The value behind one manifest-declared tile. */\nexport function computeOverviewTile(\n metric: OverviewMetric,\n automations: readonly Automation[],\n byId: Summaries,\n): OverviewTileValue {\n return TILE_COMPUTATIONS[metric](automations, byId);\n}\n"],"mappings":";AAuE0B,IAAI,IAAyB,CACrD,EAAoB,WACpB,EAAoB,MACtB,CAAC;AAyDD,SAAgB,EAAsB,GAA2B;CAI/D,OAHI,MAAO,QAAQ,CAAC,OAAO,SAAS,CAAE,IAAU,MAC5C,IAAK,MAAe,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,IAAK,GAAI,CAAC,EAAE,KAC1D,IAAK,OAAkB,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,IAAK,GAAM,CAAC,EAAE,KAC5D,IAAI,IAAK,MAAW,QAAQ,CAAC,EAAE;AACxC"}
1
+ {"version":3,"file":"automation-insights.js","names":[],"sources":["../../src/manifests/automation-insights.ts"],"sourcesContent":["/**\n * The dashboard's automation knowledge, and the only module beside\n * `automation-setup.ts` that has any.\n *\n * The interface manifest's dashboard surface names metrics, filter predicates,\n * and sort comparators from closed sets; the one implementation of each name\n * is here, over the automation service's list and runs responses, so the rest\n * of `src/manifests/` stays about composition rather than about automations.\n *\n * Summaries are derived from the newest page of runs (the sample the runs\n * hook fetches); `total` alone is the response's lifetime count. Success rate\n * and durations consider only terminal lifecycle runs; the success numerator\n * uses task-aware display status so completed-but-blocked work is not counted\n * as successful.\n */\n\nimport {\n AutomationRunStatus,\n type Automation,\n type AutomationRun,\n type AutomationRunsResponse,\n} from \"#/types/automation\";\nimport { getAutomationRunDisplay } from \"#/utils/automation-run-display\";\nimport type {\n DashboardSortValue,\n DashboardStatusValue,\n DashboardTriggerValue,\n InterfaceListInsights,\n OverviewMetric,\n} from \"./types\";\n\nexport type AutomationHealth =\n | \"healthy\"\n | \"failing\"\n | \"running\"\n | \"disabled\"\n | \"never-run\"\n | \"unknown\";\n\n/** Which caption of the manifest's `insights.health` block names each state. */\nexport const HEALTH_LABEL_KEYS: Record<\n AutomationHealth,\n keyof InterfaceListInsights[\"health\"]\n> = {\n healthy: \"healthy\",\n failing: \"failing\",\n running: \"running\",\n disabled: \"disabled\",\n \"never-run\": \"neverRun\",\n unknown: \"checking\",\n};\n\nexport interface AutomationRunSummary {\n /** Lifetime run count, from the response — not the sample's length. */\n total: number;\n /**\n * Lifetime COMPLETED-run count. From the response's `status_counts` when\n * the service reports it; an older service leaves that out, so the sample\n * stands in exactly when it holds the whole history (`total` ≤ its length)\n * and the count is null — unknowable, never guessed — otherwise.\n */\n completedTotal: number | null;\n latestRun: AutomationRun | null;\n /** Newest-first sample used by the list sparkline (same page as the summary). */\n recentRuns: AutomationRun[];\n /** COMPLETED over COMPLETED+FAILED in the sample. Null with no terminal runs. */\n recentSuccessRate: number | null;\n /** Mean completed_at − started_at over the sample's terminal runs. */\n averageDurationMs: number | null;\n}\n\n/** One automation's summary alongside its query state. */\nexport interface RunSummaryState {\n summary: AutomationRunSummary | null;\n isLoading: boolean;\n isError: boolean;\n}\n\ntype Summaries = ReadonlyMap<string, RunSummaryState>;\n\nconst TERMINAL_STATUSES = new Set<AutomationRunStatus>([\n AutomationRunStatus.COMPLETED,\n AutomationRunStatus.FAILED,\n]);\n\nfunction isTaskSuccessful(run: AutomationRun): boolean {\n const { badgeStatus } = getAutomationRunDisplay(run);\n return (\n badgeStatus === AutomationRunStatus.COMPLETED || badgeStatus === \"success\"\n );\n}\n\nfunction isTaskFailing(run: AutomationRun): boolean {\n const { badgeStatus } = getAutomationRunDisplay(run);\n return (\n badgeStatus === AutomationRunStatus.FAILED ||\n badgeStatus === \"failed\" ||\n badgeStatus === \"blocked\"\n );\n}\n\nexport function summarizeAutomationRuns(\n response: AutomationRunsResponse,\n): AutomationRunSummary {\n const terminal = response.runs.filter((run) =>\n TERMINAL_STATUSES.has(run.status),\n );\n const completed = terminal.filter(isTaskSuccessful).length;\n\n let completedTotal: number | null;\n if (response.status_counts) {\n completedTotal = response.status_counts[AutomationRunStatus.COMPLETED] ?? 0;\n } else if (response.total <= response.runs.length) {\n completedTotal = completed;\n } else {\n completedTotal = null;\n }\n\n const durations = terminal.flatMap((run) => {\n if (!run.completed_at) return [];\n const ms =\n new Date(run.completed_at).getTime() - new Date(run.started_at).getTime();\n return Number.isFinite(ms) && ms >= 0 ? [ms] : [];\n });\n\n return {\n total: response.total,\n completedTotal,\n latestRun: response.runs[0] ?? null,\n recentRuns: response.runs,\n recentSuccessRate:\n terminal.length === 0 ? null : completed / terminal.length,\n averageDurationMs:\n durations.length === 0\n ? null\n : durations.reduce((sum, ms) => sum + ms, 0) / durations.length,\n };\n}\n\n/**\n * A disabled automation is disabled no matter its history; while the summary\n * is unsettled nothing is claimed; after that the latest run speaks.\n */\nexport function deriveAutomationHealth(\n automation: Automation,\n state: RunSummaryState | undefined,\n): AutomationHealth {\n if (!automation.enabled) return \"disabled\";\n if (!state || state.isError || state.isLoading || !state.summary) {\n return \"unknown\";\n }\n const latest = state.summary.latestRun;\n if (!latest) return \"never-run\";\n if (isTaskFailing(latest)) return \"failing\";\n if (\n latest.status === AutomationRunStatus.PENDING ||\n latest.status === AutomationRunStatus.RUNNING\n ) {\n return \"running\";\n }\n return isTaskSuccessful(latest) ? \"healthy\" : \"unknown\";\n}\n\n/** \"—\" unknown, seconds under a minute, minutes under an hour, else \"1.5h\". */\nexport function formatCompactDuration(ms: number | null): string {\n if (ms === null || !Number.isFinite(ms)) return \"—\";\n if (ms < 60_000) return `${Math.max(1, Math.round(ms / 1000))}s`;\n if (ms < 3_600_000) return `${Math.max(1, Math.round(ms / 60_000))}m`;\n return `${(ms / 3_600_000).toFixed(1)}h`;\n}\n\n/** The list page's search predicate: name, prompt, repository, or model. */\nexport function matchesAutomationSearch(\n automation: Automation,\n query: string,\n): boolean {\n const q = query.toLowerCase();\n if (!q) return true;\n return (\n automation.name.toLowerCase().includes(q) ||\n (automation.prompt ?? \"\").toLowerCase().includes(q) ||\n automation.repository?.toLowerCase().includes(q) === true ||\n automation.model?.toLowerCase().includes(q) === true\n );\n}\n\nconst STATUS_PREDICATES: Record<\n Exclude<DashboardStatusValue, \"all\">,\n (automation: Automation, health: AutomationHealth) => boolean\n> = {\n active: (automation) => automation.enabled,\n failing: (_automation, health) => health === \"failing\",\n disabled: (automation) => !automation.enabled,\n};\n\nconst TRIGGER_PREDICATES: Record<\n Exclude<DashboardTriggerValue, \"all\">,\n (automation: Automation) => boolean\n> = {\n // The backend emits several scheduled-trigger aliases, so \"event\" is the\n // one exact kind and everything else is a schedule.\n event: (automation) => automation.trigger.type === \"event\",\n schedule: (automation) => automation.trigger.type !== \"event\",\n};\n\nfunction runCount(automation: Automation, byId: Summaries): number {\n return byId.get(automation.id)?.summary?.total ?? 0;\n}\n\nfunction lastRunTime(automation: Automation, byId: Summaries): number {\n const startedAt =\n byId.get(automation.id)?.summary?.latestRun?.started_at ??\n automation.last_triggered_at;\n if (!startedAt) return 0;\n const time = new Date(startedAt).getTime();\n return Number.isFinite(time) ? time : 0;\n}\n\nconst SORT_COMPARATORS: Record<\n DashboardSortValue,\n (a: Automation, b: Automation, byId: Summaries) => number\n> = {\n \"last-run\": (a, b, byId) => lastRunTime(b, byId) - lastRunTime(a, byId),\n runs: (a, b, byId) => runCount(b, byId) - runCount(a, byId),\n name: (a, b) => a.name.localeCompare(b.name),\n};\n\nexport interface DashboardViewState {\n search: string;\n status: DashboardStatusValue;\n trigger: DashboardTriggerValue;\n sort: DashboardSortValue;\n}\n\n/** The automations the dashboard shows, filtered and ordered. */\nexport function applyDashboardView(\n automations: readonly Automation[],\n view: DashboardViewState,\n byId: Summaries,\n): Automation[] {\n return automations\n .filter((automation) => {\n if (!matchesAutomationSearch(automation, view.search)) return false;\n if (\n view.status !== \"all\" &&\n !STATUS_PREDICATES[view.status](\n automation,\n deriveAutomationHealth(automation, byId.get(automation.id)),\n )\n ) {\n return false;\n }\n if (\n view.trigger !== \"all\" &&\n !TRIGGER_PREDICATES[view.trigger](automation)\n ) {\n return false;\n }\n return true;\n })\n .sort((a, b) => SORT_COMPARATORS[view.sort](a, b, byId));\n}\n\nexport interface OverviewTileValue {\n /** The formatted value the tile displays. */\n display: string;\n /** True when the metric's value is zero, which swaps in `zeroDetail`. */\n isZero: boolean;\n /** Values the tile's copy may substitute via `{{name}}`. */\n placeholderValues: Record<string, string | number>;\n}\n\nfunction loadedSummaries(\n automations: readonly Automation[],\n byId: Summaries,\n): AutomationRunSummary[] {\n return automations.flatMap((automation) => {\n const state = byId.get(automation.id);\n return state?.summary && !state.isLoading && !state.isError\n ? [state.summary]\n : [];\n });\n}\n\nconst TILE_COMPUTATIONS: Record<\n OverviewMetric,\n (automations: readonly Automation[], byId: Summaries) => OverviewTileValue\n> = {\n automations: (automations) => ({\n display: automations.length.toLocaleString(),\n isZero: automations.length === 0,\n placeholderValues: {\n active: automations.filter((automation) => automation.enabled).length,\n },\n }),\n \"needs-attention\": (automations, byId) => {\n const failing = automations.filter(\n (automation) =>\n deriveAutomationHealth(automation, byId.get(automation.id)) ===\n \"failing\",\n ).length;\n return {\n display: failing.toLocaleString(),\n isZero: failing === 0,\n placeholderValues: {},\n };\n },\n \"total-runs\": (automations, byId) => {\n const loaded = loadedSummaries(automations, byId);\n if (loaded.length === 0 && automations.length > 0) {\n return { display: \"—\", isZero: false, placeholderValues: {} };\n }\n const total = loaded.reduce((sum, summary) => sum + summary.total, 0);\n return {\n display: total.toLocaleString(),\n isZero: total === 0,\n placeholderValues: {},\n };\n },\n \"average-duration\": (automations, byId) => {\n const durations = loadedSummaries(automations, byId).flatMap((summary) =>\n summary.averageDurationMs === null ? [] : [summary.averageDurationMs],\n );\n const mean =\n durations.length === 0\n ? null\n : durations.reduce((sum, ms) => sum + ms, 0) / durations.length;\n return {\n display: formatCompactDuration(mean),\n isZero: false,\n placeholderValues: {},\n };\n },\n};\n\n/** The value behind one manifest-declared tile. */\nexport function computeOverviewTile(\n metric: OverviewMetric,\n automations: readonly Automation[],\n byId: Summaries,\n): OverviewTileValue {\n return TILE_COMPUTATIONS[metric](automations, byId);\n}\n"],"mappings":";AAgF0B,IAAI,IAAyB,CACrD,EAAoB,WACpB,EAAoB,MACtB,CAAC;AAiFD,SAAgB,EAAsB,GAA2B;CAI/D,OAHI,MAAO,QAAQ,CAAC,OAAO,SAAS,CAAE,IAAU,MAC5C,IAAK,MAAe,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,IAAK,GAAI,CAAC,EAAE,KAC1D,IAAK,OAAkB,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,IAAK,GAAM,CAAC,EAAE,KAC5D,IAAI,IAAK,MAAW,QAAQ,CAAC,EAAE;AACxC"}
@@ -1,2 +1,2 @@
1
- require("../_virtual/_rolldown/runtime.cjs");const e=require("../node_modules/@openhands/extensions/automations/index.cjs"),t=require("./interface-validation.cjs"),n=require("./manifest-sources.cjs");var r={list:`/automations`,setup:`/automations/new/:automationId`,detail:`/automations/:automationId`,templates:`/automations/templates`};function i(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function a(){return new Set(e.default.flatMap(e=>i(e)&&typeof e.id==`string`?[e.id]:[]))}function o(e){if(e===void 0)return null;let n=t.validateInterfaceManifest(e,{catalogIds:a(),mountedRoutes:r});return n.valid?e:(console.warn(`Rejected the automation interface manifest:`,n.errors.join(`; `)),null)}var s=o(n.AUTOMATION_INTERFACE_CANDIDATE);function c(){return s!==null}function l(){if(!s)throw Error(`No automation interface manifest is admitted, so this surface should not have rendered.`);return s}function u(e,t){return e.replace(`:automationId`,encodeURIComponent(t))}function d(){return r.list}function f(e){return u(r.setup,e)}function p(e){return l().endpoints[e]??``}function m(e,t){return p(e).replace(`{id}`,encodeURIComponent(t))}function h(){let e=l();return{sidebarLabel:e.navigation.sidebar.label,commandMenuTitle:e.navigation.commandMenu.title,commandMenuDescription:e.navigation.commandMenu.description,commandMenuKeywords:e.navigation.commandMenu.keywords,listTitle:e.pages.list.title,listSubtitle:e.pages.list.subtitle,detailBackLabel:e.pages.detail.backLabel,editTitle:e.pages.edit.title}}function g(){return l().importExport}function _(){return l().docsUrl}function v(){return l().featuredAutomationIds}function y(){return l().responderIntegrationIds}exports.automationListPath=d,exports.automationSetupPath=f,exports.getAutomationEndpoint=p,exports.getAutomationIdEndpoint=m,exports.getAutomationsDocsUrl=_,exports.getFeaturedAutomationIds=v,exports.getImportExportSpec=g,exports.getInterfaceCopy=h,exports.getResponderIntegrationIds=y,exports.hasAutomationInterface=c;
1
+ require("../_virtual/_rolldown/runtime.cjs");const e=require("../node_modules/@openhands/extensions/automations/index.cjs"),t=require("./interface-validation.cjs"),n=require("./manifest-sources.cjs");var r={list:`/automations`,setup:`/automations/new/:automationId`,detail:`/automations/:automationId`,templates:`/automations/templates`};function i(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function a(){return new Set(e.default.flatMap(e=>i(e)&&typeof e.id==`string`?[e.id]:[]))}function o(e){if(e===void 0)return null;let n=t.validateInterfaceManifest(e,{catalogIds:a(),mountedRoutes:r});return n.valid?e:(console.warn(`Rejected the automation interface manifest:`,n.errors.join(`; `)),null)}var s=o(n.AUTOMATION_INTERFACE_CANDIDATE);function c(){return s!==null}function l(){if(!s)throw Error(`No automation interface manifest is admitted, so this surface should not have rendered.`);return s}function u(e,t){return e.replace(`:automationId`,encodeURIComponent(t))}function d(){return r.list}function f(e){return u(r.setup,e)}function p(e){return e===r.list||e.startsWith(`${r.list}/`)}function m(e){return l().endpoints[e]??``}function h(e,t){return m(e).replace(`{id}`,encodeURIComponent(t))}function g(){let e=l();return{sidebarLabel:e.navigation.sidebar.label,commandMenuTitle:e.navigation.commandMenu.title,commandMenuDescription:e.navigation.commandMenu.description,commandMenuKeywords:e.navigation.commandMenu.keywords,listTitle:e.pages.list.title,listSubtitle:e.pages.list.subtitle,detailBackLabel:e.pages.detail.backLabel,editTitle:e.pages.edit.title}}function _(){return l().importExport}function v(){return l().docsUrl}function y(){return l().featuredAutomationIds}function b(){return l().responderIntegrationIds}exports.automationListPath=d,exports.automationSetupPath=f,exports.getAutomationEndpoint=m,exports.getAutomationIdEndpoint=h,exports.getAutomationsDocsUrl=v,exports.getFeaturedAutomationIds=y,exports.getImportExportSpec=_,exports.getInterfaceCopy=g,exports.getResponderIntegrationIds=b,exports.hasAutomationInterface=c,exports.isAutomationsRoute=p;
2
2
  //# sourceMappingURL=automation-interface.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"automation-interface.cjs","names":[],"sources":["../../src/manifests/automation-interface.ts"],"sourcesContent":["/**\n * The automation interface seam.\n *\n * Every automation-specific datum the host's surfaces render — navigation and\n * page copy, the settable attributes, the import/export envelope, endpoint\n * paths, the featured and responder id lists, the sub-page surface — is served\n * from the interface manifest the pinned `@openhands/extensions` publishes.\n * The host holds none of it. When the package publishes no manifest, or the\n * published one fails admission, there is nothing to serve: the nav entries do\n * not render and the routes 404. `hasAutomationInterface()` is that gate, and\n * every accessor that needs the manifest is reachable only from behind it.\n *\n * Routes are the exception, and they are not a definition: `src/routes.ts`\n * mounts them, so the host states them here and admission checks a manifest's\n * routes against them. That check is what keeps a published deep link\n * resolving to the page it named.\n */\n\nimport { AUTOMATION_CATALOG } from \"@openhands/extensions/automations\";\nimport { validateInterfaceManifest } from \"./interface-validation\";\nimport { AUTOMATION_INTERFACE_CANDIDATE } from \"./manifest-sources\";\nimport type {\n AutomationAttributeName,\n InterfaceDashboardFilter,\n InterfaceDashboardSort,\n InterfaceEndpointName,\n InterfaceIconSlug,\n InterfaceImportExport,\n InterfaceListInsights,\n InterfaceManifest,\n InterfaceOverview,\n InterfaceRoutes,\n InterfaceSubPageId,\n InterfaceTemplatesPage,\n} from \"./types\";\n\n/** The routes this host has registrations for, in `src/routes.ts`. */\nconst MOUNTED_ROUTES = {\n list: \"/automations\",\n setup: \"/automations/new/:automationId\",\n detail: \"/automations/:automationId\",\n templates: \"/automations/templates\",\n} satisfies InterfaceRoutes;\n\n/**\n * File-format section for portable `.automation.json` export/import. A docs\n * pointer for the host's import picker, not interface data — the manifest's\n * `importExport` block has no docs field.\n */\nexport const AUTOMATION_FILE_FORMAT_DOCS_URL =\n \"https://docs.openhands.dev/openhands/usage/agent-canvas/managing-automations#exported-file-format\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getCatalogIds(): ReadonlySet<string> {\n return new Set(\n (AUTOMATION_CATALOG as readonly unknown[]).flatMap((entry) =>\n isRecord(entry) && typeof entry.id === \"string\" ? [entry.id] : [],\n ),\n );\n}\n\nfunction admitInterfaceManifest(candidate: unknown): InterfaceManifest | null {\n if (candidate === undefined) return null;\n\n const result = validateInterfaceManifest(candidate, {\n catalogIds: getCatalogIds(),\n mountedRoutes: MOUNTED_ROUTES,\n });\n if (!result.valid) {\n // Mirrors the setup registry: a rejected manifest is skipped loudly, and\n // the surfaces it would have described stay unrendered.\n console.warn(\n \"Rejected the automation interface manifest:\",\n result.errors.join(\"; \"),\n );\n return null;\n }\n return candidate as InterfaceManifest;\n}\n\nconst ADMITTED = admitInterfaceManifest(AUTOMATION_INTERFACE_CANDIDATE);\n\n/**\n * Whether this deployment has an automation interface at all. The nav entries\n * and the route loaders ask before rendering; nothing else has to.\n */\nexport function hasAutomationInterface(): boolean {\n return ADMITTED !== null;\n}\n\n/**\n * The admitted manifest, for the accessors that cannot answer without one.\n * Reaching one of those without a manifest means a surface rendered past its\n * gate — a wiring mistake, not a state to render.\n */\nfunction requireInterface(): InterfaceManifest {\n if (!ADMITTED) {\n throw new Error(\n \"No automation interface manifest is admitted, so this surface should not have rendered.\",\n );\n }\n return ADMITTED;\n}\n\nfunction substituteRouteParam(pattern: string, id: string): string {\n return pattern.replace(\":automationId\", encodeURIComponent(id));\n}\n\nexport function automationListPath(): string {\n return MOUNTED_ROUTES.list;\n}\n\nexport function automationSetupPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.setup, id);\n}\n\nexport function automationDetailPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.detail, id);\n}\n\nexport function automationTemplatesPath(): string {\n return MOUNTED_ROUTES.templates;\n}\n\n/**\n * A declared endpoint path. Empty for one the manifest may omit - the two a\n * bundle needs were added after the block shipped - so a caller that needs one\n * says so rather than reading a host-held default that does not exist.\n */\nexport function getAutomationEndpoint(name: InterfaceEndpointName): string {\n return requireInterface().endpoints[name] ?? \"\";\n}\n\n/** An id-parameterized endpoint with `{id}` substituted, encoded. */\nexport function getAutomationIdEndpoint(\n name: \"detail\" | \"dispatch\" | \"runs\" | \"tarball\",\n id: string,\n): string {\n return getAutomationEndpoint(name).replace(\"{id}\", encodeURIComponent(id));\n}\n\nexport interface InterfaceCopy {\n sidebarLabel: string;\n commandMenuTitle: string;\n commandMenuDescription: string;\n commandMenuKeywords: string;\n listTitle: string;\n listSubtitle: string;\n detailBackLabel: string;\n editTitle: string;\n}\n\nexport function getInterfaceCopy(): InterfaceCopy {\n const manifest = requireInterface();\n return {\n sidebarLabel: manifest.navigation.sidebar.label,\n commandMenuTitle: manifest.navigation.commandMenu.title,\n commandMenuDescription: manifest.navigation.commandMenu.description,\n commandMenuKeywords: manifest.navigation.commandMenu.keywords,\n listTitle: manifest.pages.list.title,\n listSubtitle: manifest.pages.list.subtitle,\n detailBackLabel: manifest.pages.detail.backLabel,\n editTitle: manifest.pages.edit.title,\n };\n}\n\nexport interface AttributeSpec {\n present: boolean;\n label: string;\n /** Null when the manifest states no help text for the attribute. */\n help: string | null;\n required: boolean;\n min: number | null;\n max: number | null;\n}\n\n/** An attribute the manifest does not declare is not offered at all. */\nconst ABSENT_ATTRIBUTE: AttributeSpec = {\n present: false,\n label: \"\",\n help: null,\n required: false,\n min: null,\n max: null,\n};\n\nexport function getAttributeSpec(name: AutomationAttributeName): AttributeSpec {\n const attribute = requireInterface().attributes[name];\n if (!attribute) return ABSENT_ATTRIBUTE;\n return {\n present: true,\n label: attribute.label,\n help: attribute.help ?? null,\n required: attribute.required,\n min: attribute.constraints?.min ?? null,\n max: attribute.constraints?.max ?? null,\n };\n}\n\nexport function getImportExportSpec(): InterfaceImportExport {\n return requireInterface().importExport;\n}\n\nexport function getAutomationsDocsUrl(): string {\n return requireInterface().docsUrl;\n}\n\nexport function getFeaturedAutomationIds(): readonly string[] {\n return requireInterface().featuredAutomationIds;\n}\n\nexport function getResponderIntegrationIds(): readonly string[] {\n return requireInterface().responderIntegrationIds;\n}\n\nexport interface SubPageNavSpec {\n page: InterfaceSubPageId;\n /** The page's route, resolved through the host's route table. */\n to: string;\n label: string;\n icon: InterfaceIconSlug;\n}\n\n/**\n * The sub-page navigation, or null when the manifest does not declare the\n * sub-page surface.\n */\nexport function getSubPagesSpec(): SubPageNavSpec[] | null {\n const subPages = ADMITTED?.navigation.subPages;\n if (!subPages) return null;\n return subPages.map((item) => ({\n page: item.page,\n to:\n item.page === \"templates\"\n ? automationTemplatesPath()\n : automationListPath(),\n label: item.label,\n icon: item.icon,\n }));\n}\n\nexport interface DashboardSpec {\n overview: InterfaceOverview;\n filters: InterfaceDashboardFilter[];\n sort: InterfaceDashboardSort;\n insights: InterfaceListInsights;\n}\n\n/**\n * The list page's dashboard composition, or null when the manifest does not\n * declare it. Admission accepts the sub-page surface whole or not at all, so\n * these four are present together.\n */\nexport function getDashboardSpec(): DashboardSpec | null {\n const list = ADMITTED?.pages.list;\n if (!list?.overview || !list.filters || !list.sort || !list.insights) {\n return null;\n }\n return {\n overview: list.overview,\n filters: list.filters,\n sort: list.sort,\n insights: list.insights,\n };\n}\n\n/** The templates page identity, or null when the manifest does not declare it. */\nexport function getTemplatesPageSpec(): InterfaceTemplatesPage | null {\n return ADMITTED?.pages.templates ?? null;\n}\n"],"mappings":"wMAqCA,IAAM,EAAiB,CACrB,KAAM,eACN,MAAO,iCACP,OAAQ,6BACR,UAAW,wBACb,EAUA,SAAS,EAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,CAAK,CAC5E,CAEA,SAAS,GAAqC,CAC5C,OAAO,IAAI,IACR,EAAA,QAA0C,QAAS,GAClD,EAAS,CAAK,GAAK,OAAO,EAAM,IAAO,SAAW,CAAC,EAAM,EAAE,EAAI,CAAC,CAClE,CACF,CACF,CAEA,SAAS,EAAuB,EAA8C,CAC5E,GAAI,IAAc,IAAA,GAAW,OAAO,KAEpC,IAAM,EAAS,EAAA,0BAA0B,EAAW,CAClD,WAAY,EAAc,EAC1B,cAAe,CACjB,CAAC,EAUD,OATK,EAAO,MASL,GANL,QAAQ,KACN,8CACA,EAAO,OAAO,KAAK,IAAI,CACzB,EACO,KAGX,CAEA,IAAM,EAAW,EAAuB,EAAA,8BAA8B,EAMtE,SAAgB,GAAkC,CAChD,OAAO,IAAa,IACtB,CAOA,SAAS,GAAsC,CAC7C,GAAI,CAAC,EACH,MAAU,MACR,yFACF,EAEF,OAAO,CACT,CAEA,SAAS,EAAqB,EAAiB,EAAoB,CACjE,OAAO,EAAQ,QAAQ,gBAAiB,mBAAmB,CAAE,CAAC,CAChE,CAEA,SAAgB,GAA6B,CAC3C,OAAO,EAAe,IACxB,CAEA,SAAgB,EAAoB,EAAoB,CACtD,OAAO,EAAqB,EAAe,MAAO,CAAE,CACtD,CAeA,SAAgB,EAAsB,EAAqC,CACzE,OAAO,EAAiB,EAAE,UAAU,IAAS,EAC/C,CAGA,SAAgB,EACd,EACA,EACQ,CACR,OAAO,EAAsB,CAAI,EAAE,QAAQ,OAAQ,mBAAmB,CAAE,CAAC,CAC3E,CAaA,SAAgB,GAAkC,CAChD,IAAM,EAAW,EAAiB,EAClC,MAAO,CACL,aAAc,EAAS,WAAW,QAAQ,MAC1C,iBAAkB,EAAS,WAAW,YAAY,MAClD,uBAAwB,EAAS,WAAW,YAAY,YACxD,oBAAqB,EAAS,WAAW,YAAY,SACrD,UAAW,EAAS,MAAM,KAAK,MAC/B,aAAc,EAAS,MAAM,KAAK,SAClC,gBAAiB,EAAS,MAAM,OAAO,UACvC,UAAW,EAAS,MAAM,KAAK,KACjC,CACF,CAmCA,SAAgB,GAA6C,CAC3D,OAAO,EAAiB,EAAE,YAC5B,CAEA,SAAgB,GAAgC,CAC9C,OAAO,EAAiB,EAAE,OAC5B,CAEA,SAAgB,GAA8C,CAC5D,OAAO,EAAiB,EAAE,qBAC5B,CAEA,SAAgB,GAAgD,CAC9D,OAAO,EAAiB,EAAE,uBAC5B"}
1
+ {"version":3,"file":"automation-interface.cjs","names":[],"sources":["../../src/manifests/automation-interface.ts"],"sourcesContent":["/**\n * The automation interface seam.\n *\n * Every automation-specific datum the host's surfaces render — navigation and\n * page copy, the settable attributes, the import/export envelope, endpoint\n * paths, the featured and responder id lists, the sub-page surface — is served\n * from the interface manifest the pinned `@openhands/extensions` publishes.\n * The host holds none of it. When the package publishes no manifest, or the\n * published one fails admission, there is nothing to serve: the nav entries do\n * not render and the routes 404. `hasAutomationInterface()` is that gate, and\n * every accessor that needs the manifest is reachable only from behind it.\n *\n * Routes are the exception, and they are not a definition: `src/routes.ts`\n * mounts them, so the host states them here and admission checks a manifest's\n * routes against them. That check is what keeps a published deep link\n * resolving to the page it named.\n */\n\nimport { AUTOMATION_CATALOG } from \"@openhands/extensions/automations\";\nimport { validateInterfaceManifest } from \"./interface-validation\";\nimport { AUTOMATION_INTERFACE_CANDIDATE } from \"./manifest-sources\";\nimport type {\n AutomationAttributeName,\n InterfaceDashboardFilter,\n InterfaceDashboardSort,\n InterfaceEndpointName,\n InterfaceIconSlug,\n InterfaceImportExport,\n InterfaceListInsights,\n InterfaceManifest,\n InterfaceOverview,\n InterfaceRoutes,\n InterfaceSubPageId,\n InterfaceTemplatesPage,\n} from \"./types\";\n\n/** The routes this host has registrations for, in `src/routes.ts`. */\nconst MOUNTED_ROUTES = {\n list: \"/automations\",\n setup: \"/automations/new/:automationId\",\n detail: \"/automations/:automationId\",\n templates: \"/automations/templates\",\n} satisfies InterfaceRoutes;\n\n/**\n * File-format section for portable `.automation.json` export/import. A docs\n * pointer for the host's import picker, not interface data — the manifest's\n * `importExport` block has no docs field.\n */\nexport const AUTOMATION_FILE_FORMAT_DOCS_URL =\n \"https://docs.openhands.dev/openhands/usage/agent-canvas/managing-automations#exported-file-format\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getCatalogIds(): ReadonlySet<string> {\n return new Set(\n (AUTOMATION_CATALOG as readonly unknown[]).flatMap((entry) =>\n isRecord(entry) && typeof entry.id === \"string\" ? [entry.id] : [],\n ),\n );\n}\n\nfunction admitInterfaceManifest(candidate: unknown): InterfaceManifest | null {\n if (candidate === undefined) return null;\n\n const result = validateInterfaceManifest(candidate, {\n catalogIds: getCatalogIds(),\n mountedRoutes: MOUNTED_ROUTES,\n });\n if (!result.valid) {\n // Mirrors the setup registry: a rejected manifest is skipped loudly, and\n // the surfaces it would have described stay unrendered.\n console.warn(\n \"Rejected the automation interface manifest:\",\n result.errors.join(\"; \"),\n );\n return null;\n }\n return candidate as InterfaceManifest;\n}\n\nconst ADMITTED = admitInterfaceManifest(AUTOMATION_INTERFACE_CANDIDATE);\n\n/**\n * Whether this deployment has an automation interface at all. The nav entries\n * and the route loaders ask before rendering; nothing else has to.\n */\nexport function hasAutomationInterface(): boolean {\n return ADMITTED !== null;\n}\n\n/**\n * The admitted manifest, for the accessors that cannot answer without one.\n * Reaching one of those without a manifest means a surface rendered past its\n * gate — a wiring mistake, not a state to render.\n */\nfunction requireInterface(): InterfaceManifest {\n if (!ADMITTED) {\n throw new Error(\n \"No automation interface manifest is admitted, so this surface should not have rendered.\",\n );\n }\n return ADMITTED;\n}\n\nfunction substituteRouteParam(pattern: string, id: string): string {\n return pattern.replace(\":automationId\", encodeURIComponent(id));\n}\n\nexport function automationListPath(): string {\n return MOUNTED_ROUTES.list;\n}\n\nexport function automationSetupPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.setup, id);\n}\n\nexport function automationDetailPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.detail, id);\n}\n\nexport function automationTemplatesPath(): string {\n return MOUNTED_ROUTES.templates;\n}\n\n/**\n * Whether `path` (a location pathname) is inside the automation surface: the\n * list route itself or anything nested under it (detail, setup, templates,\n * git-sync). Prefix-safe: \"/automations-foo\" does not match.\n */\nexport function isAutomationsRoute(path: string): boolean {\n return (\n path === MOUNTED_ROUTES.list || path.startsWith(`${MOUNTED_ROUTES.list}/`)\n );\n}\n\n/**\n * A declared endpoint path. Empty for one the manifest may omit - the two a\n * bundle needs were added after the block shipped - so a caller that needs one\n * says so rather than reading a host-held default that does not exist.\n */\nexport function getAutomationEndpoint(name: InterfaceEndpointName): string {\n return requireInterface().endpoints[name] ?? \"\";\n}\n\n/** An id-parameterized endpoint with `{id}` substituted, encoded. */\nexport function getAutomationIdEndpoint(\n name: \"detail\" | \"dispatch\" | \"runs\" | \"tarball\",\n id: string,\n): string {\n return getAutomationEndpoint(name).replace(\"{id}\", encodeURIComponent(id));\n}\n\nexport interface InterfaceCopy {\n sidebarLabel: string;\n commandMenuTitle: string;\n commandMenuDescription: string;\n commandMenuKeywords: string;\n listTitle: string;\n listSubtitle: string;\n detailBackLabel: string;\n editTitle: string;\n}\n\nexport function getInterfaceCopy(): InterfaceCopy {\n const manifest = requireInterface();\n return {\n sidebarLabel: manifest.navigation.sidebar.label,\n commandMenuTitle: manifest.navigation.commandMenu.title,\n commandMenuDescription: manifest.navigation.commandMenu.description,\n commandMenuKeywords: manifest.navigation.commandMenu.keywords,\n listTitle: manifest.pages.list.title,\n listSubtitle: manifest.pages.list.subtitle,\n detailBackLabel: manifest.pages.detail.backLabel,\n editTitle: manifest.pages.edit.title,\n };\n}\n\nexport interface AttributeSpec {\n present: boolean;\n label: string;\n /** Null when the manifest states no help text for the attribute. */\n help: string | null;\n required: boolean;\n min: number | null;\n max: number | null;\n}\n\n/** An attribute the manifest does not declare is not offered at all. */\nconst ABSENT_ATTRIBUTE: AttributeSpec = {\n present: false,\n label: \"\",\n help: null,\n required: false,\n min: null,\n max: null,\n};\n\nexport function getAttributeSpec(name: AutomationAttributeName): AttributeSpec {\n const attribute = requireInterface().attributes[name];\n if (!attribute) return ABSENT_ATTRIBUTE;\n return {\n present: true,\n label: attribute.label,\n help: attribute.help ?? null,\n required: attribute.required,\n min: attribute.constraints?.min ?? null,\n max: attribute.constraints?.max ?? null,\n };\n}\n\nexport function getImportExportSpec(): InterfaceImportExport {\n return requireInterface().importExport;\n}\n\nexport function getAutomationsDocsUrl(): string {\n return requireInterface().docsUrl;\n}\n\nexport function getFeaturedAutomationIds(): readonly string[] {\n return requireInterface().featuredAutomationIds;\n}\n\nexport function getResponderIntegrationIds(): readonly string[] {\n return requireInterface().responderIntegrationIds;\n}\n\nexport interface SubPageNavSpec {\n page: InterfaceSubPageId;\n /** The page's route, resolved through the host's route table. */\n to: string;\n label: string;\n icon: InterfaceIconSlug;\n}\n\n/**\n * The sub-page navigation, or null when the manifest does not declare the\n * sub-page surface.\n */\nexport function getSubPagesSpec(): SubPageNavSpec[] | null {\n const subPages = ADMITTED?.navigation.subPages;\n if (!subPages) return null;\n return subPages.map((item) => ({\n page: item.page,\n to:\n item.page === \"templates\"\n ? automationTemplatesPath()\n : automationListPath(),\n label: item.label,\n icon: item.icon,\n }));\n}\n\nexport interface DashboardSpec {\n overview: InterfaceOverview;\n filters: InterfaceDashboardFilter[];\n sort: InterfaceDashboardSort;\n insights: InterfaceListInsights;\n}\n\n/**\n * The list page's dashboard composition, or null when the manifest does not\n * declare it. Admission accepts the sub-page surface whole or not at all, so\n * these four are present together.\n */\nexport function getDashboardSpec(): DashboardSpec | null {\n const list = ADMITTED?.pages.list;\n if (!list?.overview || !list.filters || !list.sort || !list.insights) {\n return null;\n }\n return {\n overview: list.overview,\n filters: list.filters,\n sort: list.sort,\n insights: list.insights,\n };\n}\n\n/** The templates page identity, or null when the manifest does not declare it. */\nexport function getTemplatesPageSpec(): InterfaceTemplatesPage | null {\n return ADMITTED?.pages.templates ?? null;\n}\n"],"mappings":"wMAqCA,IAAM,EAAiB,CACrB,KAAM,eACN,MAAO,iCACP,OAAQ,6BACR,UAAW,wBACb,EAUA,SAAS,EAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,CAAK,CAC5E,CAEA,SAAS,GAAqC,CAC5C,OAAO,IAAI,IACR,EAAA,QAA0C,QAAS,GAClD,EAAS,CAAK,GAAK,OAAO,EAAM,IAAO,SAAW,CAAC,EAAM,EAAE,EAAI,CAAC,CAClE,CACF,CACF,CAEA,SAAS,EAAuB,EAA8C,CAC5E,GAAI,IAAc,IAAA,GAAW,OAAO,KAEpC,IAAM,EAAS,EAAA,0BAA0B,EAAW,CAClD,WAAY,EAAc,EAC1B,cAAe,CACjB,CAAC,EAUD,OATK,EAAO,MASL,GANL,QAAQ,KACN,8CACA,EAAO,OAAO,KAAK,IAAI,CACzB,EACO,KAGX,CAEA,IAAM,EAAW,EAAuB,EAAA,8BAA8B,EAMtE,SAAgB,GAAkC,CAChD,OAAO,IAAa,IACtB,CAOA,SAAS,GAAsC,CAC7C,GAAI,CAAC,EACH,MAAU,MACR,yFACF,EAEF,OAAO,CACT,CAEA,SAAS,EAAqB,EAAiB,EAAoB,CACjE,OAAO,EAAQ,QAAQ,gBAAiB,mBAAmB,CAAE,CAAC,CAChE,CAEA,SAAgB,GAA6B,CAC3C,OAAO,EAAe,IACxB,CAEA,SAAgB,EAAoB,EAAoB,CACtD,OAAO,EAAqB,EAAe,MAAO,CAAE,CACtD,CAeA,SAAgB,EAAmB,EAAuB,CACxD,OACE,IAAS,EAAe,MAAQ,EAAK,WAAW,GAAG,EAAe,KAAK,EAAE,CAE7E,CAOA,SAAgB,EAAsB,EAAqC,CACzE,OAAO,EAAiB,EAAE,UAAU,IAAS,EAC/C,CAGA,SAAgB,EACd,EACA,EACQ,CACR,OAAO,EAAsB,CAAI,EAAE,QAAQ,OAAQ,mBAAmB,CAAE,CAAC,CAC3E,CAaA,SAAgB,GAAkC,CAChD,IAAM,EAAW,EAAiB,EAClC,MAAO,CACL,aAAc,EAAS,WAAW,QAAQ,MAC1C,iBAAkB,EAAS,WAAW,YAAY,MAClD,uBAAwB,EAAS,WAAW,YAAY,YACxD,oBAAqB,EAAS,WAAW,YAAY,SACrD,UAAW,EAAS,MAAM,KAAK,MAC/B,aAAc,EAAS,MAAM,KAAK,SAClC,gBAAiB,EAAS,MAAM,OAAO,UACvC,UAAW,EAAS,MAAM,KAAK,KACjC,CACF,CAmCA,SAAgB,GAA6C,CAC3D,OAAO,EAAiB,EAAE,YAC5B,CAEA,SAAgB,GAAgC,CAC9C,OAAO,EAAiB,EAAE,OAC5B,CAEA,SAAgB,GAA8C,CAC5D,OAAO,EAAiB,EAAE,qBAC5B,CAEA,SAAgB,GAAgD,CAC9D,OAAO,EAAiB,EAAE,uBAC5B"}
@@ -31,6 +31,12 @@ export declare function automationListPath(): string;
31
31
  export declare function automationSetupPath(id: string): string;
32
32
  export declare function automationDetailPath(id: string): string;
33
33
  export declare function automationTemplatesPath(): string;
34
+ /**
35
+ * Whether `path` (a location pathname) is inside the automation surface: the
36
+ * list route itself or anything nested under it (detail, setup, templates,
37
+ * git-sync). Prefix-safe: "/automations-foo" does not match.
38
+ */
39
+ export declare function isAutomationsRoute(path: string): boolean;
34
40
  /**
35
41
  * A declared endpoint path. Empty for one the manifest may omit - the two a
36
42
  * bundle needs were added after the block shipped - so a caller that needs one
@@ -40,12 +40,15 @@ function f(e) {
40
40
  return u(r.setup, e);
41
41
  }
42
42
  function p(e) {
43
+ return e === r.list || e.startsWith(`${r.list}/`);
44
+ }
45
+ function m(e) {
43
46
  return l().endpoints[e] ?? "";
44
47
  }
45
- function m(e, t) {
46
- return p(e).replace("{id}", encodeURIComponent(t));
48
+ function h(e, t) {
49
+ return m(e).replace("{id}", encodeURIComponent(t));
47
50
  }
48
- function h() {
51
+ function g() {
49
52
  let e = l();
50
53
  return {
51
54
  sidebarLabel: e.navigation.sidebar.label,
@@ -58,19 +61,19 @@ function h() {
58
61
  editTitle: e.pages.edit.title
59
62
  };
60
63
  }
61
- function g() {
64
+ function _() {
62
65
  return l().importExport;
63
66
  }
64
- function _() {
67
+ function v() {
65
68
  return l().docsUrl;
66
69
  }
67
- function v() {
70
+ function y() {
68
71
  return l().featuredAutomationIds;
69
72
  }
70
- function y() {
73
+ function b() {
71
74
  return l().responderIntegrationIds;
72
75
  }
73
76
  //#endregion
74
- export { d as automationListPath, f as automationSetupPath, p as getAutomationEndpoint, m as getAutomationIdEndpoint, _ as getAutomationsDocsUrl, v as getFeaturedAutomationIds, g as getImportExportSpec, h as getInterfaceCopy, y as getResponderIntegrationIds, c as hasAutomationInterface };
77
+ export { d as automationListPath, f as automationSetupPath, m as getAutomationEndpoint, h as getAutomationIdEndpoint, v as getAutomationsDocsUrl, y as getFeaturedAutomationIds, _ as getImportExportSpec, g as getInterfaceCopy, b as getResponderIntegrationIds, c as hasAutomationInterface, p as isAutomationsRoute };
75
78
 
76
79
  //# sourceMappingURL=automation-interface.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"automation-interface.js","names":[],"sources":["../../src/manifests/automation-interface.ts"],"sourcesContent":["/**\n * The automation interface seam.\n *\n * Every automation-specific datum the host's surfaces render — navigation and\n * page copy, the settable attributes, the import/export envelope, endpoint\n * paths, the featured and responder id lists, the sub-page surface — is served\n * from the interface manifest the pinned `@openhands/extensions` publishes.\n * The host holds none of it. When the package publishes no manifest, or the\n * published one fails admission, there is nothing to serve: the nav entries do\n * not render and the routes 404. `hasAutomationInterface()` is that gate, and\n * every accessor that needs the manifest is reachable only from behind it.\n *\n * Routes are the exception, and they are not a definition: `src/routes.ts`\n * mounts them, so the host states them here and admission checks a manifest's\n * routes against them. That check is what keeps a published deep link\n * resolving to the page it named.\n */\n\nimport { AUTOMATION_CATALOG } from \"@openhands/extensions/automations\";\nimport { validateInterfaceManifest } from \"./interface-validation\";\nimport { AUTOMATION_INTERFACE_CANDIDATE } from \"./manifest-sources\";\nimport type {\n AutomationAttributeName,\n InterfaceDashboardFilter,\n InterfaceDashboardSort,\n InterfaceEndpointName,\n InterfaceIconSlug,\n InterfaceImportExport,\n InterfaceListInsights,\n InterfaceManifest,\n InterfaceOverview,\n InterfaceRoutes,\n InterfaceSubPageId,\n InterfaceTemplatesPage,\n} from \"./types\";\n\n/** The routes this host has registrations for, in `src/routes.ts`. */\nconst MOUNTED_ROUTES = {\n list: \"/automations\",\n setup: \"/automations/new/:automationId\",\n detail: \"/automations/:automationId\",\n templates: \"/automations/templates\",\n} satisfies InterfaceRoutes;\n\n/**\n * File-format section for portable `.automation.json` export/import. A docs\n * pointer for the host's import picker, not interface data — the manifest's\n * `importExport` block has no docs field.\n */\nexport const AUTOMATION_FILE_FORMAT_DOCS_URL =\n \"https://docs.openhands.dev/openhands/usage/agent-canvas/managing-automations#exported-file-format\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getCatalogIds(): ReadonlySet<string> {\n return new Set(\n (AUTOMATION_CATALOG as readonly unknown[]).flatMap((entry) =>\n isRecord(entry) && typeof entry.id === \"string\" ? [entry.id] : [],\n ),\n );\n}\n\nfunction admitInterfaceManifest(candidate: unknown): InterfaceManifest | null {\n if (candidate === undefined) return null;\n\n const result = validateInterfaceManifest(candidate, {\n catalogIds: getCatalogIds(),\n mountedRoutes: MOUNTED_ROUTES,\n });\n if (!result.valid) {\n // Mirrors the setup registry: a rejected manifest is skipped loudly, and\n // the surfaces it would have described stay unrendered.\n console.warn(\n \"Rejected the automation interface manifest:\",\n result.errors.join(\"; \"),\n );\n return null;\n }\n return candidate as InterfaceManifest;\n}\n\nconst ADMITTED = admitInterfaceManifest(AUTOMATION_INTERFACE_CANDIDATE);\n\n/**\n * Whether this deployment has an automation interface at all. The nav entries\n * and the route loaders ask before rendering; nothing else has to.\n */\nexport function hasAutomationInterface(): boolean {\n return ADMITTED !== null;\n}\n\n/**\n * The admitted manifest, for the accessors that cannot answer without one.\n * Reaching one of those without a manifest means a surface rendered past its\n * gate — a wiring mistake, not a state to render.\n */\nfunction requireInterface(): InterfaceManifest {\n if (!ADMITTED) {\n throw new Error(\n \"No automation interface manifest is admitted, so this surface should not have rendered.\",\n );\n }\n return ADMITTED;\n}\n\nfunction substituteRouteParam(pattern: string, id: string): string {\n return pattern.replace(\":automationId\", encodeURIComponent(id));\n}\n\nexport function automationListPath(): string {\n return MOUNTED_ROUTES.list;\n}\n\nexport function automationSetupPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.setup, id);\n}\n\nexport function automationDetailPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.detail, id);\n}\n\nexport function automationTemplatesPath(): string {\n return MOUNTED_ROUTES.templates;\n}\n\n/**\n * A declared endpoint path. Empty for one the manifest may omit - the two a\n * bundle needs were added after the block shipped - so a caller that needs one\n * says so rather than reading a host-held default that does not exist.\n */\nexport function getAutomationEndpoint(name: InterfaceEndpointName): string {\n return requireInterface().endpoints[name] ?? \"\";\n}\n\n/** An id-parameterized endpoint with `{id}` substituted, encoded. */\nexport function getAutomationIdEndpoint(\n name: \"detail\" | \"dispatch\" | \"runs\" | \"tarball\",\n id: string,\n): string {\n return getAutomationEndpoint(name).replace(\"{id}\", encodeURIComponent(id));\n}\n\nexport interface InterfaceCopy {\n sidebarLabel: string;\n commandMenuTitle: string;\n commandMenuDescription: string;\n commandMenuKeywords: string;\n listTitle: string;\n listSubtitle: string;\n detailBackLabel: string;\n editTitle: string;\n}\n\nexport function getInterfaceCopy(): InterfaceCopy {\n const manifest = requireInterface();\n return {\n sidebarLabel: manifest.navigation.sidebar.label,\n commandMenuTitle: manifest.navigation.commandMenu.title,\n commandMenuDescription: manifest.navigation.commandMenu.description,\n commandMenuKeywords: manifest.navigation.commandMenu.keywords,\n listTitle: manifest.pages.list.title,\n listSubtitle: manifest.pages.list.subtitle,\n detailBackLabel: manifest.pages.detail.backLabel,\n editTitle: manifest.pages.edit.title,\n };\n}\n\nexport interface AttributeSpec {\n present: boolean;\n label: string;\n /** Null when the manifest states no help text for the attribute. */\n help: string | null;\n required: boolean;\n min: number | null;\n max: number | null;\n}\n\n/** An attribute the manifest does not declare is not offered at all. */\nconst ABSENT_ATTRIBUTE: AttributeSpec = {\n present: false,\n label: \"\",\n help: null,\n required: false,\n min: null,\n max: null,\n};\n\nexport function getAttributeSpec(name: AutomationAttributeName): AttributeSpec {\n const attribute = requireInterface().attributes[name];\n if (!attribute) return ABSENT_ATTRIBUTE;\n return {\n present: true,\n label: attribute.label,\n help: attribute.help ?? null,\n required: attribute.required,\n min: attribute.constraints?.min ?? null,\n max: attribute.constraints?.max ?? null,\n };\n}\n\nexport function getImportExportSpec(): InterfaceImportExport {\n return requireInterface().importExport;\n}\n\nexport function getAutomationsDocsUrl(): string {\n return requireInterface().docsUrl;\n}\n\nexport function getFeaturedAutomationIds(): readonly string[] {\n return requireInterface().featuredAutomationIds;\n}\n\nexport function getResponderIntegrationIds(): readonly string[] {\n return requireInterface().responderIntegrationIds;\n}\n\nexport interface SubPageNavSpec {\n page: InterfaceSubPageId;\n /** The page's route, resolved through the host's route table. */\n to: string;\n label: string;\n icon: InterfaceIconSlug;\n}\n\n/**\n * The sub-page navigation, or null when the manifest does not declare the\n * sub-page surface.\n */\nexport function getSubPagesSpec(): SubPageNavSpec[] | null {\n const subPages = ADMITTED?.navigation.subPages;\n if (!subPages) return null;\n return subPages.map((item) => ({\n page: item.page,\n to:\n item.page === \"templates\"\n ? automationTemplatesPath()\n : automationListPath(),\n label: item.label,\n icon: item.icon,\n }));\n}\n\nexport interface DashboardSpec {\n overview: InterfaceOverview;\n filters: InterfaceDashboardFilter[];\n sort: InterfaceDashboardSort;\n insights: InterfaceListInsights;\n}\n\n/**\n * The list page's dashboard composition, or null when the manifest does not\n * declare it. Admission accepts the sub-page surface whole or not at all, so\n * these four are present together.\n */\nexport function getDashboardSpec(): DashboardSpec | null {\n const list = ADMITTED?.pages.list;\n if (!list?.overview || !list.filters || !list.sort || !list.insights) {\n return null;\n }\n return {\n overview: list.overview,\n filters: list.filters,\n sort: list.sort,\n insights: list.insights,\n };\n}\n\n/** The templates page identity, or null when the manifest does not declare it. */\nexport function getTemplatesPageSpec(): InterfaceTemplatesPage | null {\n return ADMITTED?.pages.templates ?? null;\n}\n"],"mappings":";;;;AAqCA,IAAM,IAAiB;CACrB,MAAM;CACN,OAAO;CACP,QAAQ;CACR,WAAW;AACb;AAUA,SAAS,EAAS,GAAkD;CAClE,OAAO,OAAO,KAAU,cAAY,KAAkB,CAAC,MAAM,QAAQ,CAAK;AAC5E;AAEA,SAAS,IAAqC;CAC5C,OAAO,IAAI,IACR,EAA0C,SAAS,MAClD,EAAS,CAAK,KAAK,OAAO,EAAM,MAAO,WAAW,CAAC,EAAM,EAAE,IAAI,CAAC,CAClE,CACF;AACF;AAEA,SAAS,EAAuB,GAA8C;CAC5E,IAAI,MAAc,KAAA,GAAW,OAAO;CAEpC,IAAM,IAAS,EAA0B,GAAW;EAClD,YAAY,EAAc;EAC1B,eAAe;CACjB,CAAC;CAUD,OATK,EAAO,QASL,KANL,QAAQ,KACN,+CACA,EAAO,OAAO,KAAK,IAAI,CACzB,GACO;AAGX;AAEA,IAAM,IAAW,EAAuB,CAA8B;AAMtE,SAAgB,IAAkC;CAChD,OAAO,MAAa;AACtB;AAOA,SAAS,IAAsC;CAC7C,IAAI,CAAC,GACH,MAAU,MACR,yFACF;CAEF,OAAO;AACT;AAEA,SAAS,EAAqB,GAAiB,GAAoB;CACjE,OAAO,EAAQ,QAAQ,iBAAiB,mBAAmB,CAAE,CAAC;AAChE;AAEA,SAAgB,IAA6B;CAC3C,OAAO,EAAe;AACxB;AAEA,SAAgB,EAAoB,GAAoB;CACtD,OAAO,EAAqB,EAAe,OAAO,CAAE;AACtD;AAeA,SAAgB,EAAsB,GAAqC;CACzE,OAAO,EAAiB,EAAE,UAAU,MAAS;AAC/C;AAGA,SAAgB,EACd,GACA,GACQ;CACR,OAAO,EAAsB,CAAI,EAAE,QAAQ,QAAQ,mBAAmB,CAAE,CAAC;AAC3E;AAaA,SAAgB,IAAkC;CAChD,IAAM,IAAW,EAAiB;CAClC,OAAO;EACL,cAAc,EAAS,WAAW,QAAQ;EAC1C,kBAAkB,EAAS,WAAW,YAAY;EAClD,wBAAwB,EAAS,WAAW,YAAY;EACxD,qBAAqB,EAAS,WAAW,YAAY;EACrD,WAAW,EAAS,MAAM,KAAK;EAC/B,cAAc,EAAS,MAAM,KAAK;EAClC,iBAAiB,EAAS,MAAM,OAAO;EACvC,WAAW,EAAS,MAAM,KAAK;CACjC;AACF;AAmCA,SAAgB,IAA6C;CAC3D,OAAO,EAAiB,EAAE;AAC5B;AAEA,SAAgB,IAAgC;CAC9C,OAAO,EAAiB,EAAE;AAC5B;AAEA,SAAgB,IAA8C;CAC5D,OAAO,EAAiB,EAAE;AAC5B;AAEA,SAAgB,IAAgD;CAC9D,OAAO,EAAiB,EAAE;AAC5B"}
1
+ {"version":3,"file":"automation-interface.js","names":[],"sources":["../../src/manifests/automation-interface.ts"],"sourcesContent":["/**\n * The automation interface seam.\n *\n * Every automation-specific datum the host's surfaces render — navigation and\n * page copy, the settable attributes, the import/export envelope, endpoint\n * paths, the featured and responder id lists, the sub-page surface — is served\n * from the interface manifest the pinned `@openhands/extensions` publishes.\n * The host holds none of it. When the package publishes no manifest, or the\n * published one fails admission, there is nothing to serve: the nav entries do\n * not render and the routes 404. `hasAutomationInterface()` is that gate, and\n * every accessor that needs the manifest is reachable only from behind it.\n *\n * Routes are the exception, and they are not a definition: `src/routes.ts`\n * mounts them, so the host states them here and admission checks a manifest's\n * routes against them. That check is what keeps a published deep link\n * resolving to the page it named.\n */\n\nimport { AUTOMATION_CATALOG } from \"@openhands/extensions/automations\";\nimport { validateInterfaceManifest } from \"./interface-validation\";\nimport { AUTOMATION_INTERFACE_CANDIDATE } from \"./manifest-sources\";\nimport type {\n AutomationAttributeName,\n InterfaceDashboardFilter,\n InterfaceDashboardSort,\n InterfaceEndpointName,\n InterfaceIconSlug,\n InterfaceImportExport,\n InterfaceListInsights,\n InterfaceManifest,\n InterfaceOverview,\n InterfaceRoutes,\n InterfaceSubPageId,\n InterfaceTemplatesPage,\n} from \"./types\";\n\n/** The routes this host has registrations for, in `src/routes.ts`. */\nconst MOUNTED_ROUTES = {\n list: \"/automations\",\n setup: \"/automations/new/:automationId\",\n detail: \"/automations/:automationId\",\n templates: \"/automations/templates\",\n} satisfies InterfaceRoutes;\n\n/**\n * File-format section for portable `.automation.json` export/import. A docs\n * pointer for the host's import picker, not interface data — the manifest's\n * `importExport` block has no docs field.\n */\nexport const AUTOMATION_FILE_FORMAT_DOCS_URL =\n \"https://docs.openhands.dev/openhands/usage/agent-canvas/managing-automations#exported-file-format\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getCatalogIds(): ReadonlySet<string> {\n return new Set(\n (AUTOMATION_CATALOG as readonly unknown[]).flatMap((entry) =>\n isRecord(entry) && typeof entry.id === \"string\" ? [entry.id] : [],\n ),\n );\n}\n\nfunction admitInterfaceManifest(candidate: unknown): InterfaceManifest | null {\n if (candidate === undefined) return null;\n\n const result = validateInterfaceManifest(candidate, {\n catalogIds: getCatalogIds(),\n mountedRoutes: MOUNTED_ROUTES,\n });\n if (!result.valid) {\n // Mirrors the setup registry: a rejected manifest is skipped loudly, and\n // the surfaces it would have described stay unrendered.\n console.warn(\n \"Rejected the automation interface manifest:\",\n result.errors.join(\"; \"),\n );\n return null;\n }\n return candidate as InterfaceManifest;\n}\n\nconst ADMITTED = admitInterfaceManifest(AUTOMATION_INTERFACE_CANDIDATE);\n\n/**\n * Whether this deployment has an automation interface at all. The nav entries\n * and the route loaders ask before rendering; nothing else has to.\n */\nexport function hasAutomationInterface(): boolean {\n return ADMITTED !== null;\n}\n\n/**\n * The admitted manifest, for the accessors that cannot answer without one.\n * Reaching one of those without a manifest means a surface rendered past its\n * gate — a wiring mistake, not a state to render.\n */\nfunction requireInterface(): InterfaceManifest {\n if (!ADMITTED) {\n throw new Error(\n \"No automation interface manifest is admitted, so this surface should not have rendered.\",\n );\n }\n return ADMITTED;\n}\n\nfunction substituteRouteParam(pattern: string, id: string): string {\n return pattern.replace(\":automationId\", encodeURIComponent(id));\n}\n\nexport function automationListPath(): string {\n return MOUNTED_ROUTES.list;\n}\n\nexport function automationSetupPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.setup, id);\n}\n\nexport function automationDetailPath(id: string): string {\n return substituteRouteParam(MOUNTED_ROUTES.detail, id);\n}\n\nexport function automationTemplatesPath(): string {\n return MOUNTED_ROUTES.templates;\n}\n\n/**\n * Whether `path` (a location pathname) is inside the automation surface: the\n * list route itself or anything nested under it (detail, setup, templates,\n * git-sync). Prefix-safe: \"/automations-foo\" does not match.\n */\nexport function isAutomationsRoute(path: string): boolean {\n return (\n path === MOUNTED_ROUTES.list || path.startsWith(`${MOUNTED_ROUTES.list}/`)\n );\n}\n\n/**\n * A declared endpoint path. Empty for one the manifest may omit - the two a\n * bundle needs were added after the block shipped - so a caller that needs one\n * says so rather than reading a host-held default that does not exist.\n */\nexport function getAutomationEndpoint(name: InterfaceEndpointName): string {\n return requireInterface().endpoints[name] ?? \"\";\n}\n\n/** An id-parameterized endpoint with `{id}` substituted, encoded. */\nexport function getAutomationIdEndpoint(\n name: \"detail\" | \"dispatch\" | \"runs\" | \"tarball\",\n id: string,\n): string {\n return getAutomationEndpoint(name).replace(\"{id}\", encodeURIComponent(id));\n}\n\nexport interface InterfaceCopy {\n sidebarLabel: string;\n commandMenuTitle: string;\n commandMenuDescription: string;\n commandMenuKeywords: string;\n listTitle: string;\n listSubtitle: string;\n detailBackLabel: string;\n editTitle: string;\n}\n\nexport function getInterfaceCopy(): InterfaceCopy {\n const manifest = requireInterface();\n return {\n sidebarLabel: manifest.navigation.sidebar.label,\n commandMenuTitle: manifest.navigation.commandMenu.title,\n commandMenuDescription: manifest.navigation.commandMenu.description,\n commandMenuKeywords: manifest.navigation.commandMenu.keywords,\n listTitle: manifest.pages.list.title,\n listSubtitle: manifest.pages.list.subtitle,\n detailBackLabel: manifest.pages.detail.backLabel,\n editTitle: manifest.pages.edit.title,\n };\n}\n\nexport interface AttributeSpec {\n present: boolean;\n label: string;\n /** Null when the manifest states no help text for the attribute. */\n help: string | null;\n required: boolean;\n min: number | null;\n max: number | null;\n}\n\n/** An attribute the manifest does not declare is not offered at all. */\nconst ABSENT_ATTRIBUTE: AttributeSpec = {\n present: false,\n label: \"\",\n help: null,\n required: false,\n min: null,\n max: null,\n};\n\nexport function getAttributeSpec(name: AutomationAttributeName): AttributeSpec {\n const attribute = requireInterface().attributes[name];\n if (!attribute) return ABSENT_ATTRIBUTE;\n return {\n present: true,\n label: attribute.label,\n help: attribute.help ?? null,\n required: attribute.required,\n min: attribute.constraints?.min ?? null,\n max: attribute.constraints?.max ?? null,\n };\n}\n\nexport function getImportExportSpec(): InterfaceImportExport {\n return requireInterface().importExport;\n}\n\nexport function getAutomationsDocsUrl(): string {\n return requireInterface().docsUrl;\n}\n\nexport function getFeaturedAutomationIds(): readonly string[] {\n return requireInterface().featuredAutomationIds;\n}\n\nexport function getResponderIntegrationIds(): readonly string[] {\n return requireInterface().responderIntegrationIds;\n}\n\nexport interface SubPageNavSpec {\n page: InterfaceSubPageId;\n /** The page's route, resolved through the host's route table. */\n to: string;\n label: string;\n icon: InterfaceIconSlug;\n}\n\n/**\n * The sub-page navigation, or null when the manifest does not declare the\n * sub-page surface.\n */\nexport function getSubPagesSpec(): SubPageNavSpec[] | null {\n const subPages = ADMITTED?.navigation.subPages;\n if (!subPages) return null;\n return subPages.map((item) => ({\n page: item.page,\n to:\n item.page === \"templates\"\n ? automationTemplatesPath()\n : automationListPath(),\n label: item.label,\n icon: item.icon,\n }));\n}\n\nexport interface DashboardSpec {\n overview: InterfaceOverview;\n filters: InterfaceDashboardFilter[];\n sort: InterfaceDashboardSort;\n insights: InterfaceListInsights;\n}\n\n/**\n * The list page's dashboard composition, or null when the manifest does not\n * declare it. Admission accepts the sub-page surface whole or not at all, so\n * these four are present together.\n */\nexport function getDashboardSpec(): DashboardSpec | null {\n const list = ADMITTED?.pages.list;\n if (!list?.overview || !list.filters || !list.sort || !list.insights) {\n return null;\n }\n return {\n overview: list.overview,\n filters: list.filters,\n sort: list.sort,\n insights: list.insights,\n };\n}\n\n/** The templates page identity, or null when the manifest does not declare it. */\nexport function getTemplatesPageSpec(): InterfaceTemplatesPage | null {\n return ADMITTED?.pages.templates ?? null;\n}\n"],"mappings":";;;;AAqCA,IAAM,IAAiB;CACrB,MAAM;CACN,OAAO;CACP,QAAQ;CACR,WAAW;AACb;AAUA,SAAS,EAAS,GAAkD;CAClE,OAAO,OAAO,KAAU,cAAY,KAAkB,CAAC,MAAM,QAAQ,CAAK;AAC5E;AAEA,SAAS,IAAqC;CAC5C,OAAO,IAAI,IACR,EAA0C,SAAS,MAClD,EAAS,CAAK,KAAK,OAAO,EAAM,MAAO,WAAW,CAAC,EAAM,EAAE,IAAI,CAAC,CAClE,CACF;AACF;AAEA,SAAS,EAAuB,GAA8C;CAC5E,IAAI,MAAc,KAAA,GAAW,OAAO;CAEpC,IAAM,IAAS,EAA0B,GAAW;EAClD,YAAY,EAAc;EAC1B,eAAe;CACjB,CAAC;CAUD,OATK,EAAO,QASL,KANL,QAAQ,KACN,+CACA,EAAO,OAAO,KAAK,IAAI,CACzB,GACO;AAGX;AAEA,IAAM,IAAW,EAAuB,CAA8B;AAMtE,SAAgB,IAAkC;CAChD,OAAO,MAAa;AACtB;AAOA,SAAS,IAAsC;CAC7C,IAAI,CAAC,GACH,MAAU,MACR,yFACF;CAEF,OAAO;AACT;AAEA,SAAS,EAAqB,GAAiB,GAAoB;CACjE,OAAO,EAAQ,QAAQ,iBAAiB,mBAAmB,CAAE,CAAC;AAChE;AAEA,SAAgB,IAA6B;CAC3C,OAAO,EAAe;AACxB;AAEA,SAAgB,EAAoB,GAAoB;CACtD,OAAO,EAAqB,EAAe,OAAO,CAAE;AACtD;AAeA,SAAgB,EAAmB,GAAuB;CACxD,OACE,MAAS,EAAe,QAAQ,EAAK,WAAW,GAAG,EAAe,KAAK,EAAE;AAE7E;AAOA,SAAgB,EAAsB,GAAqC;CACzE,OAAO,EAAiB,EAAE,UAAU,MAAS;AAC/C;AAGA,SAAgB,EACd,GACA,GACQ;CACR,OAAO,EAAsB,CAAI,EAAE,QAAQ,QAAQ,mBAAmB,CAAE,CAAC;AAC3E;AAaA,SAAgB,IAAkC;CAChD,IAAM,IAAW,EAAiB;CAClC,OAAO;EACL,cAAc,EAAS,WAAW,QAAQ;EAC1C,kBAAkB,EAAS,WAAW,YAAY;EAClD,wBAAwB,EAAS,WAAW,YAAY;EACxD,qBAAqB,EAAS,WAAW,YAAY;EACrD,WAAW,EAAS,MAAM,KAAK;EAC/B,cAAc,EAAS,MAAM,KAAK;EAClC,iBAAiB,EAAS,MAAM,OAAO;EACvC,WAAW,EAAS,MAAM,KAAK;CACjC;AACF;AAmCA,SAAgB,IAA6C;CAC3D,OAAO,EAAiB,EAAE;AAC5B;AAEA,SAAgB,IAAgC;CAC9C,OAAO,EAAiB,EAAE;AAC5B;AAEA,SAAgB,IAA8C;CAC5D,OAAO,EAAiB,EAAE;AAC5B;AAEA,SAAgB,IAAgD;CAC9D,OAAO,EAAiB,EAAE;AAC5B"}
@@ -1,2 +1,2 @@
1
- require("../_virtual/_rolldown/runtime.cjs"),require("../i18n/index.cjs"),require("../utils/automation-catalog.cjs");const e=require("./automation-interface.cjs");function t(t){return t&&i(t)?r(`createBundle`):e.getAutomationEndpoint(`createPrompt`)}function n(){return r(`uploads`)}function r(t){let n=e.getAutomationEndpoint(t);if(!n)throw Error(`The published automation interface declares no '${t}' endpoint, so this deployment cannot create an automation from a script bundle.`);return n}function i(e){return e.setup.mode===`direct`&&e.setup.bundle!==void 0}exports.automationCreateEndpoint=t,exports.automationUploadEndpoint=n;
1
+ require("../_virtual/_rolldown/runtime.cjs"),require("../i18n/index.cjs"),require("../utils/automation-catalog.cjs");const e=require("./automation-interface.cjs"),t=require("./manifest-local-validation.cjs");function n(t,n){let r=o(t,n);return r===`plugin`?e.getAutomationEndpoint(`createPlugin`):r===`upload`||r===`bundle`?i(`createBundle`):e.getAutomationEndpoint(`createPrompt`)}function r(){return i(`uploads`)}function i(t){let n=e.getAutomationEndpoint(t);if(!n)throw Error(`The published automation interface declares no '${t}' endpoint, so this deployment cannot create an automation from a script bundle.`);return n}function a(e){return e.setup.mode===`direct`&&e.setup.bundle!==void 0}function o(e,n){if(!e)return`prompt`;if(a(e))return`bundle`;let r=e.setup.actions??{};if(n&&n in r)return n;let i=t.actionKinds(e.setup);return i.length===1?i[0]:`prompt`}exports.automationCreateEndpoint=n,exports.automationUploadEndpoint=r;
2
2
  //# sourceMappingURL=automation-setup.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"automation-setup.cjs","names":[],"sources":["../../src/manifests/automation-setup.ts"],"sourcesContent":["/**\n * The only module that knows a setup entry configures an *automation*.\n *\n * The published contract states just what varies between entries; everything\n * derivable is the host's to generate. That derivation is here, and nowhere\n * else, so the rest of `src/manifests/` stays about forms rather than about\n * automations.\n *\n * The algorithm mirrors `tests/test_automation_setup.py::_render_payload` in\n * `OpenHands/extensions`, which is the authoritative reference: it produces the\n * request bodies published in that repository's contract fixtures, and those\n * were verified against the live service. The create model is `extra=\"forbid\"`,\n * so any divergence is a hard 422 rather than a dropped field.\n */\n\nimport i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { findAutomationCommand } from \"#/utils/automation-catalog\";\nimport { getAutomationEndpoint } from \"./automation-interface\";\nimport {\n collectFields,\n fieldText,\n fieldValues,\n} from \"./manifest-local-validation\";\nimport { interpolateText, interpolateValue } from \"./manifest-template\";\nimport type {\n SetupBlock,\n SetupBundleConfigValue,\n SetupEntry,\n SetupFormValues,\n SetupRequestBody,\n SetupTriggerKind,\n} from \"./types\";\n\n/**\n * The creation endpoint a derived draft would be posted to. Resolved on call\n * rather than at import, because the endpoint is the interface manifest's and\n * this module loads whether or not one was admitted.\n *\n * A bundle entry is created through the raw endpoint, because what it sends is\n * a tarball it uploaded rather than arguments to a preset. Called without an\n * entry - as the import path does - it answers for a prompt.\n */\nexport function automationCreateEndpoint(entry?: SetupEntry): string {\n if (entry && isBundleEntry(entry)) {\n return requireBundleEndpoint(\"createBundle\");\n }\n return getAutomationEndpoint(\"createPrompt\");\n}\n\n/** Where a bundle's tarball is uploaded, before the create call. */\nexport function automationUploadEndpoint(): string {\n return requireBundleEndpoint(\"uploads\");\n}\n\n/** The endpoints a bundle entry cannot be created without. */\nconst BUNDLE_ENDPOINTS = [\"createBundle\", \"uploads\"] as const;\n\n/**\n * An endpoint only a bundle needs. The interface manifest may predate bundles,\n * and the host holds no path of its own to fall back to, so this is where that\n * runs out rather than somewhere deep in a request.\n */\nfunction requireBundleEndpoint(\n name: (typeof BUNDLE_ENDPOINTS)[number],\n): string {\n const path = getAutomationEndpoint(name);\n if (!path) {\n throw new Error(\n `The published automation interface declares no '${name}' endpoint, ` +\n \"so this deployment cannot create an automation from a script bundle.\",\n );\n }\n return path;\n}\n\n/**\n * The endpoints this entry needs that the published interface does not declare.\n *\n * Asked before the form renders rather than discovered at the moment of\n * creating: a pinned package that predates bundles can never answer, and the\n * dialog can say so while nothing has been filled in yet. Empty for every\n * entry that is not a bundle, which needs nothing beyond what the block has\n * always declared.\n */\nexport function missingCreateEndpoints(entry: SetupEntry): string[] {\n if (!isBundleEntry(entry)) return [];\n return BUNDLE_ENDPOINTS.filter((name) => !getAutomationEndpoint(name));\n}\n\n/** Whether this entry ships a script tarball instead of a prompt. */\nexport function isBundleEntry(entry: SetupEntry): boolean {\n return entry.setup.mode === \"direct\" && entry.setup.bundle !== undefined;\n}\n\n/**\n * The `tarball_path` a preflight draft carries.\n *\n * Preflight runs on every field blur and the upload happens once, at submit,\n * so there is no real path to send yet. The service checks this field's scheme\n * at preflight and its ownership only at creation, so a well-formed stand-in\n * validates exactly what preflight is for - the rest of the body - without\n * uploading an archive per keystroke.\n */\nexport const PREFLIGHT_TARBALL_PATH =\n \"oh-internal://uploads/00000000-0000-0000-0000-000000000000\";\n\n/**\n * Trigger properties a form field may fill, per trigger kind. A field under a\n * trigger kind whose name is listed here fills the trigger property of the same\n * name; anything else there, such as a phrase to match, is an input to `filter`.\n */\nconst TRIGGER_PROPERTIES: Record<SetupTriggerKind, readonly string[]> = {\n cron: [\"schedule\", \"timezone\"],\n event: [\"on\"],\n};\n\n/**\n * Repository properties a form field may fill, read the same way\n * {@link TRIGGER_PROPERTIES} is: a field whose name is listed here fills the\n * `repos[]` property of the same name. `url` and `provider` are absent because\n * neither is answered by a field of its own — they come from the repo-picker's\n * value and from its declared provider.\n *\n * These two lists are the only field names the derivation knows; everything\n * else it reads it finds by field type. An entry that wants a base branch in\n * its request therefore has to name that field `ref`.\n */\nconst REPO_PROPERTIES: readonly string[] = [\"ref\"];\n\nconst FORM_PLACEHOLDER_PATTERN = /\\{\\{form\\.([A-Za-z0-9_.]+)\\}\\}/g;\n\n/** The repository input, which supplies `repos` and an event trigger's source. */\nfunction findRepoPickerField(setup: SetupBlock) {\n const match = Object.entries(collectFields(setup)).find(\n ([, field]) => field.type === \"repo-picker\",\n );\n return match ? { name: match[0], field: match[1] } : null;\n}\n\n/** Every repository the form collected, whether the picker takes one or many. */\nfunction repositories(setup: SetupBlock, values: SetupFormValues): string[] {\n const picker = findRepoPickerField(setup);\n return picker ? fieldValues(values[picker.name]) : [];\n}\n\n/**\n * The created automation's name.\n *\n * One repository is worth naming; several are not, so the count stands in\n * rather than a list of names that would not fit.\n */\nfunction deriveName(entry: SetupEntry, values: SetupFormValues): string {\n const repos = repositories(entry.setup, values);\n if (repos.length === 0) return entry.name;\n if (repos.length === 1) return `${entry.name} - ${repos[0]}`;\n // The count is the one word here the host writes rather than reads off the\n // entry, so it is translated. There is no translator to pass in: the\n // derivation runs from a memo, an upload and a test alike.\n const count = i18n.t(I18nKey.SETUP$REPOSITORY_COUNT, {\n total: repos.length,\n });\n return `${entry.name} - ${count}`;\n}\n\n/** The single trigger kind a direct entry declares, with its fields. */\nfunction getTrigger(setup: SetupBlock) {\n const entries = Object.entries(setup.form.triggers ?? {});\n if (entries.length === 0) return null;\n const [kind, fields] = entries[0];\n return { kind: kind as SetupTriggerKind, fields: fields ?? {} };\n}\n\n/**\n * The create request body these form values produce.\n *\n * No entry declares it: `name` comes from the entry, `repos` from the\n * repo-picker field and its declared provider, and `trigger` from the key and\n * fields under `form.triggers`. Only `prompt` and an event `filter` are\n * declared, because only they cannot be read off the form.\n *\n * Returns null for an assisted entry, which hands setup to a conversation\n * instead of sending a body.\n */\nexport function buildCreatePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n /** Bundle entries only: what the upload returned. */\n tarballPath: string = PREFLIGHT_TARBALL_PATH,\n): SetupRequestBody | null {\n const { setup } = entry;\n if (setup.mode !== \"direct\") return null;\n if (setup.bundle) return buildBundlePayload(entry, values, tarballPath);\n if (!setup.prompt) return null;\n\n const scope = { form: values, automation: entry };\n const repoPicker = findRepoPickerField(setup);\n const repos = repositories(setup, values);\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n prompt: interpolateText(setup.prompt, scope),\n };\n\n if (repos.length > 0 && repoPicker?.field.provider) {\n const declared = REPO_PROPERTIES.filter((name) => name in values);\n payload.repos = repos.map((url) => ({\n url,\n ...Object.fromEntries(\n declared.map((name) => [name, fieldText(values[name])]),\n ),\n provider: repoPicker.field.provider as string,\n }));\n }\n\n // A filter is optional: an entry that declares none accepts every delivered\n // event, so the key is left off rather than sent empty.\n const trigger = buildTrigger(entry, values);\n if (trigger) payload.trigger = trigger;\n\n const template = buildTemplate(entry, values);\n if (template) payload.template = template;\n\n return payload;\n}\n\n/**\n * The `template` provenance a prompt entry sends, or undefined for one that\n * publishes no version.\n *\n * It is what lets the service recognise a second setup of the same entry as\n * the automation it already holds. The bundle path derives the same block from\n * the config the bundle declares; a prompt entry declares none, so the answer\n * to \"what was this created from\" is the form as submitted.\n */\nfunction buildTemplate(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | undefined {\n if (!entry.version) return undefined;\n return {\n id: entry.id,\n version: entry.version,\n config: { ...values },\n };\n}\n\n/**\n * The `trigger` object, read off the key and fields under `form.triggers`.\n *\n * Identical for both kinds of direct entry: only the create endpoint and what\n * the automation is told to do differ between a prompt and a bundle.\n */\nfunction buildTrigger(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | undefined {\n const trigger = getTrigger(entry.setup);\n if (!trigger) return undefined;\n\n const properties = TRIGGER_PROPERTIES[trigger.kind];\n const declared = Object.keys(trigger.fields).filter((name) =>\n properties.includes(name),\n );\n const repoPicker = findRepoPickerField(entry.setup);\n\n return {\n type: trigger.kind,\n ...Object.fromEntries(\n declared.map((name) => [name, fieldText(values[name])]),\n ),\n ...(trigger.kind === \"event\" && {\n source: repoPicker?.field.provider ?? \"\",\n ...(entry.setup.filter && {\n filter: interpolateText(entry.setup.filter, {\n form: values,\n automation: entry,\n }),\n }),\n }),\n };\n}\n\n/**\n * The raw create body a bundle entry produces.\n *\n * `tarball_path` is the one value neither declared nor derived: the host packs\n * and uploads the bundle first, and creates from what came back. `template` is\n * the provenance that makes enabling the same entry twice return the\n * automation that already exists rather than a second one.\n *\n * There is no `repos`: the raw endpoint has no such field, and a bundle's\n * script fetches what it needs itself.\n */\nfunction buildBundlePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n tarballPath: string,\n): SetupRequestBody {\n const bundle = entry.setup.bundle!;\n const scope = { form: values, automation: entry };\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n };\n\n const trigger = buildTrigger(entry, values);\n if (trigger) payload.trigger = trigger;\n\n payload.tarball_path = tarballPath;\n payload.entrypoint = bundle.entrypoint;\n if (bundle.setupScript) payload.setup_script_path = bundle.setupScript;\n if (bundle.timeout !== undefined) payload.timeout = bundle.timeout;\n payload.template = {\n id: entry.id,\n version: bundle.version,\n config: interpolateConfig(bundle.config, scope) as SetupRequestBody,\n };\n\n return payload;\n}\n\n/**\n * Placeholder substitution over the config tree. Only string leaves are\n * templated; a number, a boolean or a null is written through as itself, so an\n * entry can state a value the script reads as the type it expects.\n */\nfunction interpolateConfig(\n node: SetupBundleConfigValue,\n scope: Parameters<typeof interpolateText>[1],\n): SetupBundleConfigValue {\n if (typeof node === \"string\") {\n return interpolateValue(node, scope);\n }\n if (Array.isArray(node)) {\n return node.map((item) => interpolateConfig(item, scope));\n }\n if (typeof node === \"object\" && node !== null) {\n return Object.fromEntries(\n Object.entries(node).map(([key, value]) => [\n key,\n interpolateConfig(value, scope),\n ]),\n );\n }\n return node;\n}\n\n/**\n * The preflight body the host sends. The same shape for every entry, so no\n * entry declares it.\n */\nexport function buildPreflightBody(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | null {\n const draft = buildCreatePayload(entry, values);\n if (!draft) return null;\n\n return {\n automationId: entry.id,\n endpoint: automationCreateEndpoint(entry),\n draft,\n };\n}\n\n/**\n * What an assisted entry sends into the conversation that finishes setup.\n *\n * The command is not declared either: it lives once, in the owning skill's own\n * `triggers` frontmatter, and the skill defaults to the entry's id. An entry\n * whose skill is invoked by description rather than by command contributes\n * nothing here, and the answers alone open the conversation.\n */\nexport function buildAssistedMessage(\n entry: SetupEntry,\n values: SetupFormValues,\n): string {\n const command = findAutomationCommand(entry);\n const message = entry.setup.message\n ? interpolateText(entry.setup.message, { form: values, automation: entry })\n : \"\";\n\n return [command, message].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction collectPlaceholderFields(value: string): string[] {\n const names = Array.from(\n value.matchAll(FORM_PLACEHOLDER_PATTERN),\n (match) => match[1],\n );\n return Array.from(new Set(names));\n}\n\n/**\n * Which form fields built each payload path.\n *\n * Preflight and the create endpoint reject a draft by payload path, and the\n * host has to turn that back into a highlighted input. Building the body with\n * each field standing in for its own value recovers the mapping exactly, so an\n * entry does not declare it.\n */\nexport function deriveErrorMap(entry: SetupEntry): Record<string, string[]> {\n const mapping: Record<string, string[]> = {};\n\n const standIns = Object.fromEntries(\n Object.keys(collectFields(entry.setup)).map((name) => [\n name,\n `{{form.${name}}}`,\n ]),\n );\n const template = buildCreatePayload(entry, standIns);\n if (!template) return mapping;\n\n const walk = (node: unknown, path: string): void => {\n if (Array.isArray(node)) {\n node.forEach((item, index) => walk(item, `${path}[${index}]`));\n return;\n }\n if (typeof node === \"object\" && node !== null) {\n Object.entries(node).forEach(([key, item]) =>\n walk(item, path ? `${path}.${key}` : key),\n );\n return;\n }\n if (typeof node === \"string\") {\n const names = collectPlaceholderFields(node);\n if (names.length > 0) mapping[path] = names;\n }\n };\n\n walk(template, \"\");\n return mapping;\n}\n"],"mappings":"mKA2CA,SAAgB,EAAyB,EAA4B,CAInE,OAHI,GAAS,EAAc,CAAK,EACvB,EAAsB,cAAc,EAEtC,EAAA,sBAAsB,cAAc,CAC7C,CAGA,SAAgB,GAAmC,CACjD,OAAO,EAAsB,SAAS,CACxC,CAUA,SAAS,EACP,EACQ,CACR,IAAM,EAAO,EAAA,sBAAsB,CAAI,EACvC,GAAI,CAAC,EACH,MAAU,MACR,mDAAmD,EAAK,iFAE1D,EAEF,OAAO,CACT,CAiBA,SAAgB,EAAc,EAA4B,CACxD,OAAO,EAAM,MAAM,OAAS,UAAY,EAAM,MAAM,SAAW,IAAA,EACjE"}
1
+ {"version":3,"file":"automation-setup.cjs","names":[],"sources":["../../src/manifests/automation-setup.ts"],"sourcesContent":["/**\n * The only module that knows a setup entry configures an *automation*.\n *\n * The published contract states just what varies between entries; everything\n * derivable is the host's to generate. That derivation is here, and nowhere\n * else, so the rest of `src/manifests/` stays about forms rather than about\n * automations.\n *\n * The algorithm mirrors `tests/test_automation_setup.py::_render_payload` in\n * `OpenHands/extensions`, which is the authoritative reference: it produces the\n * request bodies published in that repository's contract fixtures, and those\n * were verified against the live service. The create model is `extra=\"forbid\"`,\n * so any divergence is a hard 422 rather than a dropped field.\n */\n\nimport i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { findAutomationCommand } from \"#/utils/automation-catalog\";\nimport { getAutomationEndpoint } from \"./automation-interface\";\nimport {\n actionKinds,\n collectFields,\n fieldText,\n fieldValues,\n} from \"./manifest-local-validation\";\nimport { interpolateText, interpolateValue } from \"./manifest-template\";\nimport type {\n SetupActionKind,\n SetupBlock,\n SetupBundleConfigValue,\n SetupEntry,\n SetupFormValues,\n SetupRequestBody,\n SetupTriggerKind,\n} from \"./types\";\n\n/**\n * The creation endpoint a derived draft would be posted to. Resolved on call\n * rather than at import, because the endpoint is the interface manifest's and\n * this module loads whether or not one was admitted.\n *\n * A bundle entry is created through the raw endpoint, because what it sends is\n * a tarball it uploaded rather than arguments to a preset. Called without an\n * entry - as the import path does - it answers for a prompt.\n */\nexport function automationCreateEndpoint(\n entry?: SetupEntry,\n selectedAction?: string | null,\n): string {\n const kind = selectedActionKind(entry, selectedAction);\n if (kind === \"plugin\") return getAutomationEndpoint(\"createPlugin\");\n if (kind === \"upload\" || kind === \"bundle\") {\n return requireBundleEndpoint(\"createBundle\");\n }\n return getAutomationEndpoint(\"createPrompt\");\n}\n\n/** Where a bundle's tarball is uploaded, before the create call. */\nexport function automationUploadEndpoint(): string {\n return requireBundleEndpoint(\"uploads\");\n}\n\n/** The endpoints a bundle entry cannot be created without. */\nconst BUNDLE_ENDPOINTS = [\"createBundle\", \"uploads\"] as const;\n\n/**\n * An endpoint only a bundle needs. The interface manifest may predate bundles,\n * and the host holds no path of its own to fall back to, so this is where that\n * runs out rather than somewhere deep in a request.\n */\nfunction requireBundleEndpoint(\n name: (typeof BUNDLE_ENDPOINTS)[number],\n): string {\n const path = getAutomationEndpoint(name);\n if (!path) {\n throw new Error(\n `The published automation interface declares no '${name}' endpoint, ` +\n \"so this deployment cannot create an automation from a script bundle.\",\n );\n }\n return path;\n}\n\n/**\n * The endpoints this entry needs that the published interface does not declare.\n *\n * Asked before the form renders rather than discovered at the moment of\n * creating: a pinned package that predates bundles can never answer, and the\n * dialog can say so while nothing has been filled in yet. Empty for every\n * entry that is not a bundle, which needs nothing beyond what the block has\n * always declared.\n */\nfunction missingEndpointsForAction(kind: SetupActionKind | \"bundle\"): string[] {\n return kind === \"upload\" || kind === \"bundle\"\n ? BUNDLE_ENDPOINTS.filter((name) => !getAutomationEndpoint(name))\n : [];\n}\n\nexport function missingCreateEndpoints(\n entry: SetupEntry,\n selectedAction?: string | null,\n): string[] {\n if (isBundleEntry(entry)) return missingEndpointsForAction(\"bundle\");\n if (!entry.setup.actions) return [];\n\n if (selectedAction && selectedAction in entry.setup.actions) {\n return missingEndpointsForAction(selectedAction as SetupActionKind);\n }\n\n const missingByAction = actionKinds(entry.setup).map(\n missingEndpointsForAction,\n );\n if (missingByAction.some((missing) => missing.length === 0)) return [];\n return Array.from(new Set(missingByAction.flat()));\n}\n\n/** Whether this entry ships a script tarball instead of a prompt. */\nexport function isBundleEntry(entry: SetupEntry): boolean {\n return entry.setup.mode === \"direct\" && entry.setup.bundle !== undefined;\n}\n\nexport function isUploadAction(\n entry: SetupEntry,\n selectedAction?: string | null,\n): boolean {\n return selectedActionKind(entry, selectedAction) === \"upload\";\n}\n\nexport function selectedActionKind(\n entry: SetupEntry | undefined,\n selectedAction?: string | null,\n): SetupActionKind | \"bundle\" | \"prompt\" {\n if (!entry) return \"prompt\";\n if (isBundleEntry(entry)) return \"bundle\";\n const actions = entry.setup.actions ?? {};\n if (selectedAction && selectedAction in actions) {\n return selectedAction as SetupActionKind;\n }\n const keys = actionKinds(entry.setup);\n if (keys.length === 1) return keys[0];\n return \"prompt\";\n}\n\n/**\n * The `tarball_path` a preflight draft carries.\n *\n * Preflight runs on every field blur and the upload happens once, at submit,\n * so there is no real path to send yet. The service checks this field's scheme\n * at preflight and its ownership only at creation, so a well-formed stand-in\n * validates exactly what preflight is for - the rest of the body - without\n * uploading an archive per keystroke.\n */\nexport const PREFLIGHT_TARBALL_PATH =\n \"oh-internal://uploads/00000000-0000-0000-0000-000000000000\";\n\n/**\n * Trigger properties a form field may fill, per trigger kind. A field under a\n * trigger kind whose name is listed here fills the trigger property of the same\n * name; anything else there, such as a phrase to match, is an input to `filter`.\n */\nconst TRIGGER_PROPERTIES: Record<SetupTriggerKind, readonly string[]> = {\n cron: [\"schedule\", \"timezone\"],\n event: [\"source\", \"on\"],\n};\n\nconst OPTIONAL_CREATE_PROPERTIES = [\"model\", \"timeout\"] as const;\n\n/**\n * Repository properties a form field may fill, read the same way\n * {@link TRIGGER_PROPERTIES} is: a field whose name is listed here fills the\n * `repos[]` property of the same name. `url` and `provider` are absent because\n * neither is answered by a field of its own — they come from the repo-picker's\n * value and from its declared provider.\n *\n * These two lists are the only field names the derivation knows; everything\n * else it reads it finds by field type. An entry that wants a base branch in\n * its request therefore has to name that field `ref`.\n */\nconst REPO_PROPERTIES: readonly string[] = [\"ref\"];\n\nconst FORM_PLACEHOLDER_PATTERN = /\\{\\{form\\.([A-Za-z0-9_.]+)\\}\\}/g;\n\n/** The repository input, which supplies `repos` and an event trigger's source. */\nfunction findRepoPickerField(\n setup: SetupBlock,\n selectedAction?: string | null,\n) {\n const match = Object.entries(collectFields(setup, null, selectedAction)).find(\n ([, field]) => field.type === \"repo-picker\",\n );\n return match ? { name: match[0], field: match[1] } : null;\n}\n\n/** Every repository the form collected, whether the picker takes one or many. */\nfunction repositories(\n setup: SetupBlock,\n values: SetupFormValues,\n selectedAction?: string | null,\n): string[] {\n const picker = findRepoPickerField(setup, selectedAction);\n return picker ? fieldValues(values[picker.name]) : [];\n}\n\n/**\n * The created automation's name.\n *\n * One repository is worth naming; several are not, so the count stands in\n * rather than a list of names that would not fit.\n */\nfunction deriveName(\n entry: SetupEntry,\n values: SetupFormValues,\n selectedAction?: string | null,\n): string {\n if (\n \"name\" in collectFields(entry.setup, null, selectedAction) &&\n \"name\" in values\n ) {\n return fieldText(values.name);\n }\n\n const repos = repositories(entry.setup, values, selectedAction);\n if (repos.length === 0) return entry.name;\n if (repos.length === 1) return `${entry.name} - ${repos[0]}`;\n // The count is the one word here the host writes rather than reads off the\n // entry, so it is translated. There is no translator to pass in: the\n // derivation runs from a memo, an upload and a test alike.\n const count = i18n.t(I18nKey.SETUP$REPOSITORY_COUNT, {\n total: repos.length,\n });\n return `${entry.name} - ${count}`;\n}\n\n/** The active trigger kind for a direct entry, with its fields. */\nfunction getTrigger(setup: SetupBlock, selectedTrigger?: string | null) {\n const triggers = setup.form.triggers ?? {};\n const entries = Object.entries(triggers);\n if (entries.length === 0) return null;\n const match =\n selectedTrigger && selectedTrigger in triggers\n ? [selectedTrigger, triggers[selectedTrigger as SetupTriggerKind]]\n : entries.length === 1\n ? entries[0]\n : null;\n if (!match) return null;\n const [kind, fields] = match;\n return { kind: kind as SetupTriggerKind, fields: fields ?? {} };\n}\n\nfunction hasPayloadValue(\n value: SetupRequestBody[string] | undefined,\n): value is SetupRequestBody[string] {\n return value !== undefined && value !== null && value !== \"\";\n}\n\nfunction fieldPayloadValue(\n fieldType: string | undefined,\n value: SetupFormValues[string],\n) {\n if (fieldType === \"number\") {\n const text = fieldText(value);\n return text === \"\" ? undefined : Number(text);\n }\n return fieldText(value);\n}\n\nfunction optionalCreateProperties(\n setup: SetupBlock,\n values: SetupFormValues,\n selectedAction?: string | null,\n): SetupRequestBody {\n return Object.fromEntries(\n OPTIONAL_CREATE_PROPERTIES.flatMap((name) => {\n const field = collectFields(setup, null, selectedAction)[name];\n const value = fieldPayloadValue(field?.type, values[name]);\n return hasPayloadValue(value) ? [[name, value]] : [];\n }),\n ) as SetupRequestBody;\n}\n\nfunction repositoryProperty(\n setup: SetupBlock,\n values: SetupFormValues,\n selectedAction?: string | null,\n): SetupRequestBody {\n const repoPicker = findRepoPickerField(setup, selectedAction);\n const repos = repositories(setup, values, selectedAction);\n if (repos.length === 0 || !repoPicker?.field.provider) return {};\n\n const declared = REPO_PROPERTIES.filter((name) =>\n hasPayloadValue(fieldText(values[name])),\n );\n return {\n repos: repos.map((url) => ({\n url,\n ...Object.fromEntries(\n declared.map((name) => [name, fieldText(values[name])]),\n ),\n provider: repoPicker.field.provider as string,\n })),\n };\n}\n\nfunction parsePluginSources(\n value: SetupRequestBody[string],\n): SetupRequestBody[string] {\n if (!hasPayloadValue(value) || typeof value !== \"string\") return value;\n const trimmed = value.trim();\n if (!trimmed) return [];\n if (trimmed.startsWith(\"[\")) {\n try {\n return JSON.parse(trimmed) as SetupRequestBody[string];\n } catch {\n return [{ source: trimmed }];\n }\n }\n return trimmed\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => {\n const [source, ...parts] = line.split(/\\s+/);\n const plugin: Record<string, string> = { source };\n parts.forEach((part) => {\n const [key, ...raw] = part.split(\"=\");\n const valuePart = raw.join(\"=\");\n if ((key === \"ref\" || key === \"repo_path\") && valuePart) {\n plugin[key] = valuePart;\n }\n });\n return plugin;\n });\n}\n\n/**\n * The create request body these form values produce.\n *\n * No entry declares it: `name` comes from the entry, `repos` from the\n * repo-picker field and its declared provider, and `trigger` from the key and\n * fields under `form.triggers`. Only `prompt` and an event `filter` are\n * declared, because only they cannot be read off the form.\n *\n * Returns null for an assisted entry, which hands setup to a conversation\n * instead of sending a body.\n */\nexport function buildCreatePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n /** Bundle/upload entries only: what the upload returned. */\n tarballPath: string = PREFLIGHT_TARBALL_PATH,\n selectedTrigger?: string | null,\n selectedActionKey?: string | null,\n): SetupRequestBody | null {\n const { setup } = entry;\n if (setup.mode !== \"direct\") return null;\n if (setup.bundle) {\n return buildBundlePayload(entry, values, tarballPath, selectedTrigger);\n }\n if (setup.actions) {\n return buildActionPayload(\n entry,\n values,\n tarballPath,\n selectedTrigger,\n selectedActionKey,\n );\n }\n if (!setup.prompt) return null;\n\n const scope = { form: values, automation: entry };\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n prompt: interpolateText(setup.prompt, scope),\n ...optionalCreateProperties(setup, values),\n ...repositoryProperty(setup, values),\n };\n\n const trigger = buildTrigger(entry, values, selectedTrigger);\n if (trigger) payload.trigger = trigger;\n\n const template = buildTemplate(entry, values);\n if (template) payload.template = template;\n\n return payload;\n}\n\nfunction buildActionPayload(\n entry: SetupEntry,\n values: SetupFormValues,\n tarballPath: string,\n selectedTrigger?: string | null,\n selectedActionKey?: string | null,\n): SetupRequestBody | null {\n const kind = selectedActionKind(entry, selectedActionKey);\n if (kind === \"bundle\") return null;\n\n const setup = entry.setup;\n const scope = { form: values, automation: entry };\n const payload: SetupRequestBody = {\n name: deriveName(entry, values, selectedActionKey),\n };\n\n const trigger = buildTrigger(\n entry,\n values,\n selectedTrigger,\n selectedActionKey,\n );\n if (trigger) payload.trigger = trigger;\n\n Object.assign(\n payload,\n optionalCreateProperties(setup, values, selectedActionKey),\n );\n\n if (kind === \"upload\") {\n const action = setup.actions?.upload;\n if (!action) return null;\n payload.tarball_path =\n tarballPath || interpolateText(action.tarballPath, scope);\n payload.entrypoint = interpolateText(action.entrypoint, scope);\n if (action.setupScript) {\n const setupScript = interpolateText(action.setupScript, scope);\n if (hasPayloadValue(setupScript)) payload.setup_script_path = setupScript;\n }\n return payload;\n }\n\n const action =\n kind === \"plugin\" ? setup.actions?.plugin : setup.actions?.prompt;\n if (!action) return null;\n payload.prompt = interpolateText(action.prompt, scope);\n Object.assign(payload, repositoryProperty(setup, values, selectedActionKey));\n\n if (kind === \"plugin\") {\n const pluginAction = setup.actions?.plugin;\n if (!pluginAction) return null;\n payload.plugins = parsePluginSources(\n interpolateValue(pluginAction.plugins, scope),\n );\n }\n\n const template = buildTemplate(entry, values);\n if (template) payload.template = template;\n\n return payload;\n}\n\n/**\n * The `template` provenance a prompt entry sends, or undefined for one that\n * publishes no version.\n *\n * It is what lets the service recognise a second setup of the same entry as\n * the automation it already holds. The bundle path derives the same block from\n * the config the bundle declares; a prompt entry declares none, so the answer\n * to \"what was this created from\" is the form as submitted.\n */\nfunction buildTemplate(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | undefined {\n if (!entry.version) return undefined;\n return {\n id: entry.id,\n version: entry.version,\n config: { ...values } as SetupRequestBody,\n };\n}\n\n/**\n * The `trigger` object, read off the key and fields under `form.triggers`.\n *\n * Identical for both kinds of direct entry: only the create endpoint and what\n * the automation is told to do differ between a prompt and a bundle.\n */\nfunction buildTrigger(\n entry: SetupEntry,\n values: SetupFormValues,\n selectedTrigger?: string | null,\n selectedAction?: string | null,\n): SetupRequestBody | undefined {\n const trigger = getTrigger(entry.setup, selectedTrigger);\n if (!trigger) return undefined;\n\n const properties = TRIGGER_PROPERTIES[trigger.kind];\n const declared = Object.keys(trigger.fields).filter((name) =>\n properties.includes(name),\n );\n const repoPicker = findRepoPickerField(entry.setup, selectedAction);\n const derived = Object.fromEntries(\n declared\n .map((name) => [name, fieldText(values[name])])\n .filter(([, value]) => hasPayloadValue(value)),\n );\n\n const filter = entry.setup.filter\n ? interpolateText(entry.setup.filter, {\n form: values,\n automation: entry,\n })\n : undefined;\n\n return {\n type: trigger.kind,\n ...derived,\n ...(trigger.kind === \"event\" && {\n ...(!(\"source\" in derived) && {\n source: repoPicker?.field.provider ?? \"\",\n }),\n ...(hasPayloadValue(filter) && { filter }),\n }),\n };\n}\n\n/**\n * The raw create body a bundle entry produces.\n *\n * `tarball_path` is the one value neither declared nor derived: the host packs\n * and uploads the bundle first, and creates from what came back. `template` is\n * the provenance that makes enabling the same entry twice return the\n * automation that already exists rather than a second one.\n *\n * There is no `repos`: the raw endpoint has no such field, and a bundle's\n * script fetches what it needs itself.\n */\nfunction buildBundlePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n tarballPath: string,\n selectedTrigger?: string | null,\n): SetupRequestBody {\n const bundle = entry.setup.bundle!;\n const scope = { form: values, automation: entry };\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n };\n\n const trigger = buildTrigger(entry, values, selectedTrigger);\n if (trigger) payload.trigger = trigger;\n\n payload.tarball_path = tarballPath;\n payload.entrypoint = bundle.entrypoint;\n if (bundle.setupScript) payload.setup_script_path = bundle.setupScript;\n if (bundle.timeout !== undefined) payload.timeout = bundle.timeout;\n payload.template = {\n id: entry.id,\n version: bundle.version,\n config: interpolateConfig(bundle.config, scope) as SetupRequestBody,\n };\n\n return payload;\n}\n\n/**\n * Placeholder substitution over the config tree. Only string leaves are\n * templated; a number, a boolean or a null is written through as itself, so an\n * entry can state a value the script reads as the type it expects.\n */\nfunction interpolateConfig(\n node: SetupBundleConfigValue,\n scope: Parameters<typeof interpolateText>[1],\n): SetupBundleConfigValue {\n if (typeof node === \"string\") {\n return interpolateValue(node, scope);\n }\n if (Array.isArray(node)) {\n return node.map((item) => interpolateConfig(item, scope));\n }\n if (typeof node === \"object\" && node !== null) {\n return Object.fromEntries(\n Object.entries(node).map(([key, value]) => [\n key,\n interpolateConfig(value, scope),\n ]),\n );\n }\n return node;\n}\n\n/**\n * The preflight body the host sends. The same shape for every entry, so no\n * entry declares it.\n */\nexport function buildPreflightBody(\n entry: SetupEntry,\n values: SetupFormValues,\n selectedTrigger?: string | null,\n selectedAction?: string | null,\n): SetupRequestBody | null {\n const draft = buildCreatePayload(\n entry,\n values,\n PREFLIGHT_TARBALL_PATH,\n selectedTrigger,\n selectedAction,\n );\n if (!draft) return null;\n\n return {\n automationId: entry.id,\n endpoint: automationCreateEndpoint(entry, selectedAction),\n draft,\n };\n}\n\n/**\n * What an assisted entry sends into the conversation that finishes setup.\n *\n * The command is not declared either: it lives once, in the owning skill's own\n * `triggers` frontmatter, and the skill defaults to the entry's id. An entry\n * whose skill is invoked by description rather than by command contributes\n * nothing here, and the answers alone open the conversation.\n */\nexport function buildAssistedMessage(\n entry: SetupEntry,\n values: SetupFormValues,\n): string {\n const command = findAutomationCommand(entry);\n const message = entry.setup.message\n ? interpolateText(entry.setup.message, { form: values, automation: entry })\n : \"\";\n\n return [command, message].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction collectPlaceholderFields(value: string): string[] {\n const names = Array.from(\n value.matchAll(FORM_PLACEHOLDER_PATTERN),\n (match) => match[1],\n );\n return Array.from(new Set(names));\n}\n\n/**\n * Which form fields built each payload path.\n *\n * Preflight and the create endpoint reject a draft by payload path, and the\n * host has to turn that back into a highlighted input. Building the body with\n * each field standing in for its own value recovers the mapping exactly, so an\n * entry does not declare it.\n */\nexport function deriveErrorMap(\n entry: SetupEntry,\n selectedTrigger?: string | null,\n selectedAction?: string | null,\n): Record<string, string[]> {\n const mapping: Record<string, string[]> = {};\n\n const triggerEntries = Object.keys(entry.setup.form.triggers ?? {});\n const triggerVariants = selectedTrigger\n ? [selectedTrigger]\n : triggerEntries.length > 1\n ? triggerEntries\n : [null];\n\n const actionEntries = actionKinds(entry.setup);\n const actionVariants = selectedAction\n ? [selectedAction]\n : actionEntries.length > 1\n ? actionEntries\n : [null];\n\n const walk = (node: unknown, path: string): void => {\n if (Array.isArray(node)) {\n node.forEach((item, index) => walk(item, `${path}[${index}]`));\n return;\n }\n if (typeof node === \"object\" && node !== null) {\n Object.entries(node).forEach(([key, item]) =>\n walk(item, path ? `${path}.${key}` : key),\n );\n return;\n }\n if (typeof node === \"string\") {\n const names = collectPlaceholderFields(node);\n if (names.length > 0) {\n mapping[path] = Array.from(\n new Set([...(mapping[path] ?? []), ...names]),\n );\n }\n }\n };\n\n actionVariants.forEach((actionVariant) => {\n triggerVariants.forEach((triggerVariant) => {\n const names = Object.keys(\n collectFields(entry.setup, triggerVariant, actionVariant),\n );\n const variantStandIns = Object.fromEntries(\n names.map((name) => [name, `{{form.${name}}}`]),\n );\n const template = buildCreatePayload(\n entry,\n variantStandIns,\n actionVariant === \"upload\" ? \"\" : PREFLIGHT_TARBALL_PATH,\n triggerVariant,\n actionVariant,\n );\n if (template) walk(template, \"\");\n });\n });\n return mapping;\n}\n"],"mappings":"gNA6CA,SAAgB,EACd,EACA,EACQ,CACR,IAAM,EAAO,EAAmB,EAAO,CAAc,EAKrD,OAJI,IAAS,SAAiB,EAAA,sBAAsB,cAAc,EAC9D,IAAS,UAAY,IAAS,SACzB,EAAsB,cAAc,EAEtC,EAAA,sBAAsB,cAAc,CAC7C,CAGA,SAAgB,GAAmC,CACjD,OAAO,EAAsB,SAAS,CACxC,CAUA,SAAS,EACP,EACQ,CACR,IAAM,EAAO,EAAA,sBAAsB,CAAI,EACvC,GAAI,CAAC,EACH,MAAU,MACR,mDAAmD,EAAK,iFAE1D,EAEF,OAAO,CACT,CAoCA,SAAgB,EAAc,EAA4B,CACxD,OAAO,EAAM,MAAM,OAAS,UAAY,EAAM,MAAM,SAAW,IAAA,EACjE,CASA,SAAgB,EACd,EACA,EACuC,CACvC,GAAI,CAAC,EAAO,MAAO,SACnB,GAAI,EAAc,CAAK,EAAG,MAAO,SACjC,IAAM,EAAU,EAAM,MAAM,SAAW,CAAC,EACxC,GAAI,GAAkB,KAAkB,EACtC,OAAO,EAET,IAAM,EAAO,EAAA,YAAY,EAAM,KAAK,EAEpC,OADI,EAAK,SAAW,EAAU,EAAK,GAC5B,QACT"}
@@ -12,7 +12,7 @@
12
12
  * were verified against the live service. The create model is `extra="forbid"`,
13
13
  * so any divergence is a hard 422 rather than a dropped field.
14
14
  */
15
- import type { SetupEntry, SetupFormValues, SetupRequestBody } from "./types";
15
+ import type { SetupActionKind, SetupEntry, SetupFormValues, SetupRequestBody } from "./types";
16
16
  /**
17
17
  * The creation endpoint a derived draft would be posted to. Resolved on call
18
18
  * rather than at import, because the endpoint is the interface manifest's and
@@ -22,21 +22,14 @@ import type { SetupEntry, SetupFormValues, SetupRequestBody } from "./types";
22
22
  * a tarball it uploaded rather than arguments to a preset. Called without an
23
23
  * entry - as the import path does - it answers for a prompt.
24
24
  */
25
- export declare function automationCreateEndpoint(entry?: SetupEntry): string;
25
+ export declare function automationCreateEndpoint(entry?: SetupEntry, selectedAction?: string | null): string;
26
26
  /** Where a bundle's tarball is uploaded, before the create call. */
27
27
  export declare function automationUploadEndpoint(): string;
28
- /**
29
- * The endpoints this entry needs that the published interface does not declare.
30
- *
31
- * Asked before the form renders rather than discovered at the moment of
32
- * creating: a pinned package that predates bundles can never answer, and the
33
- * dialog can say so while nothing has been filled in yet. Empty for every
34
- * entry that is not a bundle, which needs nothing beyond what the block has
35
- * always declared.
36
- */
37
- export declare function missingCreateEndpoints(entry: SetupEntry): string[];
28
+ export declare function missingCreateEndpoints(entry: SetupEntry, selectedAction?: string | null): string[];
38
29
  /** Whether this entry ships a script tarball instead of a prompt. */
39
30
  export declare function isBundleEntry(entry: SetupEntry): boolean;
31
+ export declare function isUploadAction(entry: SetupEntry, selectedAction?: string | null): boolean;
32
+ export declare function selectedActionKind(entry: SetupEntry | undefined, selectedAction?: string | null): SetupActionKind | "bundle" | "prompt";
40
33
  /**
41
34
  * The `tarball_path` a preflight draft carries.
42
35
  *
@@ -59,13 +52,13 @@ export declare const PREFLIGHT_TARBALL_PATH = "oh-internal://uploads/00000000-00
59
52
  * instead of sending a body.
60
53
  */
61
54
  export declare function buildCreatePayload(entry: SetupEntry, values: SetupFormValues,
62
- /** Bundle entries only: what the upload returned. */
63
- tarballPath?: string): SetupRequestBody | null;
55
+ /** Bundle/upload entries only: what the upload returned. */
56
+ tarballPath?: string, selectedTrigger?: string | null, selectedActionKey?: string | null): SetupRequestBody | null;
64
57
  /**
65
58
  * The preflight body the host sends. The same shape for every entry, so no
66
59
  * entry declares it.
67
60
  */
68
- export declare function buildPreflightBody(entry: SetupEntry, values: SetupFormValues): SetupRequestBody | null;
61
+ export declare function buildPreflightBody(entry: SetupEntry, values: SetupFormValues, selectedTrigger?: string | null, selectedAction?: string | null): SetupRequestBody | null;
69
62
  /**
70
63
  * What an assisted entry sends into the conversation that finishes setup.
71
64
  *
@@ -83,4 +76,4 @@ export declare function buildAssistedMessage(entry: SetupEntry, values: SetupFor
83
76
  * each field standing in for its own value recovers the mapping exactly, so an
84
77
  * entry does not declare it.
85
78
  */
86
- export declare function deriveErrorMap(entry: SetupEntry): Record<string, string[]>;
79
+ export declare function deriveErrorMap(entry: SetupEntry, selectedTrigger?: string | null, selectedAction?: string | null): Record<string, string[]>;
@@ -1,22 +1,32 @@
1
1
  import "../i18n/index.js";
2
2
  import "../utils/automation-catalog.js";
3
3
  import { getAutomationEndpoint as e } from "./automation-interface.js";
4
+ import { actionKinds as t } from "./manifest-local-validation.js";
4
5
  //#region src/manifests/automation-setup.ts
5
- function t(t) {
6
- return t && i(t) ? r("createBundle") : e("createPrompt");
6
+ function n(t, n) {
7
+ let r = o(t, n);
8
+ return r === "plugin" ? e("createPlugin") : r === "upload" || r === "bundle" ? i("createBundle") : e("createPrompt");
7
9
  }
8
- function n() {
9
- return r("uploads");
10
+ function r() {
11
+ return i("uploads");
10
12
  }
11
- function r(t) {
13
+ function i(t) {
12
14
  let n = e(t);
13
15
  if (!n) throw Error(`The published automation interface declares no '${t}' endpoint, so this deployment cannot create an automation from a script bundle.`);
14
16
  return n;
15
17
  }
16
- function i(e) {
18
+ function a(e) {
17
19
  return e.setup.mode === "direct" && e.setup.bundle !== void 0;
18
20
  }
21
+ function o(e, n) {
22
+ if (!e) return "prompt";
23
+ if (a(e)) return "bundle";
24
+ let r = e.setup.actions ?? {};
25
+ if (n && n in r) return n;
26
+ let i = t(e.setup);
27
+ return i.length === 1 ? i[0] : "prompt";
28
+ }
19
29
  //#endregion
20
- export { t as automationCreateEndpoint, n as automationUploadEndpoint };
30
+ export { n as automationCreateEndpoint, r as automationUploadEndpoint };
21
31
 
22
32
  //# sourceMappingURL=automation-setup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"automation-setup.js","names":[],"sources":["../../src/manifests/automation-setup.ts"],"sourcesContent":["/**\n * The only module that knows a setup entry configures an *automation*.\n *\n * The published contract states just what varies between entries; everything\n * derivable is the host's to generate. That derivation is here, and nowhere\n * else, so the rest of `src/manifests/` stays about forms rather than about\n * automations.\n *\n * The algorithm mirrors `tests/test_automation_setup.py::_render_payload` in\n * `OpenHands/extensions`, which is the authoritative reference: it produces the\n * request bodies published in that repository's contract fixtures, and those\n * were verified against the live service. The create model is `extra=\"forbid\"`,\n * so any divergence is a hard 422 rather than a dropped field.\n */\n\nimport i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { findAutomationCommand } from \"#/utils/automation-catalog\";\nimport { getAutomationEndpoint } from \"./automation-interface\";\nimport {\n collectFields,\n fieldText,\n fieldValues,\n} from \"./manifest-local-validation\";\nimport { interpolateText, interpolateValue } from \"./manifest-template\";\nimport type {\n SetupBlock,\n SetupBundleConfigValue,\n SetupEntry,\n SetupFormValues,\n SetupRequestBody,\n SetupTriggerKind,\n} from \"./types\";\n\n/**\n * The creation endpoint a derived draft would be posted to. Resolved on call\n * rather than at import, because the endpoint is the interface manifest's and\n * this module loads whether or not one was admitted.\n *\n * A bundle entry is created through the raw endpoint, because what it sends is\n * a tarball it uploaded rather than arguments to a preset. Called without an\n * entry - as the import path does - it answers for a prompt.\n */\nexport function automationCreateEndpoint(entry?: SetupEntry): string {\n if (entry && isBundleEntry(entry)) {\n return requireBundleEndpoint(\"createBundle\");\n }\n return getAutomationEndpoint(\"createPrompt\");\n}\n\n/** Where a bundle's tarball is uploaded, before the create call. */\nexport function automationUploadEndpoint(): string {\n return requireBundleEndpoint(\"uploads\");\n}\n\n/** The endpoints a bundle entry cannot be created without. */\nconst BUNDLE_ENDPOINTS = [\"createBundle\", \"uploads\"] as const;\n\n/**\n * An endpoint only a bundle needs. The interface manifest may predate bundles,\n * and the host holds no path of its own to fall back to, so this is where that\n * runs out rather than somewhere deep in a request.\n */\nfunction requireBundleEndpoint(\n name: (typeof BUNDLE_ENDPOINTS)[number],\n): string {\n const path = getAutomationEndpoint(name);\n if (!path) {\n throw new Error(\n `The published automation interface declares no '${name}' endpoint, ` +\n \"so this deployment cannot create an automation from a script bundle.\",\n );\n }\n return path;\n}\n\n/**\n * The endpoints this entry needs that the published interface does not declare.\n *\n * Asked before the form renders rather than discovered at the moment of\n * creating: a pinned package that predates bundles can never answer, and the\n * dialog can say so while nothing has been filled in yet. Empty for every\n * entry that is not a bundle, which needs nothing beyond what the block has\n * always declared.\n */\nexport function missingCreateEndpoints(entry: SetupEntry): string[] {\n if (!isBundleEntry(entry)) return [];\n return BUNDLE_ENDPOINTS.filter((name) => !getAutomationEndpoint(name));\n}\n\n/** Whether this entry ships a script tarball instead of a prompt. */\nexport function isBundleEntry(entry: SetupEntry): boolean {\n return entry.setup.mode === \"direct\" && entry.setup.bundle !== undefined;\n}\n\n/**\n * The `tarball_path` a preflight draft carries.\n *\n * Preflight runs on every field blur and the upload happens once, at submit,\n * so there is no real path to send yet. The service checks this field's scheme\n * at preflight and its ownership only at creation, so a well-formed stand-in\n * validates exactly what preflight is for - the rest of the body - without\n * uploading an archive per keystroke.\n */\nexport const PREFLIGHT_TARBALL_PATH =\n \"oh-internal://uploads/00000000-0000-0000-0000-000000000000\";\n\n/**\n * Trigger properties a form field may fill, per trigger kind. A field under a\n * trigger kind whose name is listed here fills the trigger property of the same\n * name; anything else there, such as a phrase to match, is an input to `filter`.\n */\nconst TRIGGER_PROPERTIES: Record<SetupTriggerKind, readonly string[]> = {\n cron: [\"schedule\", \"timezone\"],\n event: [\"on\"],\n};\n\n/**\n * Repository properties a form field may fill, read the same way\n * {@link TRIGGER_PROPERTIES} is: a field whose name is listed here fills the\n * `repos[]` property of the same name. `url` and `provider` are absent because\n * neither is answered by a field of its own — they come from the repo-picker's\n * value and from its declared provider.\n *\n * These two lists are the only field names the derivation knows; everything\n * else it reads it finds by field type. An entry that wants a base branch in\n * its request therefore has to name that field `ref`.\n */\nconst REPO_PROPERTIES: readonly string[] = [\"ref\"];\n\nconst FORM_PLACEHOLDER_PATTERN = /\\{\\{form\\.([A-Za-z0-9_.]+)\\}\\}/g;\n\n/** The repository input, which supplies `repos` and an event trigger's source. */\nfunction findRepoPickerField(setup: SetupBlock) {\n const match = Object.entries(collectFields(setup)).find(\n ([, field]) => field.type === \"repo-picker\",\n );\n return match ? { name: match[0], field: match[1] } : null;\n}\n\n/** Every repository the form collected, whether the picker takes one or many. */\nfunction repositories(setup: SetupBlock, values: SetupFormValues): string[] {\n const picker = findRepoPickerField(setup);\n return picker ? fieldValues(values[picker.name]) : [];\n}\n\n/**\n * The created automation's name.\n *\n * One repository is worth naming; several are not, so the count stands in\n * rather than a list of names that would not fit.\n */\nfunction deriveName(entry: SetupEntry, values: SetupFormValues): string {\n const repos = repositories(entry.setup, values);\n if (repos.length === 0) return entry.name;\n if (repos.length === 1) return `${entry.name} - ${repos[0]}`;\n // The count is the one word here the host writes rather than reads off the\n // entry, so it is translated. There is no translator to pass in: the\n // derivation runs from a memo, an upload and a test alike.\n const count = i18n.t(I18nKey.SETUP$REPOSITORY_COUNT, {\n total: repos.length,\n });\n return `${entry.name} - ${count}`;\n}\n\n/** The single trigger kind a direct entry declares, with its fields. */\nfunction getTrigger(setup: SetupBlock) {\n const entries = Object.entries(setup.form.triggers ?? {});\n if (entries.length === 0) return null;\n const [kind, fields] = entries[0];\n return { kind: kind as SetupTriggerKind, fields: fields ?? {} };\n}\n\n/**\n * The create request body these form values produce.\n *\n * No entry declares it: `name` comes from the entry, `repos` from the\n * repo-picker field and its declared provider, and `trigger` from the key and\n * fields under `form.triggers`. Only `prompt` and an event `filter` are\n * declared, because only they cannot be read off the form.\n *\n * Returns null for an assisted entry, which hands setup to a conversation\n * instead of sending a body.\n */\nexport function buildCreatePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n /** Bundle entries only: what the upload returned. */\n tarballPath: string = PREFLIGHT_TARBALL_PATH,\n): SetupRequestBody | null {\n const { setup } = entry;\n if (setup.mode !== \"direct\") return null;\n if (setup.bundle) return buildBundlePayload(entry, values, tarballPath);\n if (!setup.prompt) return null;\n\n const scope = { form: values, automation: entry };\n const repoPicker = findRepoPickerField(setup);\n const repos = repositories(setup, values);\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n prompt: interpolateText(setup.prompt, scope),\n };\n\n if (repos.length > 0 && repoPicker?.field.provider) {\n const declared = REPO_PROPERTIES.filter((name) => name in values);\n payload.repos = repos.map((url) => ({\n url,\n ...Object.fromEntries(\n declared.map((name) => [name, fieldText(values[name])]),\n ),\n provider: repoPicker.field.provider as string,\n }));\n }\n\n // A filter is optional: an entry that declares none accepts every delivered\n // event, so the key is left off rather than sent empty.\n const trigger = buildTrigger(entry, values);\n if (trigger) payload.trigger = trigger;\n\n const template = buildTemplate(entry, values);\n if (template) payload.template = template;\n\n return payload;\n}\n\n/**\n * The `template` provenance a prompt entry sends, or undefined for one that\n * publishes no version.\n *\n * It is what lets the service recognise a second setup of the same entry as\n * the automation it already holds. The bundle path derives the same block from\n * the config the bundle declares; a prompt entry declares none, so the answer\n * to \"what was this created from\" is the form as submitted.\n */\nfunction buildTemplate(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | undefined {\n if (!entry.version) return undefined;\n return {\n id: entry.id,\n version: entry.version,\n config: { ...values },\n };\n}\n\n/**\n * The `trigger` object, read off the key and fields under `form.triggers`.\n *\n * Identical for both kinds of direct entry: only the create endpoint and what\n * the automation is told to do differ between a prompt and a bundle.\n */\nfunction buildTrigger(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | undefined {\n const trigger = getTrigger(entry.setup);\n if (!trigger) return undefined;\n\n const properties = TRIGGER_PROPERTIES[trigger.kind];\n const declared = Object.keys(trigger.fields).filter((name) =>\n properties.includes(name),\n );\n const repoPicker = findRepoPickerField(entry.setup);\n\n return {\n type: trigger.kind,\n ...Object.fromEntries(\n declared.map((name) => [name, fieldText(values[name])]),\n ),\n ...(trigger.kind === \"event\" && {\n source: repoPicker?.field.provider ?? \"\",\n ...(entry.setup.filter && {\n filter: interpolateText(entry.setup.filter, {\n form: values,\n automation: entry,\n }),\n }),\n }),\n };\n}\n\n/**\n * The raw create body a bundle entry produces.\n *\n * `tarball_path` is the one value neither declared nor derived: the host packs\n * and uploads the bundle first, and creates from what came back. `template` is\n * the provenance that makes enabling the same entry twice return the\n * automation that already exists rather than a second one.\n *\n * There is no `repos`: the raw endpoint has no such field, and a bundle's\n * script fetches what it needs itself.\n */\nfunction buildBundlePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n tarballPath: string,\n): SetupRequestBody {\n const bundle = entry.setup.bundle!;\n const scope = { form: values, automation: entry };\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n };\n\n const trigger = buildTrigger(entry, values);\n if (trigger) payload.trigger = trigger;\n\n payload.tarball_path = tarballPath;\n payload.entrypoint = bundle.entrypoint;\n if (bundle.setupScript) payload.setup_script_path = bundle.setupScript;\n if (bundle.timeout !== undefined) payload.timeout = bundle.timeout;\n payload.template = {\n id: entry.id,\n version: bundle.version,\n config: interpolateConfig(bundle.config, scope) as SetupRequestBody,\n };\n\n return payload;\n}\n\n/**\n * Placeholder substitution over the config tree. Only string leaves are\n * templated; a number, a boolean or a null is written through as itself, so an\n * entry can state a value the script reads as the type it expects.\n */\nfunction interpolateConfig(\n node: SetupBundleConfigValue,\n scope: Parameters<typeof interpolateText>[1],\n): SetupBundleConfigValue {\n if (typeof node === \"string\") {\n return interpolateValue(node, scope);\n }\n if (Array.isArray(node)) {\n return node.map((item) => interpolateConfig(item, scope));\n }\n if (typeof node === \"object\" && node !== null) {\n return Object.fromEntries(\n Object.entries(node).map(([key, value]) => [\n key,\n interpolateConfig(value, scope),\n ]),\n );\n }\n return node;\n}\n\n/**\n * The preflight body the host sends. The same shape for every entry, so no\n * entry declares it.\n */\nexport function buildPreflightBody(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | null {\n const draft = buildCreatePayload(entry, values);\n if (!draft) return null;\n\n return {\n automationId: entry.id,\n endpoint: automationCreateEndpoint(entry),\n draft,\n };\n}\n\n/**\n * What an assisted entry sends into the conversation that finishes setup.\n *\n * The command is not declared either: it lives once, in the owning skill's own\n * `triggers` frontmatter, and the skill defaults to the entry's id. An entry\n * whose skill is invoked by description rather than by command contributes\n * nothing here, and the answers alone open the conversation.\n */\nexport function buildAssistedMessage(\n entry: SetupEntry,\n values: SetupFormValues,\n): string {\n const command = findAutomationCommand(entry);\n const message = entry.setup.message\n ? interpolateText(entry.setup.message, { form: values, automation: entry })\n : \"\";\n\n return [command, message].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction collectPlaceholderFields(value: string): string[] {\n const names = Array.from(\n value.matchAll(FORM_PLACEHOLDER_PATTERN),\n (match) => match[1],\n );\n return Array.from(new Set(names));\n}\n\n/**\n * Which form fields built each payload path.\n *\n * Preflight and the create endpoint reject a draft by payload path, and the\n * host has to turn that back into a highlighted input. Building the body with\n * each field standing in for its own value recovers the mapping exactly, so an\n * entry does not declare it.\n */\nexport function deriveErrorMap(entry: SetupEntry): Record<string, string[]> {\n const mapping: Record<string, string[]> = {};\n\n const standIns = Object.fromEntries(\n Object.keys(collectFields(entry.setup)).map((name) => [\n name,\n `{{form.${name}}}`,\n ]),\n );\n const template = buildCreatePayload(entry, standIns);\n if (!template) return mapping;\n\n const walk = (node: unknown, path: string): void => {\n if (Array.isArray(node)) {\n node.forEach((item, index) => walk(item, `${path}[${index}]`));\n return;\n }\n if (typeof node === \"object\" && node !== null) {\n Object.entries(node).forEach(([key, item]) =>\n walk(item, path ? `${path}.${key}` : key),\n );\n return;\n }\n if (typeof node === \"string\") {\n const names = collectPlaceholderFields(node);\n if (names.length > 0) mapping[path] = names;\n }\n };\n\n walk(template, \"\");\n return mapping;\n}\n"],"mappings":";;;;AA2CA,SAAgB,EAAyB,GAA4B;CAInE,OAHI,KAAS,EAAc,CAAK,IACvB,EAAsB,cAAc,IAEtC,EAAsB,cAAc;AAC7C;AAGA,SAAgB,IAAmC;CACjD,OAAO,EAAsB,SAAS;AACxC;AAUA,SAAS,EACP,GACQ;CACR,IAAM,IAAO,EAAsB,CAAI;CACvC,IAAI,CAAC,GACH,MAAU,MACR,mDAAmD,EAAK,iFAE1D;CAEF,OAAO;AACT;AAiBA,SAAgB,EAAc,GAA4B;CACxD,OAAO,EAAM,MAAM,SAAS,YAAY,EAAM,MAAM,WAAW,KAAA;AACjE"}
1
+ {"version":3,"file":"automation-setup.js","names":[],"sources":["../../src/manifests/automation-setup.ts"],"sourcesContent":["/**\n * The only module that knows a setup entry configures an *automation*.\n *\n * The published contract states just what varies between entries; everything\n * derivable is the host's to generate. That derivation is here, and nowhere\n * else, so the rest of `src/manifests/` stays about forms rather than about\n * automations.\n *\n * The algorithm mirrors `tests/test_automation_setup.py::_render_payload` in\n * `OpenHands/extensions`, which is the authoritative reference: it produces the\n * request bodies published in that repository's contract fixtures, and those\n * were verified against the live service. The create model is `extra=\"forbid\"`,\n * so any divergence is a hard 422 rather than a dropped field.\n */\n\nimport i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { findAutomationCommand } from \"#/utils/automation-catalog\";\nimport { getAutomationEndpoint } from \"./automation-interface\";\nimport {\n actionKinds,\n collectFields,\n fieldText,\n fieldValues,\n} from \"./manifest-local-validation\";\nimport { interpolateText, interpolateValue } from \"./manifest-template\";\nimport type {\n SetupActionKind,\n SetupBlock,\n SetupBundleConfigValue,\n SetupEntry,\n SetupFormValues,\n SetupRequestBody,\n SetupTriggerKind,\n} from \"./types\";\n\n/**\n * The creation endpoint a derived draft would be posted to. Resolved on call\n * rather than at import, because the endpoint is the interface manifest's and\n * this module loads whether or not one was admitted.\n *\n * A bundle entry is created through the raw endpoint, because what it sends is\n * a tarball it uploaded rather than arguments to a preset. Called without an\n * entry - as the import path does - it answers for a prompt.\n */\nexport function automationCreateEndpoint(\n entry?: SetupEntry,\n selectedAction?: string | null,\n): string {\n const kind = selectedActionKind(entry, selectedAction);\n if (kind === \"plugin\") return getAutomationEndpoint(\"createPlugin\");\n if (kind === \"upload\" || kind === \"bundle\") {\n return requireBundleEndpoint(\"createBundle\");\n }\n return getAutomationEndpoint(\"createPrompt\");\n}\n\n/** Where a bundle's tarball is uploaded, before the create call. */\nexport function automationUploadEndpoint(): string {\n return requireBundleEndpoint(\"uploads\");\n}\n\n/** The endpoints a bundle entry cannot be created without. */\nconst BUNDLE_ENDPOINTS = [\"createBundle\", \"uploads\"] as const;\n\n/**\n * An endpoint only a bundle needs. The interface manifest may predate bundles,\n * and the host holds no path of its own to fall back to, so this is where that\n * runs out rather than somewhere deep in a request.\n */\nfunction requireBundleEndpoint(\n name: (typeof BUNDLE_ENDPOINTS)[number],\n): string {\n const path = getAutomationEndpoint(name);\n if (!path) {\n throw new Error(\n `The published automation interface declares no '${name}' endpoint, ` +\n \"so this deployment cannot create an automation from a script bundle.\",\n );\n }\n return path;\n}\n\n/**\n * The endpoints this entry needs that the published interface does not declare.\n *\n * Asked before the form renders rather than discovered at the moment of\n * creating: a pinned package that predates bundles can never answer, and the\n * dialog can say so while nothing has been filled in yet. Empty for every\n * entry that is not a bundle, which needs nothing beyond what the block has\n * always declared.\n */\nfunction missingEndpointsForAction(kind: SetupActionKind | \"bundle\"): string[] {\n return kind === \"upload\" || kind === \"bundle\"\n ? BUNDLE_ENDPOINTS.filter((name) => !getAutomationEndpoint(name))\n : [];\n}\n\nexport function missingCreateEndpoints(\n entry: SetupEntry,\n selectedAction?: string | null,\n): string[] {\n if (isBundleEntry(entry)) return missingEndpointsForAction(\"bundle\");\n if (!entry.setup.actions) return [];\n\n if (selectedAction && selectedAction in entry.setup.actions) {\n return missingEndpointsForAction(selectedAction as SetupActionKind);\n }\n\n const missingByAction = actionKinds(entry.setup).map(\n missingEndpointsForAction,\n );\n if (missingByAction.some((missing) => missing.length === 0)) return [];\n return Array.from(new Set(missingByAction.flat()));\n}\n\n/** Whether this entry ships a script tarball instead of a prompt. */\nexport function isBundleEntry(entry: SetupEntry): boolean {\n return entry.setup.mode === \"direct\" && entry.setup.bundle !== undefined;\n}\n\nexport function isUploadAction(\n entry: SetupEntry,\n selectedAction?: string | null,\n): boolean {\n return selectedActionKind(entry, selectedAction) === \"upload\";\n}\n\nexport function selectedActionKind(\n entry: SetupEntry | undefined,\n selectedAction?: string | null,\n): SetupActionKind | \"bundle\" | \"prompt\" {\n if (!entry) return \"prompt\";\n if (isBundleEntry(entry)) return \"bundle\";\n const actions = entry.setup.actions ?? {};\n if (selectedAction && selectedAction in actions) {\n return selectedAction as SetupActionKind;\n }\n const keys = actionKinds(entry.setup);\n if (keys.length === 1) return keys[0];\n return \"prompt\";\n}\n\n/**\n * The `tarball_path` a preflight draft carries.\n *\n * Preflight runs on every field blur and the upload happens once, at submit,\n * so there is no real path to send yet. The service checks this field's scheme\n * at preflight and its ownership only at creation, so a well-formed stand-in\n * validates exactly what preflight is for - the rest of the body - without\n * uploading an archive per keystroke.\n */\nexport const PREFLIGHT_TARBALL_PATH =\n \"oh-internal://uploads/00000000-0000-0000-0000-000000000000\";\n\n/**\n * Trigger properties a form field may fill, per trigger kind. A field under a\n * trigger kind whose name is listed here fills the trigger property of the same\n * name; anything else there, such as a phrase to match, is an input to `filter`.\n */\nconst TRIGGER_PROPERTIES: Record<SetupTriggerKind, readonly string[]> = {\n cron: [\"schedule\", \"timezone\"],\n event: [\"source\", \"on\"],\n};\n\nconst OPTIONAL_CREATE_PROPERTIES = [\"model\", \"timeout\"] as const;\n\n/**\n * Repository properties a form field may fill, read the same way\n * {@link TRIGGER_PROPERTIES} is: a field whose name is listed here fills the\n * `repos[]` property of the same name. `url` and `provider` are absent because\n * neither is answered by a field of its own — they come from the repo-picker's\n * value and from its declared provider.\n *\n * These two lists are the only field names the derivation knows; everything\n * else it reads it finds by field type. An entry that wants a base branch in\n * its request therefore has to name that field `ref`.\n */\nconst REPO_PROPERTIES: readonly string[] = [\"ref\"];\n\nconst FORM_PLACEHOLDER_PATTERN = /\\{\\{form\\.([A-Za-z0-9_.]+)\\}\\}/g;\n\n/** The repository input, which supplies `repos` and an event trigger's source. */\nfunction findRepoPickerField(\n setup: SetupBlock,\n selectedAction?: string | null,\n) {\n const match = Object.entries(collectFields(setup, null, selectedAction)).find(\n ([, field]) => field.type === \"repo-picker\",\n );\n return match ? { name: match[0], field: match[1] } : null;\n}\n\n/** Every repository the form collected, whether the picker takes one or many. */\nfunction repositories(\n setup: SetupBlock,\n values: SetupFormValues,\n selectedAction?: string | null,\n): string[] {\n const picker = findRepoPickerField(setup, selectedAction);\n return picker ? fieldValues(values[picker.name]) : [];\n}\n\n/**\n * The created automation's name.\n *\n * One repository is worth naming; several are not, so the count stands in\n * rather than a list of names that would not fit.\n */\nfunction deriveName(\n entry: SetupEntry,\n values: SetupFormValues,\n selectedAction?: string | null,\n): string {\n if (\n \"name\" in collectFields(entry.setup, null, selectedAction) &&\n \"name\" in values\n ) {\n return fieldText(values.name);\n }\n\n const repos = repositories(entry.setup, values, selectedAction);\n if (repos.length === 0) return entry.name;\n if (repos.length === 1) return `${entry.name} - ${repos[0]}`;\n // The count is the one word here the host writes rather than reads off the\n // entry, so it is translated. There is no translator to pass in: the\n // derivation runs from a memo, an upload and a test alike.\n const count = i18n.t(I18nKey.SETUP$REPOSITORY_COUNT, {\n total: repos.length,\n });\n return `${entry.name} - ${count}`;\n}\n\n/** The active trigger kind for a direct entry, with its fields. */\nfunction getTrigger(setup: SetupBlock, selectedTrigger?: string | null) {\n const triggers = setup.form.triggers ?? {};\n const entries = Object.entries(triggers);\n if (entries.length === 0) return null;\n const match =\n selectedTrigger && selectedTrigger in triggers\n ? [selectedTrigger, triggers[selectedTrigger as SetupTriggerKind]]\n : entries.length === 1\n ? entries[0]\n : null;\n if (!match) return null;\n const [kind, fields] = match;\n return { kind: kind as SetupTriggerKind, fields: fields ?? {} };\n}\n\nfunction hasPayloadValue(\n value: SetupRequestBody[string] | undefined,\n): value is SetupRequestBody[string] {\n return value !== undefined && value !== null && value !== \"\";\n}\n\nfunction fieldPayloadValue(\n fieldType: string | undefined,\n value: SetupFormValues[string],\n) {\n if (fieldType === \"number\") {\n const text = fieldText(value);\n return text === \"\" ? undefined : Number(text);\n }\n return fieldText(value);\n}\n\nfunction optionalCreateProperties(\n setup: SetupBlock,\n values: SetupFormValues,\n selectedAction?: string | null,\n): SetupRequestBody {\n return Object.fromEntries(\n OPTIONAL_CREATE_PROPERTIES.flatMap((name) => {\n const field = collectFields(setup, null, selectedAction)[name];\n const value = fieldPayloadValue(field?.type, values[name]);\n return hasPayloadValue(value) ? [[name, value]] : [];\n }),\n ) as SetupRequestBody;\n}\n\nfunction repositoryProperty(\n setup: SetupBlock,\n values: SetupFormValues,\n selectedAction?: string | null,\n): SetupRequestBody {\n const repoPicker = findRepoPickerField(setup, selectedAction);\n const repos = repositories(setup, values, selectedAction);\n if (repos.length === 0 || !repoPicker?.field.provider) return {};\n\n const declared = REPO_PROPERTIES.filter((name) =>\n hasPayloadValue(fieldText(values[name])),\n );\n return {\n repos: repos.map((url) => ({\n url,\n ...Object.fromEntries(\n declared.map((name) => [name, fieldText(values[name])]),\n ),\n provider: repoPicker.field.provider as string,\n })),\n };\n}\n\nfunction parsePluginSources(\n value: SetupRequestBody[string],\n): SetupRequestBody[string] {\n if (!hasPayloadValue(value) || typeof value !== \"string\") return value;\n const trimmed = value.trim();\n if (!trimmed) return [];\n if (trimmed.startsWith(\"[\")) {\n try {\n return JSON.parse(trimmed) as SetupRequestBody[string];\n } catch {\n return [{ source: trimmed }];\n }\n }\n return trimmed\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => {\n const [source, ...parts] = line.split(/\\s+/);\n const plugin: Record<string, string> = { source };\n parts.forEach((part) => {\n const [key, ...raw] = part.split(\"=\");\n const valuePart = raw.join(\"=\");\n if ((key === \"ref\" || key === \"repo_path\") && valuePart) {\n plugin[key] = valuePart;\n }\n });\n return plugin;\n });\n}\n\n/**\n * The create request body these form values produce.\n *\n * No entry declares it: `name` comes from the entry, `repos` from the\n * repo-picker field and its declared provider, and `trigger` from the key and\n * fields under `form.triggers`. Only `prompt` and an event `filter` are\n * declared, because only they cannot be read off the form.\n *\n * Returns null for an assisted entry, which hands setup to a conversation\n * instead of sending a body.\n */\nexport function buildCreatePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n /** Bundle/upload entries only: what the upload returned. */\n tarballPath: string = PREFLIGHT_TARBALL_PATH,\n selectedTrigger?: string | null,\n selectedActionKey?: string | null,\n): SetupRequestBody | null {\n const { setup } = entry;\n if (setup.mode !== \"direct\") return null;\n if (setup.bundle) {\n return buildBundlePayload(entry, values, tarballPath, selectedTrigger);\n }\n if (setup.actions) {\n return buildActionPayload(\n entry,\n values,\n tarballPath,\n selectedTrigger,\n selectedActionKey,\n );\n }\n if (!setup.prompt) return null;\n\n const scope = { form: values, automation: entry };\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n prompt: interpolateText(setup.prompt, scope),\n ...optionalCreateProperties(setup, values),\n ...repositoryProperty(setup, values),\n };\n\n const trigger = buildTrigger(entry, values, selectedTrigger);\n if (trigger) payload.trigger = trigger;\n\n const template = buildTemplate(entry, values);\n if (template) payload.template = template;\n\n return payload;\n}\n\nfunction buildActionPayload(\n entry: SetupEntry,\n values: SetupFormValues,\n tarballPath: string,\n selectedTrigger?: string | null,\n selectedActionKey?: string | null,\n): SetupRequestBody | null {\n const kind = selectedActionKind(entry, selectedActionKey);\n if (kind === \"bundle\") return null;\n\n const setup = entry.setup;\n const scope = { form: values, automation: entry };\n const payload: SetupRequestBody = {\n name: deriveName(entry, values, selectedActionKey),\n };\n\n const trigger = buildTrigger(\n entry,\n values,\n selectedTrigger,\n selectedActionKey,\n );\n if (trigger) payload.trigger = trigger;\n\n Object.assign(\n payload,\n optionalCreateProperties(setup, values, selectedActionKey),\n );\n\n if (kind === \"upload\") {\n const action = setup.actions?.upload;\n if (!action) return null;\n payload.tarball_path =\n tarballPath || interpolateText(action.tarballPath, scope);\n payload.entrypoint = interpolateText(action.entrypoint, scope);\n if (action.setupScript) {\n const setupScript = interpolateText(action.setupScript, scope);\n if (hasPayloadValue(setupScript)) payload.setup_script_path = setupScript;\n }\n return payload;\n }\n\n const action =\n kind === \"plugin\" ? setup.actions?.plugin : setup.actions?.prompt;\n if (!action) return null;\n payload.prompt = interpolateText(action.prompt, scope);\n Object.assign(payload, repositoryProperty(setup, values, selectedActionKey));\n\n if (kind === \"plugin\") {\n const pluginAction = setup.actions?.plugin;\n if (!pluginAction) return null;\n payload.plugins = parsePluginSources(\n interpolateValue(pluginAction.plugins, scope),\n );\n }\n\n const template = buildTemplate(entry, values);\n if (template) payload.template = template;\n\n return payload;\n}\n\n/**\n * The `template` provenance a prompt entry sends, or undefined for one that\n * publishes no version.\n *\n * It is what lets the service recognise a second setup of the same entry as\n * the automation it already holds. The bundle path derives the same block from\n * the config the bundle declares; a prompt entry declares none, so the answer\n * to \"what was this created from\" is the form as submitted.\n */\nfunction buildTemplate(\n entry: SetupEntry,\n values: SetupFormValues,\n): SetupRequestBody | undefined {\n if (!entry.version) return undefined;\n return {\n id: entry.id,\n version: entry.version,\n config: { ...values } as SetupRequestBody,\n };\n}\n\n/**\n * The `trigger` object, read off the key and fields under `form.triggers`.\n *\n * Identical for both kinds of direct entry: only the create endpoint and what\n * the automation is told to do differ between a prompt and a bundle.\n */\nfunction buildTrigger(\n entry: SetupEntry,\n values: SetupFormValues,\n selectedTrigger?: string | null,\n selectedAction?: string | null,\n): SetupRequestBody | undefined {\n const trigger = getTrigger(entry.setup, selectedTrigger);\n if (!trigger) return undefined;\n\n const properties = TRIGGER_PROPERTIES[trigger.kind];\n const declared = Object.keys(trigger.fields).filter((name) =>\n properties.includes(name),\n );\n const repoPicker = findRepoPickerField(entry.setup, selectedAction);\n const derived = Object.fromEntries(\n declared\n .map((name) => [name, fieldText(values[name])])\n .filter(([, value]) => hasPayloadValue(value)),\n );\n\n const filter = entry.setup.filter\n ? interpolateText(entry.setup.filter, {\n form: values,\n automation: entry,\n })\n : undefined;\n\n return {\n type: trigger.kind,\n ...derived,\n ...(trigger.kind === \"event\" && {\n ...(!(\"source\" in derived) && {\n source: repoPicker?.field.provider ?? \"\",\n }),\n ...(hasPayloadValue(filter) && { filter }),\n }),\n };\n}\n\n/**\n * The raw create body a bundle entry produces.\n *\n * `tarball_path` is the one value neither declared nor derived: the host packs\n * and uploads the bundle first, and creates from what came back. `template` is\n * the provenance that makes enabling the same entry twice return the\n * automation that already exists rather than a second one.\n *\n * There is no `repos`: the raw endpoint has no such field, and a bundle's\n * script fetches what it needs itself.\n */\nfunction buildBundlePayload(\n entry: SetupEntry,\n values: SetupFormValues,\n tarballPath: string,\n selectedTrigger?: string | null,\n): SetupRequestBody {\n const bundle = entry.setup.bundle!;\n const scope = { form: values, automation: entry };\n\n const payload: SetupRequestBody = {\n name: deriveName(entry, values),\n };\n\n const trigger = buildTrigger(entry, values, selectedTrigger);\n if (trigger) payload.trigger = trigger;\n\n payload.tarball_path = tarballPath;\n payload.entrypoint = bundle.entrypoint;\n if (bundle.setupScript) payload.setup_script_path = bundle.setupScript;\n if (bundle.timeout !== undefined) payload.timeout = bundle.timeout;\n payload.template = {\n id: entry.id,\n version: bundle.version,\n config: interpolateConfig(bundle.config, scope) as SetupRequestBody,\n };\n\n return payload;\n}\n\n/**\n * Placeholder substitution over the config tree. Only string leaves are\n * templated; a number, a boolean or a null is written through as itself, so an\n * entry can state a value the script reads as the type it expects.\n */\nfunction interpolateConfig(\n node: SetupBundleConfigValue,\n scope: Parameters<typeof interpolateText>[1],\n): SetupBundleConfigValue {\n if (typeof node === \"string\") {\n return interpolateValue(node, scope);\n }\n if (Array.isArray(node)) {\n return node.map((item) => interpolateConfig(item, scope));\n }\n if (typeof node === \"object\" && node !== null) {\n return Object.fromEntries(\n Object.entries(node).map(([key, value]) => [\n key,\n interpolateConfig(value, scope),\n ]),\n );\n }\n return node;\n}\n\n/**\n * The preflight body the host sends. The same shape for every entry, so no\n * entry declares it.\n */\nexport function buildPreflightBody(\n entry: SetupEntry,\n values: SetupFormValues,\n selectedTrigger?: string | null,\n selectedAction?: string | null,\n): SetupRequestBody | null {\n const draft = buildCreatePayload(\n entry,\n values,\n PREFLIGHT_TARBALL_PATH,\n selectedTrigger,\n selectedAction,\n );\n if (!draft) return null;\n\n return {\n automationId: entry.id,\n endpoint: automationCreateEndpoint(entry, selectedAction),\n draft,\n };\n}\n\n/**\n * What an assisted entry sends into the conversation that finishes setup.\n *\n * The command is not declared either: it lives once, in the owning skill's own\n * `triggers` frontmatter, and the skill defaults to the entry's id. An entry\n * whose skill is invoked by description rather than by command contributes\n * nothing here, and the answers alone open the conversation.\n */\nexport function buildAssistedMessage(\n entry: SetupEntry,\n values: SetupFormValues,\n): string {\n const command = findAutomationCommand(entry);\n const message = entry.setup.message\n ? interpolateText(entry.setup.message, { form: values, automation: entry })\n : \"\";\n\n return [command, message].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction collectPlaceholderFields(value: string): string[] {\n const names = Array.from(\n value.matchAll(FORM_PLACEHOLDER_PATTERN),\n (match) => match[1],\n );\n return Array.from(new Set(names));\n}\n\n/**\n * Which form fields built each payload path.\n *\n * Preflight and the create endpoint reject a draft by payload path, and the\n * host has to turn that back into a highlighted input. Building the body with\n * each field standing in for its own value recovers the mapping exactly, so an\n * entry does not declare it.\n */\nexport function deriveErrorMap(\n entry: SetupEntry,\n selectedTrigger?: string | null,\n selectedAction?: string | null,\n): Record<string, string[]> {\n const mapping: Record<string, string[]> = {};\n\n const triggerEntries = Object.keys(entry.setup.form.triggers ?? {});\n const triggerVariants = selectedTrigger\n ? [selectedTrigger]\n : triggerEntries.length > 1\n ? triggerEntries\n : [null];\n\n const actionEntries = actionKinds(entry.setup);\n const actionVariants = selectedAction\n ? [selectedAction]\n : actionEntries.length > 1\n ? actionEntries\n : [null];\n\n const walk = (node: unknown, path: string): void => {\n if (Array.isArray(node)) {\n node.forEach((item, index) => walk(item, `${path}[${index}]`));\n return;\n }\n if (typeof node === \"object\" && node !== null) {\n Object.entries(node).forEach(([key, item]) =>\n walk(item, path ? `${path}.${key}` : key),\n );\n return;\n }\n if (typeof node === \"string\") {\n const names = collectPlaceholderFields(node);\n if (names.length > 0) {\n mapping[path] = Array.from(\n new Set([...(mapping[path] ?? []), ...names]),\n );\n }\n }\n };\n\n actionVariants.forEach((actionVariant) => {\n triggerVariants.forEach((triggerVariant) => {\n const names = Object.keys(\n collectFields(entry.setup, triggerVariant, actionVariant),\n );\n const variantStandIns = Object.fromEntries(\n names.map((name) => [name, `{{form.${name}}}`]),\n );\n const template = buildCreatePayload(\n entry,\n variantStandIns,\n actionVariant === \"upload\" ? \"\" : PREFLIGHT_TARBALL_PATH,\n triggerVariant,\n actionVariant,\n );\n if (template) walk(template, \"\");\n });\n });\n return mapping;\n}\n"],"mappings":";;;;;AA6CA,SAAgB,EACd,GACA,GACQ;CACR,IAAM,IAAO,EAAmB,GAAO,CAAc;CAKrD,OAJI,MAAS,WAAiB,EAAsB,cAAc,IAC9D,MAAS,YAAY,MAAS,WACzB,EAAsB,cAAc,IAEtC,EAAsB,cAAc;AAC7C;AAGA,SAAgB,IAAmC;CACjD,OAAO,EAAsB,SAAS;AACxC;AAUA,SAAS,EACP,GACQ;CACR,IAAM,IAAO,EAAsB,CAAI;CACvC,IAAI,CAAC,GACH,MAAU,MACR,mDAAmD,EAAK,iFAE1D;CAEF,OAAO;AACT;AAoCA,SAAgB,EAAc,GAA4B;CACxD,OAAO,EAAM,MAAM,SAAS,YAAY,EAAM,MAAM,WAAW,KAAA;AACjE;AASA,SAAgB,EACd,GACA,GACuC;CACvC,IAAI,CAAC,GAAO,OAAO;CACnB,IAAI,EAAc,CAAK,GAAG,OAAO;CACjC,IAAM,IAAU,EAAM,MAAM,WAAW,CAAC;CACxC,IAAI,KAAkB,KAAkB,GACtC,OAAO;CAET,IAAM,IAAO,EAAY,EAAM,KAAK;CAEpC,OADI,EAAK,WAAW,IAAU,EAAK,KAC5B;AACT"}
@@ -17,4 +17,4 @@ export interface SetupActionResult {
17
17
  /** The created resource, or the conversation that will finish setup. */
18
18
  response: Record<string, unknown>;
19
19
  }
20
- export declare function useSetupAction(): (entry: SetupEntry, values: SetupFormValues, payload: SetupRequestBody | null) => Promise<SetupActionResult>;
20
+ export declare function useSetupAction(): (entry: SetupEntry, values: SetupFormValues, payload: SetupRequestBody | null, selectedTrigger?: string | null, selectedAction?: string | null) => Promise<SetupActionResult>;
@@ -6,7 +6,7 @@
6
6
  * what it supports. Neither side is interpreted here beyond set membership, so
7
7
  * this stays neutral about what any particular capability means.
8
8
  */
9
- import type { DeploymentCapabilities, SetupEntry } from "./types";
9
+ import type { DeploymentCapabilities, SetupActionKind, SetupEntry, SetupTriggerKind } from "./types";
10
10
  /**
11
11
  * "unknown" is a real outcome, not an error: a deployment that cannot be asked
12
12
  * must not be treated as one that answered no.
@@ -22,4 +22,6 @@ export interface SetupCapabilityAssessment {
22
22
  */
23
23
  unmet: string[];
24
24
  }
25
+ export declare function supportedActionKinds(entry: SetupEntry, reported: DeploymentCapabilities): SetupActionKind[];
26
+ export declare function supportedTriggerKinds(entry: SetupEntry, reported: DeploymentCapabilities): SetupTriggerKind[];
25
27
  export declare function assessCapabilityRequirements(entry: SetupEntry, reported: DeploymentCapabilities): SetupCapabilityAssessment;
@@ -0,0 +1,2 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");function e(e){return Object.keys(e.actions??{})}exports.actionKinds=e;
2
+ //# sourceMappingURL=manifest-local-validation.cjs.map