openclaw-multi-auto 1.3.6 → 1.3.8

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 (328) hide show
  1. package/dist/{audio-preflight-5FEeDooz.js → audio-preflight-DDBLZBdb.js} +4 -4
  2. package/dist/{audio-transcription-runner-B-UvoDjZ.js → audio-transcription-runner-DZbSWT9E.js} +1 -1
  3. package/dist/build-info.json +3 -3
  4. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  5. package/dist/{chrome-D45SyhQL.js → chrome-CMU2WVFh.js} +8 -8
  6. package/dist/{deliver-B9cys0EZ.js → deliver-BXVcFIHL.js} +1 -1
  7. package/dist/{deliver-runtime-DhaQJ0pI.js → deliver-runtime-DTaIS-1i.js} +3 -3
  8. package/dist/{deps-send-whatsapp.runtime-DvTL2tzN.js → deps-send-whatsapp.runtime-CIZqFAqb.js} +7 -7
  9. package/dist/extensionAPI.js +6 -6
  10. package/dist/{image-DAOPwVXi.js → image-BCVLo0qw.js} +1 -1
  11. package/dist/{image-runtime-wlCLVvVv.js → image-runtime-DtCKpMPZ.js} +3 -3
  12. package/dist/{pi-embedded-DYU79yGe.js → pi-embedded-CgQ_W6Xs.js} +24 -24
  13. package/dist/{pi-embedded-helpers-uTRAmQ4n.js → pi-embedded-helpers-CwuBTKza.js} +3 -3
  14. package/dist/plugin-sdk/{accounts-DyFCXtHv.js → accounts-BslAlVYS.js} +2 -2
  15. package/dist/plugin-sdk/{accounts-BJAXxY46.js → accounts-C3m65--E.js} +2 -2
  16. package/dist/plugin-sdk/{accounts-C1j7HSL0.js → accounts-CNCCkdEF.js} +3 -3
  17. package/dist/plugin-sdk/{active-listener-CftX5jLD.js → active-listener-CkPnMUkB.js} +2 -2
  18. package/dist/plugin-sdk/{api-key-rotation-8nyyt1kx.js → api-key-rotation-BXnNsojA.js} +2 -2
  19. package/dist/plugin-sdk/{audio-preflight-C_aSAPR1.js → audio-preflight-CtO4fFvp.js} +26 -26
  20. package/dist/plugin-sdk/{audio-transcription-runner-CB53F7_7.js → audio-transcription-runner-DnxvOS1-.js} +11 -11
  21. package/dist/plugin-sdk/{audit-membership-runtime-BXndI4LG.js → audit-membership-runtime-BpfoSk8M.js} +2 -2
  22. package/dist/plugin-sdk/{channel-activity-C5y8AgAV.js → channel-activity-WJYxcJ3S.js} +3 -3
  23. package/dist/plugin-sdk/{channel-web-DBTRO03V.js → channel-web-dO5k3ubM.js} +18 -18
  24. package/dist/plugin-sdk/{chrome-f00sZkDX.js → chrome-CjNTuJML.js} +6 -6
  25. package/dist/plugin-sdk/{commands-registry-BJ_NxG2F.js → commands-registry-CdYjoI0i.js} +4 -4
  26. package/dist/plugin-sdk/{common-Cf27Jwxu.js → common-oYc5vPFl.js} +2 -2
  27. package/dist/plugin-sdk/{config-CHQrpx-Q.js → config-B1z-UxQ3.js} +7 -7
  28. package/dist/plugin-sdk/{deliver-DNEuetST.js → deliver-D5_6T567.js} +10 -10
  29. package/dist/plugin-sdk/deliver-runtime-C5dgvvga.js +32 -0
  30. package/dist/plugin-sdk/deps-send-discord.runtime-Dg4N7PHJ.js +23 -0
  31. package/dist/plugin-sdk/deps-send-imessage.runtime-0OEwzMQm.js +22 -0
  32. package/dist/plugin-sdk/deps-send-signal.runtime-BM1jRt3G.js +21 -0
  33. package/dist/plugin-sdk/deps-send-slack.runtime-1E3BYRdF.js +19 -0
  34. package/dist/plugin-sdk/deps-send-telegram.runtime-DNCxIflA.js +24 -0
  35. package/dist/plugin-sdk/deps-send-whatsapp.runtime-OLwr-9c8.js +57 -0
  36. package/dist/plugin-sdk/{diagnostic-LYUUmjJ5.js → diagnostic-Bxxu0ig-.js} +2 -2
  37. package/dist/plugin-sdk/{errors-CtMWwS2Z.js → errors-B3cHyZZA.js} +1 -1
  38. package/dist/plugin-sdk/{fetch-guard-CxYB5Kg6.js → fetch-guard-Dcgod0tg.js} +2 -2
  39. package/dist/plugin-sdk/{fs-safe-DtfhxbrI.js → fs-safe-BaKqI3G4.js} +3 -3
  40. package/dist/plugin-sdk/{image-BwjYjRHx.js → image-B2mQW9Rb.js} +6 -6
  41. package/dist/plugin-sdk/{image-ops-BnZKcbd6.js → image-ops-Cbzr4U9l.js} +2 -2
  42. package/dist/plugin-sdk/image-runtime-BFm45j49.js +25 -0
  43. package/dist/plugin-sdk/{ir-Z4hX67TJ.js → ir-ZEmrTr4J.js} +7 -7
  44. package/dist/plugin-sdk/{local-roots-KhjQw04O.js → local-roots-CIPRxA-4.js} +4 -4
  45. package/dist/plugin-sdk/{logger-DHIIvMxj.js → logger-CvPFVOgT.js} +2 -2
  46. package/dist/plugin-sdk/{login-C31642Ld.js → login-CCTew9bt.js} +4 -4
  47. package/dist/plugin-sdk/{login-qr--y2SG_Ue.js → login-qr-BI3Vi_wJ.js} +5 -5
  48. package/dist/plugin-sdk/{manager-2UZBMCc7.js → manager-BEoYPn7R.js} +8 -8
  49. package/dist/plugin-sdk/manager-runtime-DxclHQ4U.js +15 -0
  50. package/dist/plugin-sdk/{outbound-Ba0QUI5h.js → outbound-ByOw1K6W.js} +5 -5
  51. package/dist/plugin-sdk/{outbound-attachment-B1Laso-8.js → outbound-attachment-BzVhxRRw.js} +2 -2
  52. package/dist/plugin-sdk/{path-alias-guards-C7Vm5DZ1.js → path-alias-guards-sWayacde.js} +1 -1
  53. package/dist/plugin-sdk/{paths-DopV9PQG.js → paths-Dpg3qxcl.js} +1 -1
  54. package/dist/plugin-sdk/{pi-embedded-helpers-DnA_OCzP.js → pi-embedded-helpers-DIxXkGJf.js} +16 -16
  55. package/dist/plugin-sdk/{pi-model-discovery-DdPqXk8f.js → pi-model-discovery-DM_2uFtj.js} +1 -1
  56. package/dist/plugin-sdk/pi-model-discovery-runtime-BuzvkvNR.js +8 -0
  57. package/dist/plugin-sdk/{pi-tools.before-tool-call.runtime-DxFHiLUE.js → pi-tools.before-tool-call.runtime-w1dqL_ty.js} +4 -4
  58. package/dist/plugin-sdk/{plugins-CbCt4osF.js → plugins-C4USiH29.js} +4 -4
  59. package/dist/plugin-sdk/{proxy-env-C63mMdas.js → proxy-env-ET-rp8eg.js} +1 -1
  60. package/dist/plugin-sdk/{proxy-fetch-Ch95c_Y2.js → proxy-fetch-uDXGKG3Z.js} +1 -1
  61. package/dist/plugin-sdk/{pw-ai-DpJk62D4.js → pw-ai-CyOt3RDA.js} +9 -9
  62. package/dist/plugin-sdk/{qmd-manager-Ca-iSfEE.js → qmd-manager-BySdoVR7.js} +7 -7
  63. package/dist/plugin-sdk/{query-expansion-B_Xe41Ab.js → query-expansion-C6uS-7lj.js} +4 -4
  64. package/dist/plugin-sdk/{redact-hp9TOulW.js → redact-Bvxt1T_Q.js} +1 -1
  65. package/dist/plugin-sdk/{reply-CovBlFea.js → reply-CTCSeQqW.js} +73 -73
  66. package/dist/plugin-sdk/{resolve-outbound-target-BbrHgyUk.js → resolve-outbound-target-Bw8YNANu.js} +2 -2
  67. package/dist/plugin-sdk/{run-with-concurrency-BR1DXa8T.js → run-with-concurrency-C_KCHwvf.js} +1 -1
  68. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-BxgRDkhc.js +10 -0
  69. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-elOqrkfg.js +19 -0
  70. package/dist/plugin-sdk/{send-BvAtLLPl.js → send-BZ6nYFZr.js} +5 -5
  71. package/dist/plugin-sdk/{send-BTztm3D2.js → send-C0w6xP2x.js} +6 -6
  72. package/dist/plugin-sdk/{send-CWJUuG0i.js → send-CFf-1V89.js} +8 -8
  73. package/dist/plugin-sdk/{send-EcglC4cG.js → send-CY-Qfwia.js} +7 -7
  74. package/dist/plugin-sdk/{send-BXpXBwM_.js → send-qPyNGSe4.js} +13 -13
  75. package/dist/plugin-sdk/{session-k256LJZT.js → session-COrvpvUQ.js} +3 -3
  76. package/dist/plugin-sdk/signal.js +2 -2
  77. package/dist/plugin-sdk/{skill-commands-DoRqLzxm.js → skill-commands-DZqhtmiv.js} +4 -4
  78. package/dist/plugin-sdk/{skills-QudILG6e.js → skills-Cw_vXEJb.js} +6 -6
  79. package/dist/plugin-sdk/slash-commands.runtime-D67JLweo.js +13 -0
  80. package/dist/plugin-sdk/slash-dispatch.runtime-DvcpvCJ0.js +52 -0
  81. package/dist/plugin-sdk/slash-skill-commands.runtime-BM1x3azR.js +16 -0
  82. package/dist/plugin-sdk/{store-BbDQw3g6.js → store-CMHj6IIw.js} +2 -2
  83. package/dist/plugin-sdk/subagent-registry-runtime-1lbDyRzz.js +52 -0
  84. package/dist/plugin-sdk/{tables-BhvloMKN.js → tables-CSqrHsKL.js} +1 -1
  85. package/dist/plugin-sdk/{thinking-URzkT-3p.js → thinking-DOnsR_A8.js} +7 -7
  86. package/dist/plugin-sdk/{tokens-B1PW5Ayy.js → tokens-BDr0Z9o3.js} +1 -1
  87. package/dist/plugin-sdk/{tool-images-xpqbP6RR.js → tool-images-eEfOVkzf.js} +2 -2
  88. package/dist/plugin-sdk/web-BLyT64pW.js +56 -0
  89. package/dist/plugin-sdk/{whatsapp-actions-RcZ6vp61.js → whatsapp-actions-xcleMoMv.js} +17 -17
  90. package/dist/plugin-sdk/whatsapp.js +50 -50
  91. package/dist/{pw-ai-GcYO6HPE.js → pw-ai-CmphSzHx.js} +1 -1
  92. package/dist/{slash-dispatch.runtime-Dh053pQK.js → slash-dispatch.runtime-131yup2e.js} +6 -6
  93. package/dist/{subagent-registry-runtime-DSi5mnCQ.js → subagent-registry-runtime-DbSf_Je6.js} +6 -6
  94. package/dist/{web-1hWJDzNA.js → web-MR9d7KyB.js} +6 -6
  95. package/package.json +5 -2
  96. package/scripts/create-instance.sh +44 -19
  97. package/scripts/install-maca.sh +39 -28
  98. package/scripts/npm_publish.sh +8 -6
  99. package/ui/index.html +16 -0
  100. package/ui/node_modules/.bin/jiti +21 -0
  101. package/ui/node_modules/.bin/lessc +21 -0
  102. package/ui/node_modules/.bin/marked +21 -0
  103. package/ui/node_modules/.bin/playwright +21 -0
  104. package/ui/node_modules/.bin/sass +21 -0
  105. package/ui/node_modules/.bin/tsx +21 -0
  106. package/ui/node_modules/.bin/vite +21 -0
  107. package/ui/node_modules/.bin/vitest +21 -0
  108. package/ui/node_modules/.bin/yaml +21 -0
  109. package/ui/package.json +27 -0
  110. package/ui/public/apple-touch-icon.png +0 -0
  111. package/ui/public/favicon-32.png +0 -0
  112. package/ui/public/favicon.ico +0 -0
  113. package/ui/public/favicon.svg +22 -0
  114. package/ui/src/css.d.ts +1 -0
  115. package/ui/src/i18n/index.ts +3 -0
  116. package/ui/src/i18n/lib/lit-controller.ts +22 -0
  117. package/ui/src/i18n/lib/registry.ts +64 -0
  118. package/ui/src/i18n/lib/translate.ts +123 -0
  119. package/ui/src/i18n/lib/types.ts +9 -0
  120. package/ui/src/i18n/locales/de.ts +129 -0
  121. package/ui/src/i18n/locales/en.ts +337 -0
  122. package/ui/src/i18n/locales/pt-BR.ts +128 -0
  123. package/ui/src/i18n/locales/zh-CN.ts +330 -0
  124. package/ui/src/i18n/locales/zh-TW.ts +125 -0
  125. package/ui/src/i18n/test/translate.test.ts +56 -0
  126. package/ui/src/main.ts +2 -0
  127. package/ui/src/styles/base.css +385 -0
  128. package/ui/src/styles/chat/grouped.css +300 -0
  129. package/ui/src/styles/chat/layout.css +481 -0
  130. package/ui/src/styles/chat/sidebar.css +117 -0
  131. package/ui/src/styles/chat/text.css +146 -0
  132. package/ui/src/styles/chat/tool-cards.css +202 -0
  133. package/ui/src/styles/chat.css +5 -0
  134. package/ui/src/styles/components.css +2612 -0
  135. package/ui/src/styles/config.css +1658 -0
  136. package/ui/src/styles/layout.css +621 -0
  137. package/ui/src/styles/layout.mobile.css +374 -0
  138. package/ui/src/styles.css +5 -0
  139. package/ui/src/ui/__screenshots__/config-form.browser.test.ts/config-form-renderer-flags-unsupported-unions-1.png +0 -0
  140. package/ui/src/ui/__screenshots__/config-form.browser.test.ts/config-form-renderer-renders-inputs-and-patches-values-1.png +0 -0
  141. package/ui/src/ui/__screenshots__/config-form.browser.test.ts/config-form-renderer-renders-union-literals-as-select-options-1.png +0 -0
  142. package/ui/src/ui/__screenshots__/navigation.browser.test.ts/control-UI-routing-auto-scrolls-chat-history-to-the-latest-message-1.png +0 -0
  143. package/ui/src/ui/app-channels.ts +279 -0
  144. package/ui/src/ui/app-chat.ts +266 -0
  145. package/ui/src/ui/app-defaults.ts +50 -0
  146. package/ui/src/ui/app-events.ts +5 -0
  147. package/ui/src/ui/app-gateway.node.test.ts +229 -0
  148. package/ui/src/ui/app-gateway.ts +349 -0
  149. package/ui/src/ui/app-lifecycle.node.test.ts +44 -0
  150. package/ui/src/ui/app-lifecycle.ts +109 -0
  151. package/ui/src/ui/app-polling.ts +69 -0
  152. package/ui/src/ui/app-render-usage-tab.ts +273 -0
  153. package/ui/src/ui/app-render.helpers.node.test.ts +286 -0
  154. package/ui/src/ui/app-render.helpers.ts +574 -0
  155. package/ui/src/ui/app-render.ts +1168 -0
  156. package/ui/src/ui/app-scroll.test.ts +275 -0
  157. package/ui/src/ui/app-scroll.ts +179 -0
  158. package/ui/src/ui/app-settings.test.ts +70 -0
  159. package/ui/src/ui/app-settings.ts +440 -0
  160. package/ui/src/ui/app-tool-stream.node.test.ts +139 -0
  161. package/ui/src/ui/app-tool-stream.ts +455 -0
  162. package/ui/src/ui/app-view-state.ts +321 -0
  163. package/ui/src/ui/app.ts +621 -0
  164. package/ui/src/ui/assistant-identity.ts +23 -0
  165. package/ui/src/ui/chat/constants.ts +12 -0
  166. package/ui/src/ui/chat/copy-as-markdown.ts +97 -0
  167. package/ui/src/ui/chat/grouped-render.ts +287 -0
  168. package/ui/src/ui/chat/message-extract.test.ts +64 -0
  169. package/ui/src/ui/chat/message-extract.ts +122 -0
  170. package/ui/src/ui/chat/message-normalizer.test.ts +179 -0
  171. package/ui/src/ui/chat/message-normalizer.ts +101 -0
  172. package/ui/src/ui/chat/tool-cards.ts +156 -0
  173. package/ui/src/ui/chat/tool-helpers.test.ts +141 -0
  174. package/ui/src/ui/chat/tool-helpers.ts +37 -0
  175. package/ui/src/ui/chat-event-reload.test.ts +47 -0
  176. package/ui/src/ui/chat-event-reload.ts +16 -0
  177. package/ui/src/ui/chat-markdown.browser.test.ts +37 -0
  178. package/ui/src/ui/components/resizable-divider.ts +110 -0
  179. package/ui/src/ui/config-form.browser.test.ts +443 -0
  180. package/ui/src/ui/controllers/agent-files.ts +126 -0
  181. package/ui/src/ui/controllers/agent-identity.ts +59 -0
  182. package/ui/src/ui/controllers/agent-skills.ts +33 -0
  183. package/ui/src/ui/controllers/agents.test.ts +61 -0
  184. package/ui/src/ui/controllers/agents.ts +64 -0
  185. package/ui/src/ui/controllers/assistant-identity.ts +34 -0
  186. package/ui/src/ui/controllers/channels.ts +94 -0
  187. package/ui/src/ui/controllers/channels.types.ts +15 -0
  188. package/ui/src/ui/controllers/chat.test.ts +568 -0
  189. package/ui/src/ui/controllers/chat.ts +318 -0
  190. package/ui/src/ui/controllers/config/form-coerce.ts +160 -0
  191. package/ui/src/ui/controllers/config/form-utils.node.test.ts +455 -0
  192. package/ui/src/ui/controllers/config/form-utils.ts +90 -0
  193. package/ui/src/ui/controllers/config.test.ts +289 -0
  194. package/ui/src/ui/controllers/config.ts +219 -0
  195. package/ui/src/ui/controllers/control-ui-bootstrap.test.ts +82 -0
  196. package/ui/src/ui/controllers/control-ui-bootstrap.ts +49 -0
  197. package/ui/src/ui/controllers/cron-filters.test.ts +81 -0
  198. package/ui/src/ui/controllers/cron.test.ts +1070 -0
  199. package/ui/src/ui/controllers/cron.ts +921 -0
  200. package/ui/src/ui/controllers/debug.ts +60 -0
  201. package/ui/src/ui/controllers/devices.ts +159 -0
  202. package/ui/src/ui/controllers/exec-approval.ts +100 -0
  203. package/ui/src/ui/controllers/exec-approvals.ts +170 -0
  204. package/ui/src/ui/controllers/logs.ts +147 -0
  205. package/ui/src/ui/controllers/nodes.ts +32 -0
  206. package/ui/src/ui/controllers/presence.ts +37 -0
  207. package/ui/src/ui/controllers/sessions.test.ts +104 -0
  208. package/ui/src/ui/controllers/sessions.ts +127 -0
  209. package/ui/src/ui/controllers/skills.ts +157 -0
  210. package/ui/src/ui/controllers/usage.node.test.ts +181 -0
  211. package/ui/src/ui/controllers/usage.ts +315 -0
  212. package/ui/src/ui/data/moonshot-kimi-k2.ts +45 -0
  213. package/ui/src/ui/device-auth.ts +73 -0
  214. package/ui/src/ui/device-identity.ts +112 -0
  215. package/ui/src/ui/external-link.test.ts +18 -0
  216. package/ui/src/ui/external-link.ts +19 -0
  217. package/ui/src/ui/focus-mode.browser.test.ts +39 -0
  218. package/ui/src/ui/format.test.ts +101 -0
  219. package/ui/src/ui/format.ts +60 -0
  220. package/ui/src/ui/gateway.ts +360 -0
  221. package/ui/src/ui/icons.ts +256 -0
  222. package/ui/src/ui/markdown.test.ts +85 -0
  223. package/ui/src/ui/markdown.ts +139 -0
  224. package/ui/src/ui/navigation.browser.test.ts +188 -0
  225. package/ui/src/ui/navigation.test.ts +189 -0
  226. package/ui/src/ui/navigation.ts +165 -0
  227. package/ui/src/ui/open-external-url.test.ts +108 -0
  228. package/ui/src/ui/open-external-url.ts +73 -0
  229. package/ui/src/ui/presenter.ts +85 -0
  230. package/ui/src/ui/storage.node.test.ts +63 -0
  231. package/ui/src/ui/storage.ts +99 -0
  232. package/ui/src/ui/test-helpers/app-mount.ts +27 -0
  233. package/ui/src/ui/text-direction.test.ts +24 -0
  234. package/ui/src/ui/text-direction.ts +30 -0
  235. package/ui/src/ui/theme-transition.ts +109 -0
  236. package/ui/src/ui/theme.ts +16 -0
  237. package/ui/src/ui/tool-display.ts +159 -0
  238. package/ui/src/ui/types/chat-types.ts +44 -0
  239. package/ui/src/ui/types.ts +627 -0
  240. package/ui/src/ui/ui-types.ts +54 -0
  241. package/ui/src/ui/usage-helpers.node.test.ts +43 -0
  242. package/ui/src/ui/usage-helpers.ts +321 -0
  243. package/ui/src/ui/usage-types.ts +22 -0
  244. package/ui/src/ui/uuid.test.ts +41 -0
  245. package/ui/src/ui/uuid.ts +57 -0
  246. package/ui/src/ui/views/agents-panels-status-files.ts +461 -0
  247. package/ui/src/ui/views/agents-panels-tools-skills.browser.test.ts +102 -0
  248. package/ui/src/ui/views/agents-panels-tools-skills.ts +537 -0
  249. package/ui/src/ui/views/agents-utils.test.ts +100 -0
  250. package/ui/src/ui/views/agents-utils.ts +502 -0
  251. package/ui/src/ui/views/agents.ts +499 -0
  252. package/ui/src/ui/views/channel-config-extras.ts +49 -0
  253. package/ui/src/ui/views/channels.config.ts +155 -0
  254. package/ui/src/ui/views/channels.discord.ts +65 -0
  255. package/ui/src/ui/views/channels.googlechat.ts +79 -0
  256. package/ui/src/ui/views/channels.imessage.ts +65 -0
  257. package/ui/src/ui/views/channels.nostr-profile-form.ts +321 -0
  258. package/ui/src/ui/views/channels.nostr.ts +237 -0
  259. package/ui/src/ui/views/channels.shared.ts +38 -0
  260. package/ui/src/ui/views/channels.signal.ts +69 -0
  261. package/ui/src/ui/views/channels.slack.ts +65 -0
  262. package/ui/src/ui/views/channels.telegram.ts +120 -0
  263. package/ui/src/ui/views/channels.ts +325 -0
  264. package/ui/src/ui/views/channels.types.ts +62 -0
  265. package/ui/src/ui/views/channels.whatsapp.ts +118 -0
  266. package/ui/src/ui/views/chat-image-open.browser.test.ts +70 -0
  267. package/ui/src/ui/views/chat.test.ts +227 -0
  268. package/ui/src/ui/views/chat.ts +616 -0
  269. package/ui/src/ui/views/config-form.analyze.ts +267 -0
  270. package/ui/src/ui/views/config-form.node.ts +1073 -0
  271. package/ui/src/ui/views/config-form.render.ts +478 -0
  272. package/ui/src/ui/views/config-form.search.node.test.ts +69 -0
  273. package/ui/src/ui/views/config-form.shared.ts +96 -0
  274. package/ui/src/ui/views/config-form.ts +4 -0
  275. package/ui/src/ui/views/config-search.node.test.ts +50 -0
  276. package/ui/src/ui/views/config-search.ts +92 -0
  277. package/ui/src/ui/views/config.browser.test.ts +233 -0
  278. package/ui/src/ui/views/config.ts +820 -0
  279. package/ui/src/ui/views/cron.test.ts +741 -0
  280. package/ui/src/ui/views/cron.ts +1758 -0
  281. package/ui/src/ui/views/debug.ts +151 -0
  282. package/ui/src/ui/views/exec-approval.ts +89 -0
  283. package/ui/src/ui/views/gateway-url-confirmation.ts +40 -0
  284. package/ui/src/ui/views/instances.ts +89 -0
  285. package/ui/src/ui/views/logs.ts +155 -0
  286. package/ui/src/ui/views/markdown-sidebar.ts +40 -0
  287. package/ui/src/ui/views/nodes-exec-approvals.ts +617 -0
  288. package/ui/src/ui/views/nodes-shared.ts +67 -0
  289. package/ui/src/ui/views/nodes.ts +485 -0
  290. package/ui/src/ui/views/overview-hints.ts +16 -0
  291. package/ui/src/ui/views/overview.node.test.ts +39 -0
  292. package/ui/src/ui/views/overview.ts +361 -0
  293. package/ui/src/ui/views/sessions.test.ts +81 -0
  294. package/ui/src/ui/views/sessions.ts +321 -0
  295. package/ui/src/ui/views/skills-grouping.ts +40 -0
  296. package/ui/src/ui/views/skills-shared.ts +52 -0
  297. package/ui/src/ui/views/skills.ts +192 -0
  298. package/ui/src/ui/views/usage-metrics.ts +578 -0
  299. package/ui/src/ui/views/usage-query.ts +277 -0
  300. package/ui/src/ui/views/usage-render-details.test.ts +136 -0
  301. package/ui/src/ui/views/usage-render-details.ts +1083 -0
  302. package/ui/src/ui/views/usage-render-overview.ts +796 -0
  303. package/ui/src/ui/views/usage-styles/usageStyles-part1.ts +701 -0
  304. package/ui/src/ui/views/usage-styles/usageStyles-part2.ts +702 -0
  305. package/ui/src/ui/views/usage-styles/usageStyles-part3.ts +551 -0
  306. package/ui/src/ui/views/usage.ts +836 -0
  307. package/ui/src/ui/views/usageStyles.ts +5 -0
  308. package/ui/src/ui/views/usageTypes.ts +105 -0
  309. package/ui/vite.config.ts +43 -0
  310. package/ui/vitest.config.ts +15 -0
  311. package/ui/vitest.node.config.ts +10 -0
  312. package/dist/plugin-sdk/deliver-runtime-BFdqklJM.js +0 -32
  313. package/dist/plugin-sdk/deps-send-discord.runtime-DuqpYwU0.js +0 -23
  314. package/dist/plugin-sdk/deps-send-imessage.runtime-CZ2rS8Lb.js +0 -22
  315. package/dist/plugin-sdk/deps-send-signal.runtime-BdqiWhIh.js +0 -21
  316. package/dist/plugin-sdk/deps-send-slack.runtime-04s36qiC.js +0 -19
  317. package/dist/plugin-sdk/deps-send-telegram.runtime-LE5tkPvr.js +0 -24
  318. package/dist/plugin-sdk/deps-send-whatsapp.runtime-Bz57lobC.js +0 -57
  319. package/dist/plugin-sdk/image-runtime-B8twoubs.js +0 -25
  320. package/dist/plugin-sdk/manager-runtime-CMeLwose.js +0 -15
  321. package/dist/plugin-sdk/pi-model-discovery-runtime-D8CJhtJY.js +0 -8
  322. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-SkO91TZH.js +0 -10
  323. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-B0VWK5hm.js +0 -19
  324. package/dist/plugin-sdk/slash-commands.runtime-DS6vCNSL.js +0 -13
  325. package/dist/plugin-sdk/slash-dispatch.runtime-BXrxb2wd.js +0 -52
  326. package/dist/plugin-sdk/slash-skill-commands.runtime-Bd6qQ2oT.js +0 -16
  327. package/dist/plugin-sdk/subagent-registry-runtime-1uwQbuXj.js +0 -52
  328. package/dist/plugin-sdk/web-B74yhL2N.js +0 -56
@@ -0,0 +1,537 @@
1
+ import { html, nothing } from "lit";
2
+ import { normalizeToolName } from "../../../../src/agents/tool-policy-shared.js";
3
+ import type { SkillStatusEntry, SkillStatusReport, ToolsCatalogResult } from "../types.ts";
4
+ import {
5
+ isAllowedByPolicy,
6
+ matchesList,
7
+ PROFILE_OPTIONS,
8
+ resolveAgentConfig,
9
+ resolveToolProfile,
10
+ TOOL_SECTIONS,
11
+ } from "./agents-utils.ts";
12
+ import type { SkillGroup } from "./skills-grouping.ts";
13
+ import { groupSkills } from "./skills-grouping.ts";
14
+ import {
15
+ computeSkillMissing,
16
+ computeSkillReasons,
17
+ renderSkillStatusChips,
18
+ } from "./skills-shared.ts";
19
+
20
+ export function renderAgentTools(params: {
21
+ agentId: string;
22
+ configForm: Record<string, unknown> | null;
23
+ configLoading: boolean;
24
+ configSaving: boolean;
25
+ configDirty: boolean;
26
+ toolsCatalogLoading: boolean;
27
+ toolsCatalogError: string | null;
28
+ toolsCatalogResult: ToolsCatalogResult | null;
29
+ onProfileChange: (agentId: string, profile: string | null, clearAllow: boolean) => void;
30
+ onOverridesChange: (agentId: string, alsoAllow: string[], deny: string[]) => void;
31
+ onConfigReload: () => void;
32
+ onConfigSave: () => void;
33
+ }) {
34
+ const config = resolveAgentConfig(params.configForm, params.agentId);
35
+ const agentTools = config.entry?.tools ?? {};
36
+ const globalTools = config.globalTools ?? {};
37
+ const profile = agentTools.profile ?? globalTools.profile ?? "full";
38
+ const profileSource = agentTools.profile
39
+ ? "agent override"
40
+ : globalTools.profile
41
+ ? "global default"
42
+ : "default";
43
+ const hasAgentAllow = Array.isArray(agentTools.allow) && agentTools.allow.length > 0;
44
+ const hasGlobalAllow = Array.isArray(globalTools.allow) && globalTools.allow.length > 0;
45
+ const editable =
46
+ Boolean(params.configForm) && !params.configLoading && !params.configSaving && !hasAgentAllow;
47
+ const alsoAllow = hasAgentAllow
48
+ ? []
49
+ : Array.isArray(agentTools.alsoAllow)
50
+ ? agentTools.alsoAllow
51
+ : [];
52
+ const deny = hasAgentAllow ? [] : Array.isArray(agentTools.deny) ? agentTools.deny : [];
53
+ const basePolicy = hasAgentAllow
54
+ ? { allow: agentTools.allow ?? [], deny: agentTools.deny ?? [] }
55
+ : (resolveToolProfile(profile) ?? undefined);
56
+ const sections =
57
+ params.toolsCatalogResult?.groups?.length &&
58
+ params.toolsCatalogResult.agentId === params.agentId
59
+ ? params.toolsCatalogResult.groups
60
+ : TOOL_SECTIONS;
61
+ const profileOptions =
62
+ params.toolsCatalogResult?.profiles?.length &&
63
+ params.toolsCatalogResult.agentId === params.agentId
64
+ ? params.toolsCatalogResult.profiles
65
+ : PROFILE_OPTIONS;
66
+ const toolIds = sections.flatMap((section) => section.tools.map((tool) => tool.id));
67
+
68
+ const resolveAllowed = (toolId: string) => {
69
+ const baseAllowed = isAllowedByPolicy(toolId, basePolicy);
70
+ const extraAllowed = matchesList(toolId, alsoAllow);
71
+ const denied = matchesList(toolId, deny);
72
+ const allowed = (baseAllowed || extraAllowed) && !denied;
73
+ return {
74
+ allowed,
75
+ baseAllowed,
76
+ denied,
77
+ };
78
+ };
79
+ const enabledCount = toolIds.filter((toolId) => resolveAllowed(toolId).allowed).length;
80
+
81
+ const updateTool = (toolId: string, nextEnabled: boolean) => {
82
+ const nextAllow = new Set(
83
+ alsoAllow.map((entry) => normalizeToolName(entry)).filter((entry) => entry.length > 0),
84
+ );
85
+ const nextDeny = new Set(
86
+ deny.map((entry) => normalizeToolName(entry)).filter((entry) => entry.length > 0),
87
+ );
88
+ const baseAllowed = resolveAllowed(toolId).baseAllowed;
89
+ const normalized = normalizeToolName(toolId);
90
+ if (nextEnabled) {
91
+ nextDeny.delete(normalized);
92
+ if (!baseAllowed) {
93
+ nextAllow.add(normalized);
94
+ }
95
+ } else {
96
+ nextAllow.delete(normalized);
97
+ nextDeny.add(normalized);
98
+ }
99
+ params.onOverridesChange(params.agentId, [...nextAllow], [...nextDeny]);
100
+ };
101
+
102
+ const updateAll = (nextEnabled: boolean) => {
103
+ const nextAllow = new Set(
104
+ alsoAllow.map((entry) => normalizeToolName(entry)).filter((entry) => entry.length > 0),
105
+ );
106
+ const nextDeny = new Set(
107
+ deny.map((entry) => normalizeToolName(entry)).filter((entry) => entry.length > 0),
108
+ );
109
+ for (const toolId of toolIds) {
110
+ const baseAllowed = resolveAllowed(toolId).baseAllowed;
111
+ const normalized = normalizeToolName(toolId);
112
+ if (nextEnabled) {
113
+ nextDeny.delete(normalized);
114
+ if (!baseAllowed) {
115
+ nextAllow.add(normalized);
116
+ }
117
+ } else {
118
+ nextAllow.delete(normalized);
119
+ nextDeny.add(normalized);
120
+ }
121
+ }
122
+ params.onOverridesChange(params.agentId, [...nextAllow], [...nextDeny]);
123
+ };
124
+
125
+ return html`
126
+ <section class="card">
127
+ <div class="row" style="justify-content: space-between;">
128
+ <div>
129
+ <div class="card-title">Tool Access</div>
130
+ <div class="card-sub">
131
+ Profile + per-tool overrides for this agent.
132
+ <span class="mono">${enabledCount}/${toolIds.length}</span> enabled.
133
+ </div>
134
+ </div>
135
+ <div class="row" style="gap: 8px;">
136
+ <button class="btn btn--sm" ?disabled=${!editable} @click=${() => updateAll(true)}>
137
+ Enable All
138
+ </button>
139
+ <button class="btn btn--sm" ?disabled=${!editable} @click=${() => updateAll(false)}>
140
+ Disable All
141
+ </button>
142
+ <button class="btn btn--sm" ?disabled=${params.configLoading} @click=${params.onConfigReload}>
143
+ Reload Config
144
+ </button>
145
+ <button
146
+ class="btn btn--sm primary"
147
+ ?disabled=${params.configSaving || !params.configDirty}
148
+ @click=${params.onConfigSave}
149
+ >
150
+ ${params.configSaving ? "Saving…" : "Save"}
151
+ </button>
152
+ </div>
153
+ </div>
154
+
155
+ ${
156
+ params.toolsCatalogError
157
+ ? html`
158
+ <div class="callout warn" style="margin-top: 12px">
159
+ Could not load runtime tool catalog. Showing fallback list.
160
+ </div>
161
+ `
162
+ : nothing
163
+ }
164
+ ${
165
+ !params.configForm
166
+ ? html`
167
+ <div class="callout info" style="margin-top: 12px">
168
+ Load the gateway config to adjust tool profiles.
169
+ </div>
170
+ `
171
+ : nothing
172
+ }
173
+ ${
174
+ hasAgentAllow
175
+ ? html`
176
+ <div class="callout info" style="margin-top: 12px">
177
+ This agent is using an explicit allowlist in config. Tool overrides are managed in the Config tab.
178
+ </div>
179
+ `
180
+ : nothing
181
+ }
182
+ ${
183
+ hasGlobalAllow
184
+ ? html`
185
+ <div class="callout info" style="margin-top: 12px">
186
+ Global tools.allow is set. Agent overrides cannot enable tools that are globally blocked.
187
+ </div>
188
+ `
189
+ : nothing
190
+ }
191
+
192
+ <div class="agent-tools-meta" style="margin-top: 16px;">
193
+ <div class="agent-kv">
194
+ <div class="label">Profile</div>
195
+ <div class="mono">${profile}</div>
196
+ </div>
197
+ <div class="agent-kv">
198
+ <div class="label">Source</div>
199
+ <div>${profileSource}</div>
200
+ </div>
201
+ ${
202
+ params.configDirty
203
+ ? html`
204
+ <div class="agent-kv">
205
+ <div class="label">Status</div>
206
+ <div class="mono">unsaved</div>
207
+ </div>
208
+ `
209
+ : nothing
210
+ }
211
+ </div>
212
+
213
+ <div class="agent-tools-presets" style="margin-top: 16px;">
214
+ <div class="label">Quick Presets</div>
215
+ <div class="agent-tools-buttons">
216
+ ${profileOptions.map(
217
+ (option) => html`
218
+ <button
219
+ class="btn btn--sm ${profile === option.id ? "active" : ""}"
220
+ ?disabled=${!editable}
221
+ @click=${() => params.onProfileChange(params.agentId, option.id, true)}
222
+ >
223
+ ${option.label}
224
+ </button>
225
+ `,
226
+ )}
227
+ <button
228
+ class="btn btn--sm"
229
+ ?disabled=${!editable}
230
+ @click=${() => params.onProfileChange(params.agentId, null, false)}
231
+ >
232
+ Inherit
233
+ </button>
234
+ </div>
235
+ </div>
236
+
237
+ <div class="agent-tools-grid" style="margin-top: 20px;">
238
+ ${sections.map(
239
+ (section) =>
240
+ html`
241
+ <div class="agent-tools-section">
242
+ <div class="agent-tools-header">
243
+ ${section.label}
244
+ ${
245
+ "source" in section && section.source === "plugin"
246
+ ? html`
247
+ <span class="mono" style="margin-left: 6px">plugin</span>
248
+ `
249
+ : nothing
250
+ }
251
+ </div>
252
+ <div class="agent-tools-list">
253
+ ${section.tools.map((tool) => {
254
+ const { allowed } = resolveAllowed(tool.id);
255
+ const catalogTool = tool as {
256
+ source?: "core" | "plugin";
257
+ pluginId?: string;
258
+ optional?: boolean;
259
+ };
260
+ const source =
261
+ catalogTool.source === "plugin"
262
+ ? catalogTool.pluginId
263
+ ? `plugin:${catalogTool.pluginId}`
264
+ : "plugin"
265
+ : "core";
266
+ const isOptional = catalogTool.optional === true;
267
+ return html`
268
+ <div class="agent-tool-row">
269
+ <div>
270
+ <div class="agent-tool-title mono">
271
+ ${tool.label}
272
+ <span class="mono" style="margin-left: 8px; opacity: 0.8;">${source}</span>
273
+ ${
274
+ isOptional
275
+ ? html`
276
+ <span class="mono" style="margin-left: 6px; opacity: 0.8">optional</span>
277
+ `
278
+ : nothing
279
+ }
280
+ </div>
281
+ <div class="agent-tool-sub">${tool.description}</div>
282
+ </div>
283
+ <label class="cfg-toggle">
284
+ <input
285
+ type="checkbox"
286
+ .checked=${allowed}
287
+ ?disabled=${!editable}
288
+ @change=${(e: Event) =>
289
+ updateTool(tool.id, (e.target as HTMLInputElement).checked)}
290
+ />
291
+ <span class="cfg-toggle__track"></span>
292
+ </label>
293
+ </div>
294
+ `;
295
+ })}
296
+ </div>
297
+ </div>
298
+ `,
299
+ )}
300
+ </div>
301
+ ${
302
+ params.toolsCatalogLoading
303
+ ? html`
304
+ <div class="card-sub" style="margin-top: 10px">Refreshing tool catalog…</div>
305
+ `
306
+ : nothing
307
+ }
308
+ </section>
309
+ `;
310
+ }
311
+
312
+ export function renderAgentSkills(params: {
313
+ agentId: string;
314
+ report: SkillStatusReport | null;
315
+ loading: boolean;
316
+ error: string | null;
317
+ activeAgentId: string | null;
318
+ configForm: Record<string, unknown> | null;
319
+ configLoading: boolean;
320
+ configSaving: boolean;
321
+ configDirty: boolean;
322
+ filter: string;
323
+ onFilterChange: (next: string) => void;
324
+ onRefresh: () => void;
325
+ onToggle: (agentId: string, skillName: string, enabled: boolean) => void;
326
+ onClear: (agentId: string) => void;
327
+ onDisableAll: (agentId: string) => void;
328
+ onConfigReload: () => void;
329
+ onConfigSave: () => void;
330
+ }) {
331
+ const editable = Boolean(params.configForm) && !params.configLoading && !params.configSaving;
332
+ const config = resolveAgentConfig(params.configForm, params.agentId);
333
+ const allowlist = Array.isArray(config.entry?.skills) ? config.entry?.skills : undefined;
334
+ const allowSet = new Set((allowlist ?? []).map((name) => name.trim()).filter(Boolean));
335
+ const usingAllowlist = allowlist !== undefined;
336
+ const reportReady = Boolean(params.report && params.activeAgentId === params.agentId);
337
+ const rawSkills = reportReady ? (params.report?.skills ?? []) : [];
338
+ const filter = params.filter.trim().toLowerCase();
339
+ const filtered = filter
340
+ ? rawSkills.filter((skill) =>
341
+ [skill.name, skill.description, skill.source].join(" ").toLowerCase().includes(filter),
342
+ )
343
+ : rawSkills;
344
+ const groups = groupSkills(filtered);
345
+ const enabledCount = usingAllowlist
346
+ ? rawSkills.filter((skill) => allowSet.has(skill.name)).length
347
+ : rawSkills.length;
348
+ const totalCount = rawSkills.length;
349
+
350
+ return html`
351
+ <section class="card">
352
+ <div class="row" style="justify-content: space-between;">
353
+ <div>
354
+ <div class="card-title">Skills</div>
355
+ <div class="card-sub">
356
+ Per-agent skill allowlist and workspace skills.
357
+ ${
358
+ totalCount > 0
359
+ ? html`<span class="mono">${enabledCount}/${totalCount}</span>`
360
+ : nothing
361
+ }
362
+ </div>
363
+ </div>
364
+ <div class="row" style="gap: 8px;">
365
+ <button class="btn btn--sm" ?disabled=${!editable} @click=${() => params.onClear(params.agentId)}>
366
+ Use All
367
+ </button>
368
+ <button
369
+ class="btn btn--sm"
370
+ ?disabled=${!editable}
371
+ @click=${() => params.onDisableAll(params.agentId)}
372
+ >
373
+ Disable All
374
+ </button>
375
+ <button class="btn btn--sm" ?disabled=${params.configLoading} @click=${params.onConfigReload}>
376
+ Reload Config
377
+ </button>
378
+ <button class="btn btn--sm" ?disabled=${params.loading} @click=${params.onRefresh}>
379
+ ${params.loading ? "Loading…" : "Refresh"}
380
+ </button>
381
+ <button
382
+ class="btn btn--sm primary"
383
+ ?disabled=${params.configSaving || !params.configDirty}
384
+ @click=${params.onConfigSave}
385
+ >
386
+ ${params.configSaving ? "Saving…" : "Save"}
387
+ </button>
388
+ </div>
389
+ </div>
390
+
391
+ ${
392
+ !params.configForm
393
+ ? html`
394
+ <div class="callout info" style="margin-top: 12px">
395
+ Load the gateway config to set per-agent skills.
396
+ </div>
397
+ `
398
+ : nothing
399
+ }
400
+ ${
401
+ usingAllowlist
402
+ ? html`
403
+ <div class="callout info" style="margin-top: 12px">This agent uses a custom skill allowlist.</div>
404
+ `
405
+ : html`
406
+ <div class="callout info" style="margin-top: 12px">
407
+ All skills are enabled. Disabling any skill will create a per-agent allowlist.
408
+ </div>
409
+ `
410
+ }
411
+ ${
412
+ !reportReady && !params.loading
413
+ ? html`
414
+ <div class="callout info" style="margin-top: 12px">
415
+ Load skills for this agent to view workspace-specific entries.
416
+ </div>
417
+ `
418
+ : nothing
419
+ }
420
+ ${
421
+ params.error
422
+ ? html`<div class="callout danger" style="margin-top: 12px;">${params.error}</div>`
423
+ : nothing
424
+ }
425
+
426
+ <div class="filters" style="margin-top: 14px;">
427
+ <label class="field" style="flex: 1;">
428
+ <span>Filter</span>
429
+ <input
430
+ .value=${params.filter}
431
+ @input=${(e: Event) => params.onFilterChange((e.target as HTMLInputElement).value)}
432
+ placeholder="Search skills"
433
+ />
434
+ </label>
435
+ <div class="muted">${filtered.length} shown</div>
436
+ </div>
437
+
438
+ ${
439
+ filtered.length === 0
440
+ ? html`
441
+ <div class="muted" style="margin-top: 16px">No skills found.</div>
442
+ `
443
+ : html`
444
+ <div class="agent-skills-groups" style="margin-top: 16px;">
445
+ ${groups.map((group) =>
446
+ renderAgentSkillGroup(group, {
447
+ agentId: params.agentId,
448
+ allowSet,
449
+ usingAllowlist,
450
+ editable,
451
+ onToggle: params.onToggle,
452
+ }),
453
+ )}
454
+ </div>
455
+ `
456
+ }
457
+ </section>
458
+ `;
459
+ }
460
+
461
+ function renderAgentSkillGroup(
462
+ group: SkillGroup,
463
+ params: {
464
+ agentId: string;
465
+ allowSet: Set<string>;
466
+ usingAllowlist: boolean;
467
+ editable: boolean;
468
+ onToggle: (agentId: string, skillName: string, enabled: boolean) => void;
469
+ },
470
+ ) {
471
+ const collapsedByDefault = group.id === "workspace" || group.id === "built-in";
472
+ return html`
473
+ <details class="agent-skills-group" ?open=${!collapsedByDefault}>
474
+ <summary class="agent-skills-header">
475
+ <span>${group.label}</span>
476
+ <span class="muted">${group.skills.length}</span>
477
+ </summary>
478
+ <div class="list skills-grid">
479
+ ${group.skills.map((skill) =>
480
+ renderAgentSkillRow(skill, {
481
+ agentId: params.agentId,
482
+ allowSet: params.allowSet,
483
+ usingAllowlist: params.usingAllowlist,
484
+ editable: params.editable,
485
+ onToggle: params.onToggle,
486
+ }),
487
+ )}
488
+ </div>
489
+ </details>
490
+ `;
491
+ }
492
+
493
+ function renderAgentSkillRow(
494
+ skill: SkillStatusEntry,
495
+ params: {
496
+ agentId: string;
497
+ allowSet: Set<string>;
498
+ usingAllowlist: boolean;
499
+ editable: boolean;
500
+ onToggle: (agentId: string, skillName: string, enabled: boolean) => void;
501
+ },
502
+ ) {
503
+ const enabled = params.usingAllowlist ? params.allowSet.has(skill.name) : true;
504
+ const missing = computeSkillMissing(skill);
505
+ const reasons = computeSkillReasons(skill);
506
+ return html`
507
+ <div class="list-item agent-skill-row">
508
+ <div class="list-main">
509
+ <div class="list-title">${skill.emoji ? `${skill.emoji} ` : ""}${skill.name}</div>
510
+ <div class="list-sub">${skill.description}</div>
511
+ ${renderSkillStatusChips({ skill })}
512
+ ${
513
+ missing.length > 0
514
+ ? html`<div class="muted" style="margin-top: 6px;">Missing: ${missing.join(", ")}</div>`
515
+ : nothing
516
+ }
517
+ ${
518
+ reasons.length > 0
519
+ ? html`<div class="muted" style="margin-top: 6px;">Reason: ${reasons.join(", ")}</div>`
520
+ : nothing
521
+ }
522
+ </div>
523
+ <div class="list-meta">
524
+ <label class="cfg-toggle">
525
+ <input
526
+ type="checkbox"
527
+ .checked=${enabled}
528
+ ?disabled=${!params.editable}
529
+ @change=${(e: Event) =>
530
+ params.onToggle(params.agentId, skill.name, (e.target as HTMLInputElement).checked)}
531
+ />
532
+ <span class="cfg-toggle__track"></span>
533
+ </label>
534
+ </div>
535
+ </div>
536
+ `;
537
+ }
@@ -0,0 +1,100 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ resolveConfiguredCronModelSuggestions,
4
+ resolveEffectiveModelFallbacks,
5
+ sortLocaleStrings,
6
+ } from "./agents-utils.ts";
7
+
8
+ describe("resolveEffectiveModelFallbacks", () => {
9
+ it("inherits defaults when no entry fallbacks are configured", () => {
10
+ const entryModel = undefined;
11
+ const defaultModel = {
12
+ primary: "openai/gpt-5-nano",
13
+ fallbacks: ["google/gemini-2.0-flash"],
14
+ };
15
+
16
+ expect(resolveEffectiveModelFallbacks(entryModel, defaultModel)).toEqual([
17
+ "google/gemini-2.0-flash",
18
+ ]);
19
+ });
20
+
21
+ it("prefers entry fallbacks over defaults", () => {
22
+ const entryModel = {
23
+ primary: "openai/gpt-5-mini",
24
+ fallbacks: ["openai/gpt-5-nano"],
25
+ };
26
+ const defaultModel = {
27
+ primary: "openai/gpt-5",
28
+ fallbacks: ["google/gemini-2.0-flash"],
29
+ };
30
+
31
+ expect(resolveEffectiveModelFallbacks(entryModel, defaultModel)).toEqual(["openai/gpt-5-nano"]);
32
+ });
33
+
34
+ it("keeps explicit empty entry fallback lists", () => {
35
+ const entryModel = {
36
+ primary: "openai/gpt-5-mini",
37
+ fallbacks: [],
38
+ };
39
+ const defaultModel = {
40
+ primary: "openai/gpt-5",
41
+ fallbacks: ["google/gemini-2.0-flash"],
42
+ };
43
+
44
+ expect(resolveEffectiveModelFallbacks(entryModel, defaultModel)).toEqual([]);
45
+ });
46
+ });
47
+
48
+ describe("resolveConfiguredCronModelSuggestions", () => {
49
+ it("collects defaults primary/fallbacks, alias map keys, and per-agent model entries", () => {
50
+ const result = resolveConfiguredCronModelSuggestions({
51
+ agents: {
52
+ defaults: {
53
+ model: {
54
+ primary: "openai/gpt-5.2",
55
+ fallbacks: ["google/gemini-2.5-pro", "openai/gpt-5.2-mini"],
56
+ },
57
+ models: {
58
+ "anthropic/claude-sonnet-4-5": { alias: "smart" },
59
+ "openai/gpt-5.2": { alias: "main" },
60
+ },
61
+ },
62
+ list: {
63
+ writer: {
64
+ model: { primary: "xai/grok-4", fallbacks: ["openai/gpt-5.2-mini"] },
65
+ },
66
+ planner: {
67
+ model: "google/gemini-2.5-flash",
68
+ },
69
+ },
70
+ },
71
+ });
72
+
73
+ expect(result).toEqual([
74
+ "anthropic/claude-sonnet-4-5",
75
+ "google/gemini-2.5-flash",
76
+ "google/gemini-2.5-pro",
77
+ "openai/gpt-5.2",
78
+ "openai/gpt-5.2-mini",
79
+ "xai/grok-4",
80
+ ]);
81
+ });
82
+
83
+ it("returns empty array for invalid or missing config shape", () => {
84
+ expect(resolveConfiguredCronModelSuggestions(null)).toEqual([]);
85
+ expect(resolveConfiguredCronModelSuggestions({})).toEqual([]);
86
+ expect(resolveConfiguredCronModelSuggestions({ agents: { defaults: { model: "" } } })).toEqual(
87
+ [],
88
+ );
89
+ });
90
+ });
91
+
92
+ describe("sortLocaleStrings", () => {
93
+ it("sorts values using localeCompare without relying on Array.prototype.toSorted", () => {
94
+ expect(sortLocaleStrings(["z", "b", "a"])).toEqual(["a", "b", "z"]);
95
+ });
96
+
97
+ it("accepts any iterable input, including sets", () => {
98
+ expect(sortLocaleStrings(new Set(["beta", "alpha"]))).toEqual(["alpha", "beta"]);
99
+ });
100
+ });