loopengine 0.1.94 → 0.1.97

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 (68) hide show
  1. package/README.md +21 -2
  2. package/dist/bin/ability-manager.d.ts +4 -0
  3. package/dist/bin/ability-manager.js +34 -2
  4. package/dist/bin/cli.d.ts +1 -1
  5. package/dist/bin/cli.js +3 -0
  6. package/dist/core/ability-details.d.ts +31 -0
  7. package/dist/core/ability-details.js +140 -0
  8. package/dist/core/ability-sources.d.ts +59 -0
  9. package/dist/core/ability-sources.js +312 -0
  10. package/dist/core/agent-config.d.ts +21 -1
  11. package/dist/core/discover-agents.js +11 -1
  12. package/dist/core/http-notifier.d.ts +7 -1
  13. package/dist/core/http-notifier.js +68 -9
  14. package/dist/core/http-notify-triggers/email.d.ts +2 -2
  15. package/dist/core/http-notify-triggers/email.js +2 -4
  16. package/dist/core/http-notify-triggers/lark.d.ts +2 -2
  17. package/dist/core/http-notify-triggers/lark.js +2 -4
  18. package/dist/core/http-notify-triggers/slack.d.ts +2 -2
  19. package/dist/core/http-notify-triggers/slack.js +2 -4
  20. package/dist/core/http-notify-triggers/telegram.d.ts +28 -0
  21. package/dist/core/http-notify-triggers/telegram.js +71 -0
  22. package/dist/core/http-notify-triggers/webhook.d.ts +2 -2
  23. package/dist/core/http-notify-triggers/webhook.js +2 -4
  24. package/dist/core/model-calls/meta-model-call.d.ts +15 -0
  25. package/dist/core/model-calls/meta-model-call.js +48 -0
  26. package/dist/core/model-calls/openai-model-call.d.ts +5 -0
  27. package/dist/core/model-calls/openai-model-call.js +47 -1
  28. package/dist/core/model-calls/xai-model-call.d.ts +15 -0
  29. package/dist/core/model-calls/xai-model-call.js +45 -0
  30. package/dist/core/notifier-file.d.ts +40 -6
  31. package/dist/core/notifier-file.js +119 -25
  32. package/dist/core/system-agents/workflow-planner.d.ts +100 -0
  33. package/dist/core/system-agents/workflow-planner.js +278 -0
  34. package/dist/core/workflows.d.ts +26 -0
  35. package/dist/core/workflows.js +39 -0
  36. package/dist/core/workspace-abilities.d.ts +31 -0
  37. package/dist/core/workspace-abilities.js +110 -0
  38. package/dist/index.d.ts +18 -4
  39. package/dist/index.js +17 -3
  40. package/dist/web/ability-details-admin.d.ts +17 -0
  41. package/dist/web/ability-details-admin.js +45 -0
  42. package/dist/web/ability-sources-admin.d.ts +33 -0
  43. package/dist/web/ability-sources-admin.js +65 -0
  44. package/dist/web/agent-file-admin.d.ts +1 -1
  45. package/dist/web/agent-file-admin.js +3 -0
  46. package/dist/web/agents-config-page.js +608 -224
  47. package/dist/web/agents-list-page.js +49 -20
  48. package/dist/web/console-shell.js +248 -7
  49. package/dist/web/dev-ui-styles.js +13 -1
  50. package/dist/web/global-config-page.js +403 -81
  51. package/dist/web/global-config.d.ts +44 -1
  52. package/dist/web/global-config.js +162 -2
  53. package/dist/web/interview-admin.js +2 -2
  54. package/dist/web/messaging-icons.d.ts +2 -0
  55. package/dist/web/messaging-icons.js +7 -0
  56. package/dist/web/notifier-admin.d.ts +33 -15
  57. package/dist/web/notifier-admin.js +109 -65
  58. package/dist/web/provider-icons.d.ts +2 -0
  59. package/dist/web/provider-icons.js +10 -0
  60. package/dist/web/templates-admin.d.ts +3 -0
  61. package/dist/web/templates-admin.js +5 -1
  62. package/dist/web/workflow-admin.d.ts +124 -0
  63. package/dist/web/workflow-admin.js +308 -0
  64. package/dist/web/workflow-page.d.ts +1 -0
  65. package/dist/web/workflow-page.js +226 -0
  66. package/dist/web/workspace-abilities-admin.d.ts +24 -0
  67. package/dist/web/workspace-abilities-admin.js +118 -0
  68. package/package.json +13 -1
@@ -0,0 +1,10 @@
1
+ export const PROVIDER_ICONS = {
2
+ openai: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" fill="currentColor" fill-rule="evenodd" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"></path></svg>',
3
+ anthropic: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" fill="currentColor" fill-rule="evenodd" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z"></path></svg>',
4
+ gemini: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="#3186FF"></path><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="url(#lobe-icons-gemini-0-_R_0_)"></path><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="url(#lobe-icons-gemini-1-_R_0_)"></path><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="url(#lobe-icons-gemini-2-_R_0_)"></path><defs><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-0-_R_0_" x1="7" x2="11" y1="15.5" y2="12"><stop stop-color="#08B962"></stop><stop offset="1" stop-color="#08B962" stop-opacity="0"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-1-_R_0_" x1="8" x2="11.5" y1="5.5" y2="11"><stop stop-color="#F94543"></stop><stop offset="1" stop-color="#F94543" stop-opacity="0"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-2-_R_0_" x1="3.5" x2="17.5" y1="13.5" y2="12"><stop stop-color="#FABC12"></stop><stop offset=".46" stop-color="#FABC12" stop-opacity="0"></stop></linearGradient></defs></svg>',
5
+ meta: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.897 4h-.024l-.031 2.615h.022c1.715 0 3.046 1.357 5.94 6.246l.175.297.012.02 1.62-2.438-.012-.019a48.763 48.763 0 00-1.098-1.716 28.01 28.01 0 00-1.175-1.629C10.413 4.932 8.812 4 6.896 4z" fill="url(#lobe-icons-meta-0-_R_0_)"></path><path d="M6.873 4C4.95 4.01 3.247 5.258 2.02 7.17a4.352 4.352 0 00-.01.017l2.254 1.231.011-.017c.718-1.083 1.61-1.774 2.568-1.785h.021L6.896 4h-.023z" fill="url(#lobe-icons-meta-1-_R_0_)"></path><path d="M2.019 7.17l-.011.017C1.2 8.447.598 9.995.274 11.664l-.005.022 2.534.6.004-.022c.27-1.467.786-2.828 1.456-3.845l.011-.017L2.02 7.17z" fill="url(#lobe-icons-meta-2-_R_0_)"></path><path d="M2.807 12.264l-2.533-.6-.005.022c-.177.918-.267 1.851-.269 2.786v.023l2.598.233v-.023a12.591 12.591 0 01.21-2.44z" fill="url(#lobe-icons-meta-3-_R_0_)"></path><path d="M2.677 15.537a5.462 5.462 0 01-.079-.813v-.022L0 14.468v.024a8.89 8.89 0 00.146 1.652l2.535-.585a4.106 4.106 0 01-.004-.022z" fill="url(#lobe-icons-meta-4-_R_0_)"></path><path d="M3.27 16.89c-.284-.31-.484-.756-.589-1.328l-.004-.021-2.535.585.004.021c.192 1.01.568 1.85 1.106 2.487l.014.017 2.018-1.745a2.106 2.106 0 01-.015-.016z" fill="url(#lobe-icons-meta-5-_R_0_)"></path><path d="M10.78 9.654c-1.528 2.35-2.454 3.825-2.454 3.825-2.035 3.2-2.739 3.917-3.871 3.917a1.545 1.545 0 01-1.186-.508l-2.017 1.744.014.017C2.01 19.518 3.058 20 4.356 20c1.963 0 3.374-.928 5.884-5.33l1.766-3.13a41.283 41.283 0 00-1.227-1.886z" fill="#0082FB"></path><path d="M13.502 5.946l-.016.016c-.4.43-.786.908-1.16 1.416.378.483.768 1.024 1.175 1.63.48-.743.928-1.345 1.367-1.807l.016-.016-1.382-1.24z" fill="url(#lobe-icons-meta-6-_R_0_)"></path><path d="M20.918 5.713C19.853 4.633 18.583 4 17.225 4c-1.432 0-2.637.787-3.723 1.944l-.016.016 1.382 1.24.016-.017c.715-.747 1.408-1.12 2.176-1.12.826 0 1.6.39 2.27 1.075l.015.016 1.589-1.425-.016-.016z" fill="#0082FB"></path><path d="M23.998 14.125c-.06-3.467-1.27-6.566-3.064-8.396l-.016-.016-1.588 1.424.015.016c1.35 1.392 2.277 3.98 2.361 6.971v.023h2.292v-.022z" fill="url(#lobe-icons-meta-7-_R_0_)"></path><path d="M23.998 14.15v-.023h-2.292v.022c.004.14.006.282.006.424 0 .815-.121 1.474-.368 1.95l-.011.022 1.708 1.782.013-.02c.62-.96.946-2.293.946-3.91 0-.083 0-.165-.002-.247z" fill="url(#lobe-icons-meta-8-_R_0_)"></path><path d="M21.344 16.52l-.011.02c-.214.402-.519.67-.917.787l.778 2.462a3.493 3.493 0 00.438-.182 3.558 3.558 0 001.366-1.218l.044-.065.012-.02-1.71-1.784z" fill="url(#lobe-icons-meta-9-_R_0_)"></path><path d="M19.92 17.393c-.262 0-.492-.039-.718-.14l-.798 2.522c.449.153.927.222 1.46.222.492 0 .943-.073 1.352-.215l-.78-2.462c-.167.05-.341.075-.517.073z" fill="url(#lobe-icons-meta-10-_R_0_)"></path><path d="M18.323 16.534l-.014-.017-1.836 1.914.016.017c.637.682 1.246 1.105 1.937 1.337l.797-2.52c-.291-.125-.573-.353-.9-.731z" fill="url(#lobe-icons-meta-11-_R_0_)"></path><path d="M18.309 16.515c-.55-.642-1.232-1.712-2.303-3.44l-1.396-2.336-.011-.02-1.62 2.438.012.02.989 1.668c.959 1.61 1.74 2.774 2.493 3.585l.016.016 1.834-1.914a2.353 2.353 0 01-.014-.017z" fill="url(#lobe-icons-meta-12-_R_0_)"></path><defs><linearGradient id="lobe-icons-meta-0-_R_0_" x1="75.897%" x2="26.312%" y1="89.199%" y2="12.194%"><stop offset=".06%" stop-color="#0867DF"></stop><stop offset="45.39%" stop-color="#0668E1"></stop><stop offset="85.91%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-1-_R_0_" x1="21.67%" x2="97.068%" y1="75.874%" y2="23.985%"><stop offset="13.23%" stop-color="#0064DF"></stop><stop offset="99.88%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-2-_R_0_" x1="38.263%" x2="60.895%" y1="89.127%" y2="16.131%"><stop offset="1.47%" stop-color="#0072EC"></stop><stop offset="68.81%" stop-color="#0064DF"></stop></linearGradient><linearGradient id="lobe-icons-meta-3-_R_0_" x1="47.032%" x2="52.15%" y1="90.19%" y2="15.745%"><stop offset="7.31%" stop-color="#007CF6"></stop><stop offset="99.43%" stop-color="#0072EC"></stop></linearGradient><linearGradient id="lobe-icons-meta-4-_R_0_" x1="52.155%" x2="47.591%" y1="58.301%" y2="37.004%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#007CF6"></stop></linearGradient><linearGradient id="lobe-icons-meta-5-_R_0_" x1="37.689%" x2="61.961%" y1="12.502%" y2="63.624%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-6-_R_0_" x1="34.808%" x2="62.313%" y1="68.859%" y2="23.174%"><stop offset="27.99%" stop-color="#007FF8"></stop><stop offset="91.41%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-7-_R_0_" x1="43.762%" x2="57.602%" y1="6.235%" y2="98.514%"><stop offset="0%" stop-color="#0082FB"></stop><stop offset="99.95%" stop-color="#0081FA"></stop></linearGradient><linearGradient id="lobe-icons-meta-8-_R_0_" x1="60.055%" x2="39.88%" y1="4.661%" y2="69.077%"><stop offset="6.19%" stop-color="#0081FA"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-9-_R_0_" x1="30.282%" x2="61.081%" y1="59.32%" y2="33.244%"><stop offset="0%" stop-color="#027AF3"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-10-_R_0_" x1="20.433%" x2="82.112%" y1="50.001%" y2="50.001%"><stop offset="0%" stop-color="#0377EF"></stop><stop offset="99.94%" stop-color="#0279F1"></stop></linearGradient><linearGradient id="lobe-icons-meta-11-_R_0_" x1="40.303%" x2="72.394%" y1="35.298%" y2="57.811%"><stop offset=".19%" stop-color="#0471E9"></stop><stop offset="100%" stop-color="#0377EF"></stop></linearGradient><linearGradient id="lobe-icons-meta-12-_R_0_" x1="32.254%" x2="68.003%" y1="19.719%" y2="84.908%"><stop offset="27.65%" stop-color="#0867DF"></stop><stop offset="100%" stop-color="#0471E9"></stop></linearGradient></defs></svg>',
6
+ xai: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" fill="currentColor" fill-rule="evenodd" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.469 8.776L16.512 23h-4.464L2.005 8.776H6.47zm-.004 7.9l2.233 3.164L6.467 23H2l4.465-6.324zM22 2.582V23h-3.659V7.764L22 2.582zM22 1l-9.952 14.095-2.233-3.163L17.533 1H22z"></path></svg>',
7
+ deepseek: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" fill="#4D6BFE"></path></svg>',
8
+ kimi: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M21.846 0a1.923 1.923 0 110 3.846H20.15a.226.226 0 01-.227-.226V1.923C19.923.861 20.784 0 21.846 0z" fill="#1783FF"></path><path d="M11.065 11.199l7.257-7.2c.137-.136.06-.41-.116-.41H14.3a.164.164 0 00-.117.051l-7.82 7.756c-.122.12-.302.013-.302-.179V3.82c0-.127-.083-.23-.185-.23H3.186c-.103 0-.186.103-.186.23V19.77c0 .128.083.23.186.23h2.69c.103 0 .186-.102.186-.23v-3.25c0-.069.025-.135.069-.178l2.424-2.406a.158.158 0 01.205-.023l6.484 4.772a7.677 7.677 0 003.453 1.283c.108.012.2-.095.2-.23v-3.06c0-.117-.07-.212-.164-.227a5.028 5.028 0 01-2.027-.807l-5.613-4.064c-.117-.078-.132-.279-.028-.381z" fill="currentColor"></path></svg>',
9
+ glm: '<svg class="le-provider-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M11.991 23.503a.24.24 0 00-.244.248.24.24 0 00.244.249.24.24 0 00.245-.249.24.24 0 00-.22-.247l-.025-.001zM9.671 5.365a1.697 1.697 0 011.099 2.132l-.071.172-.016.04-.018.054c-.07.16-.104.32-.104.498-.035.71.47 1.279 1.186 1.314h.366c1.309.053 2.338 1.173 2.286 2.523-.052 1.332-1.152 2.38-2.478 2.327h-.174c-.715.018-1.274.64-1.239 1.368 0 .124.018.23.053.337.209.373.54.658.96.8.75.23 1.517-.125 1.9-.782l.018-.035c.402-.64 1.17-.96 1.92-.711.854.284 1.378 1.226 1.099 2.167a1.661 1.661 0 01-2.077 1.102 1.711 1.711 0 01-.907-.711l-.017-.035c-.2-.323-.463-.58-.851-.711l-.056-.018a1.646 1.646 0 00-1.954.746 1.66 1.66 0 01-1.065.764 1.677 1.677 0 01-1.989-1.279c-.209-.906.332-1.83 1.257-2.043a1.51 1.51 0 01.296-.035h.018c.68-.071 1.151-.622 1.116-1.333a1.307 1.307 0 00-.227-.693 2.515 2.515 0 01-.366-1.403 2.39 2.39 0 01.366-1.208c.14-.195.21-.444.227-.693.018-.71-.506-1.261-1.186-1.332l-.07-.018a1.43 1.43 0 01-.299-.07l-.05-.019a1.7 1.7 0 01-1.047-2.114 1.68 1.68 0 012.094-1.101zm-5.575 10.11c.26-.264.639-.367.994-.27.355.096.633.379.728.74.095.362-.007.748-.267 1.013-.402.41-1.053.41-1.455 0a1.062 1.062 0 010-1.482zm14.845-.294c.359-.09.738.024.992.297.254.274.344.665.237 1.025-.107.36-.396.634-.756.718-.551.128-1.1-.22-1.23-.781a1.05 1.05 0 01.757-1.26zm-.064-4.39c.314.32.49.753.49 1.206 0 .452-.176.886-.49 1.206-.315.32-.74.5-1.185.5-.444 0-.87-.18-1.184-.5a1.727 1.727 0 010-2.412 1.654 1.654 0 012.369 0zm-11.243.163c.364.484.447 1.128.218 1.691a1.665 1.665 0 01-2.188.923c-.855-.36-1.26-1.358-.907-2.228a1.68 1.68 0 011.33-1.038c.593-.08 1.183.169 1.547.652zm11.545-4.221c.368 0 .708.2.892.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.892.524c-.568 0-1.03-.47-1.03-1.048 0-.579.462-1.048 1.03-1.048zm-14.358 0c.368 0 .707.2.891.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.891.524c-.569 0-1.03-.47-1.03-1.048 0-.579.461-1.048 1.03-1.048zm10.031-1.475c.925 0 1.675.764 1.675 1.706s-.75 1.705-1.675 1.705-1.674-.763-1.674-1.705c0-.942.75-1.706 1.674-1.706zm-2.626-.684c.362-.082.653-.356.761-.718a1.062 1.062 0 00-.238-1.028 1.017 1.017 0 00-.996-.294c-.547.14-.881.7-.752 1.257.13.558.675.907 1.225.783zm0 16.876c.359-.087.644-.36.75-.72a1.062 1.062 0 00-.237-1.019 1.018 1.018 0 00-.985-.301 1.037 1.037 0 00-.762.717c-.108.361-.017.754.239 1.028.245.263.606.377.953.305l.043-.01zM17.19 3.5a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64a.631.631 0 00-.628.64c0 .355.28.64.628.64zm-10.38 0a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64a.631.631 0 00-.628.64c0 .355.279.64.628.64zm-5.182 7.852a.631.631 0 00-.628.64c0 .354.28.639.628.639a.63.63 0 00.627-.606l.001-.034a.62.62 0 00-.628-.64zm5.182 9.13a.631.631 0 00-.628.64c0 .355.279.64.628.64a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm10.38.018a.631.631 0 00-.628.64c0 .355.28.64.628.64a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64zm5.182-9.148a.631.631 0 00-.628.64c0 .354.279.639.628.639a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm-.384-4.992a.24.24 0 00.244-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249c0 .142.122.249.244.249zM11.991.497a.24.24 0 00.245-.248A.24.24 0 0011.99 0a.24.24 0 00-.244.249c0 .133.108.236.223.247l.021.001zM2.011 6.36a.24.24 0 00.245-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249.24.24 0 00.244.249zm0 11.263a.24.24 0 00-.243.248.24.24 0 00.244.249.24.24 0 00.244-.249.252.252 0 00-.244-.248zm19.995-.018a.24.24 0 00-.245.248.24.24 0 00.245.25.24.24 0 00.244-.25.252.252 0 00-.244-.248z" fill="#3859FF" fill-rule="nonzero"></path></svg>',
10
+ };
@@ -92,3 +92,6 @@ export declare function templateChecklist(template: StarterTemplate, name: strin
92
92
  /** Creates a new agent from the template, with its whole setup, under the
93
93
  * first free name, so the same template can be used more than once. */
94
94
  export declare function createFromTemplate(id: string, deps: TemplateCreateDeps | LegacyTemplateCreateDeps): Promise<CreatedTemplateAgent>;
95
+ /** createFromTemplate for a template that isn't one of the built-in ones:
96
+ * a workflow planned from a description (web/workflow-admin.ts). */
97
+ export declare function createFromTemplateObject(template: StarterTemplate, deps: TemplateCreateDeps): Promise<CreatedTemplateAgent>;
@@ -110,7 +110,11 @@ export function templateChecklist(template, name, deps, failed = []) {
110
110
  export async function createFromTemplate(id, deps) {
111
111
  if (!('scaffoldAgent' in deps))
112
112
  return createFromTemplateLegacy(id, deps);
113
- const template = templateAt(id);
113
+ return createFromTemplateObject(templateAt(id), deps);
114
+ }
115
+ /** createFromTemplate for a template that isn't one of the built-in ones:
116
+ * a workflow planned from a description (web/workflow-admin.ts). */
117
+ export async function createFromTemplateObject(template, deps) {
114
118
  const { candidate, setup } = template;
115
119
  const name = freeAgentName(candidate.agent.name, deps.agentExists);
116
120
  // 1. The agent, and its permissions: everything asks unless a rule says
@@ -0,0 +1,124 @@
1
+ import { type CreatedTemplateAgent, type TemplateChecklistItem, type TemplateCreateDeps } from './templates-admin.js';
2
+ import { type WorkflowPlan, type WorkflowPlannerTools } from '../core/system-agents/workflow-planner.js';
3
+ import { type WorkflowRecord } from '../core/workflows.js';
4
+ import type { ModelCall } from '../core/run-agent.js';
5
+ export declare class WorkflowError extends Error {
6
+ }
7
+ /** What the planner and the checks need to know about an agent. */
8
+ export interface WorkflowAgentInfo {
9
+ name: string;
10
+ tools: string[];
11
+ abilities: string[];
12
+ rules: {
13
+ tool: string;
14
+ decision: 'allow' | 'ask' | 'deny';
15
+ scope: string;
16
+ }[];
17
+ defaultDecision: 'allow' | 'ask' | 'deny';
18
+ /** Its primary messaging app, if any. */
19
+ messaging: string | null;
20
+ /** Tools from its connected services (gateway-tools.yml), by source. */
21
+ connected?: {
22
+ source: string;
23
+ tools: string[];
24
+ }[];
25
+ }
26
+ /** A rule the workflow wants that the agent already decides differently. */
27
+ export interface WorkflowConflict {
28
+ tool: string;
29
+ has: 'allow' | 'ask' | 'deny';
30
+ wants: 'allow' | 'ask' | 'deny';
31
+ }
32
+ export interface WorkflowCheck {
33
+ /** For an existing agent: rules it already decides differently. */
34
+ conflicts: WorkflowConflict[];
35
+ /** Abilities the agent doesn't have yet (they'd be installed). */
36
+ toInstall: string[];
37
+ /** Connected-service tools it doesn't have yet (they'd be copied in). */
38
+ toConnect: string[];
39
+ /** Whether adding to this agent changes nothing it already does. */
40
+ fits: boolean;
41
+ }
42
+ /** Checks a plan against the agent it would be added to. */
43
+ export declare function checkWorkflowForAgent(plan: WorkflowPlan, agent: WorkflowAgentInfo): WorkflowCheck;
44
+ /** Marks each step whose tool comes from some agent's connected service
45
+ * (whatever the model said) and drops that service from the abilities to
46
+ * install: it's copied over, not installed. */
47
+ export declare function resolveConnections(plan: WorkflowPlan, agents: WorkflowAgentInfo[]): void;
48
+ export interface PlannedWorkflow {
49
+ plan: WorkflowPlan;
50
+ /** The check against the suggested agent, when the plan names one. */
51
+ check?: WorkflowCheck;
52
+ /** Why the suggestion was changed, if it was (e.g. a conflict). */
53
+ note?: string;
54
+ }
55
+ /** Plans a workflow from a description, then checks the suggested agent:
56
+ * one that would have a decision changed, or doesn't exist, becomes a new
57
+ * agent instead, saying why. */
58
+ export declare function planWorkflow(description: unknown, modelCall: ModelCall, tools: WorkflowPlannerTools & {
59
+ agentInfo(): Promise<WorkflowAgentInfo[]>;
60
+ }): Promise<PlannedWorkflow>;
61
+ export interface PlanJob {
62
+ id: string;
63
+ status: 'planning' | 'done' | 'failed';
64
+ startedAt: string;
65
+ result?: PlannedWorkflow;
66
+ error?: string;
67
+ }
68
+ /** Starts planning in the background; check on it with getPlanJob. */
69
+ export declare function startPlanJob(run: () => Promise<PlannedWorkflow>): PlanJob;
70
+ export declare function getPlanJob(id: string): PlanJob | undefined;
71
+ export interface CreatedWorkflow {
72
+ agent: string;
73
+ /** A new agent was made for it. */
74
+ newAgent: boolean;
75
+ workflow: string;
76
+ abilities: {
77
+ installed: string[];
78
+ failed: {
79
+ name: string;
80
+ error: string;
81
+ }[];
82
+ };
83
+ /** Rules left as the agent already had them. */
84
+ keptDecisions: WorkflowConflict[];
85
+ checklist: TemplateChecklistItem[];
86
+ schedule?: CreatedTemplateAgent['schedule'];
87
+ scheduleError?: string;
88
+ loadError?: string;
89
+ }
90
+ export interface WorkflowCreateDeps extends TemplateCreateDeps {
91
+ agentDir(name: string): string;
92
+ agentInfo(): Promise<WorkflowAgentInfo[]>;
93
+ /** Makes sure the agent is loaded once its new tools and skill are in place. */
94
+ reloadAgent(name: string): Promise<void>;
95
+ }
96
+ /** Creates the workflow: in a new agent (as a template would), or in an
97
+ * existing one. An existing agent keeps every decision it already has: a
98
+ * conflicting rule is left alone and reported, not changed. */
99
+ export declare function createWorkflow(input: {
100
+ plan?: unknown;
101
+ target?: unknown;
102
+ description?: unknown;
103
+ }, deps: WorkflowCreateDeps): Promise<CreatedWorkflow>;
104
+ export interface AgentWorkflowView extends WorkflowRecord {
105
+ schedule?: {
106
+ taskId: string;
107
+ status: string;
108
+ nextRunAt?: string;
109
+ lastRunAt?: string;
110
+ lastStatus?: string;
111
+ waiting: boolean;
112
+ };
113
+ }
114
+ /** An agent's workflows, each with its schedule's state when it has one. */
115
+ export declare function listAgentWorkflows(agentName: string, agentDir: string): Promise<AgentWorkflowView[]>;
116
+ /** Starts a workflow now: its schedule runs once (a paused one goes back
117
+ * to paused after), or, with no schedule, a one-off run. Questions and
118
+ * approvals go to the agent's messaging app. */
119
+ export declare function runWorkflowNow(agentName: string, agentDir: string, id: string): Promise<{
120
+ taskId: string;
121
+ }>;
122
+ /** Forgets a workflow (its skill, schedule and rules stay). */
123
+ export declare function forgetWorkflow(agentDir: string, id: string): void;
124
+ export declare function agentDirIn(projectDir: string, name: string): string;
@@ -0,0 +1,308 @@
1
+ // Backs "Describe a workflow" (web/workflow-page.ts) and the Workflows list
2
+ // on an agent's Overview: plans a workflow from a description
3
+ // (core/system-agents/workflow-planner.ts), checks the plan against the
4
+ // agent it would go into, and creates it: as a new agent through the same
5
+ // path as a template (web/templates-admin.ts), or added to an existing
6
+ // one without changing anything that agent already does.
7
+ import { join } from 'node:path';
8
+ import { addGatewayTool, readGatewayToolsFromDir } from '../core/gateway-tools.js';
9
+ import { addActauthRule, readActauthConfig } from './actauth-admin.js';
10
+ import { writeSkill } from './skills-admin.js';
11
+ import { createFromTemplateObject, installFailureReason, templateChecklist } from './templates-admin.js';
12
+ import { templateScheduleStart } from '../core/system-agents/templates.js';
13
+ import { planWorkflowWithModel, slugify, workflowTemplate } from '../core/system-agents/workflow-planner.js';
14
+ import { readWorkflows, removeWorkflow, saveWorkflow } from '../core/workflows.js';
15
+ import { createScheduledTask, listScheduledTasks, pauseScheduledTask, runScheduledTaskNow } from '../core/scheduler.js';
16
+ import { workspaceTimeZone } from '../core/time-zone.js';
17
+ export class WorkflowError extends Error {
18
+ }
19
+ /** Checks a plan against the agent it would be added to. */
20
+ export function checkWorkflowForAgent(plan, agent) {
21
+ const conflicts = plan.rules.flatMap((rule) => {
22
+ const existing = agent.rules.find((r) => r.tool === rule.tool && r.scope === '*/*') ?? agent.rules.find((r) => r.tool === rule.tool);
23
+ return existing && existing.decision !== rule.decision ? [{ tool: rule.tool, has: existing.decision, wants: rule.decision }] : [];
24
+ });
25
+ const toInstall = plan.abilities.filter((spec) => !agent.abilities.some((name) => specNames(spec).includes(name)));
26
+ const toConnect = [...new Set(plan.steps.flatMap((s) => (s.connection && s.tool && !agent.tools.includes(s.tool) ? [s.tool] : [])))];
27
+ return { conflicts, toInstall, toConnect, fits: conflicts.length === 0 };
28
+ }
29
+ /** Marks each step whose tool comes from some agent's connected service
30
+ * (whatever the model said) and drops that service from the abilities to
31
+ * install: it's copied over, not installed. */
32
+ export function resolveConnections(plan, agents) {
33
+ const sources = new Set();
34
+ for (const step of plan.steps) {
35
+ if (!step.tool)
36
+ continue;
37
+ const found = agents.flatMap((a) => a.connected ?? []).find((c) => c.tools.includes(step.tool));
38
+ if (!found)
39
+ continue;
40
+ step.connection = found.source;
41
+ if (step.ability === found.source)
42
+ delete step.ability;
43
+ sources.add(found.source);
44
+ }
45
+ plan.abilities = plan.abilities.filter((spec) => !sources.has(spec) || plan.steps.some((s) => s.ability === spec));
46
+ }
47
+ /** Copies the connected-service tools the workflow uses into `target`,
48
+ * from an agent that has them. Returns the tools copied. */
49
+ function copyConnectedTools(plan, target, agents, agentDir) {
50
+ const has = new Set(agents.find((a) => a.name === target)?.tools ?? []);
51
+ const copied = [];
52
+ const failed = [];
53
+ for (const step of plan.steps) {
54
+ const tool = step.tool;
55
+ if (!step.connection || !tool || has.has(tool) || copied.includes(tool))
56
+ continue;
57
+ const from = agents.find((a) => a.connected?.some((c) => c.source === step.connection && c.tools.includes(tool)));
58
+ const entry = from && readGatewayToolsFromDir(agentDir(from.name)).find((e) => e.name === step.connection);
59
+ const slug = tool.slice(String(step.connection).length + 1);
60
+ if (!entry || !entry.slugs.includes(slug)) {
61
+ failed.push({ name: tool, error: `no agent has ${tool} connected any more` });
62
+ continue;
63
+ }
64
+ try {
65
+ const resolved = entry.resolved?.[slug];
66
+ addGatewayTool(target, { provider: entry.provider, name: entry.name, slugs: [slug], ...(entry.cliCommand ? { cliCommand: entry.cliCommand } : {}), ...(resolved ? { resolved: { [slug]: resolved } } : {}) });
67
+ copied.push(tool);
68
+ }
69
+ catch (err) {
70
+ failed.push({ name: tool, error: err instanceof Error ? err.message : String(err) });
71
+ }
72
+ }
73
+ return { copied, failed };
74
+ }
75
+ /** The package names a spec could install as: "lp-x", "@acme/lp-x@1.0.0" →
76
+ * "@acme/lp-x", "file:/…/lp-x" or "github:org/lp-x" → "lp-x". */
77
+ function specNames(spec) {
78
+ const bare = spec.replace(/^file:/, '').replace(/^github:/, '').replace(/#.*$/, '');
79
+ const last = bare.split('/').pop() ?? bare;
80
+ const scoped = spec.match(/^(@[^/]+\/[^@]+)/)?.[1];
81
+ const named = spec.match(/^([^@/:][^@]*)(@.*)?$/)?.[1];
82
+ return [...new Set([scoped, named, last.replace(/@.*$/, '')].filter((n) => Boolean(n)))];
83
+ }
84
+ /** Plans a workflow from a description, then checks the suggested agent:
85
+ * one that would have a decision changed, or doesn't exist, becomes a new
86
+ * agent instead, saying why. */
87
+ export async function planWorkflow(description, modelCall, tools) {
88
+ const text = typeof description === 'string' ? description.trim() : '';
89
+ if (text.length < 15)
90
+ throw new WorkflowError('Describe the workflow in a sentence or two: what starts it, the steps, and where you want to decide.');
91
+ if (text.length > 4000)
92
+ throw new WorkflowError('That description is too long: keep it under 4000 characters.');
93
+ const result = await planWorkflowWithModel(modelCall, text, tools);
94
+ if (!result.plan)
95
+ throw new WorkflowError(result.reply || 'The planner did not produce a plan; try describing the steps more plainly.');
96
+ const plan = result.plan;
97
+ const agents = await tools.agentInfo();
98
+ resolveConnections(plan, agents);
99
+ if (plan.target.kind !== 'existing' || !plan.target.agent)
100
+ return { plan };
101
+ const agent = agents.find((a) => a.name === plan.target.agent);
102
+ if (!agent) {
103
+ plan.target = { kind: 'new', reason: `${plan.target.agent} doesn't exist, so it would be a new agent.` };
104
+ return { plan, note: plan.target.reason };
105
+ }
106
+ const check = checkWorkflowForAgent(plan, agent);
107
+ if (!check.fits) {
108
+ const note = `Adding it to ${agent.name} would change ${check.conflicts.map((c) => `${c.tool} (${c.has} there, ${c.wants} here)`).join(', ')}, so a new agent is suggested.`;
109
+ plan.target = { kind: 'new', reason: note };
110
+ return { plan, check, note };
111
+ }
112
+ return { plan, check };
113
+ }
114
+ const JOB_TTL_MS = 60 * 60 * 1000;
115
+ const planJobs = new Map();
116
+ /** Starts planning in the background; check on it with getPlanJob. */
117
+ export function startPlanJob(run) {
118
+ for (const [id, job] of planJobs)
119
+ if (Date.now() - job.at > JOB_TTL_MS)
120
+ planJobs.delete(id);
121
+ const id = `plan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
122
+ const job = { id, status: 'planning', startedAt: new Date().toISOString(), at: Date.now() };
123
+ planJobs.set(id, job);
124
+ run().then((result) => planJobs.set(id, { ...job, status: 'done', result }), (err) => planJobs.set(id, { ...job, status: 'failed', error: err instanceof Error ? err.message : String(err) }));
125
+ const { at: _at, ...view } = job;
126
+ return view;
127
+ }
128
+ export function getPlanJob(id) {
129
+ const job = planJobs.get(id);
130
+ if (!job)
131
+ return undefined;
132
+ const { at: _at, ...view } = job;
133
+ return view;
134
+ }
135
+ /** A to-do for each step nothing could be found for, first in the list. */
136
+ function withMissingSteps(plan, agent, checklist) {
137
+ const href = `/agents/config?agent=${encodeURIComponent(agent)}&tab=abilities`;
138
+ const missing = plan.steps.flatMap((s) => (s.missing ? [{ label: `Find or add a tool for "${s.title}": ${s.missing}`, href }] : []));
139
+ return [...missing, ...checklist];
140
+ }
141
+ /** Creates the workflow: in a new agent (as a template would), or in an
142
+ * existing one. An existing agent keeps every decision it already has: a
143
+ * conflicting rule is left alone and reported, not changed. */
144
+ export async function createWorkflow(input, deps) {
145
+ const plan = input.plan;
146
+ if (!plan || typeof plan !== 'object' || !Array.isArray(plan.steps) || !plan.skill || !plan.newAgent)
147
+ throw new WorkflowError('Plan the workflow first.');
148
+ const target = (input.target ?? plan.target);
149
+ const description = typeof input.description === 'string' ? input.description.trim().slice(0, 4000) : undefined;
150
+ const record = (scheduleName) => ({
151
+ id: plan.id,
152
+ name: plan.name,
153
+ summary: plan.summary,
154
+ trigger: { kind: plan.trigger.kind, description: plan.trigger.description },
155
+ steps: plan.steps,
156
+ startMessage: plan.startMessage,
157
+ ...(scheduleName ? { scheduleName } : {}),
158
+ ...(description ? { description } : {}),
159
+ createdAt: new Date().toISOString(),
160
+ });
161
+ const agents = await deps.agentInfo();
162
+ resolveConnections(plan, agents);
163
+ const template = workflowTemplate(plan);
164
+ if (target.kind !== 'existing') {
165
+ const created = await createFromTemplateObject(template, deps);
166
+ // Read on its next run: the agent needs no reloading for it.
167
+ const connected = copyConnectedTools(plan, created.name, agents, deps.agentDir);
168
+ const loadError = created.loadError;
169
+ saveWorkflow(deps.agentDir(created.name), record(created.schedule ? plan.name : undefined));
170
+ return {
171
+ agent: created.name,
172
+ newAgent: true,
173
+ workflow: plan.name,
174
+ abilities: { installed: [...created.abilities.installed, ...connected.copied], failed: [...created.abilities.failed, ...connected.failed] },
175
+ keptDecisions: [],
176
+ checklist: withMissingSteps(plan, created.name, created.checklist),
177
+ ...(created.schedule ? { schedule: created.schedule } : {}),
178
+ ...(created.scheduleError ? { scheduleError: created.scheduleError } : {}),
179
+ ...(loadError ? { loadError } : {}),
180
+ };
181
+ }
182
+ const agent = agents.find((a) => a.name === target.agent);
183
+ if (!agent)
184
+ throw new WorkflowError(`There's no agent named ${target.agent ?? '(none)'}.`);
185
+ const check = checkWorkflowForAgent(plan, agent);
186
+ // 1. Rules: only for tools the agent has no rule for.
187
+ const { rules: current } = readActauthConfig(agent.name);
188
+ for (const rule of plan.rules) {
189
+ if (current.some((r) => r.tool === rule.tool))
190
+ continue;
191
+ let name = slugify(`${plan.id}-${rule.tool}`, `${plan.id}-rule`);
192
+ for (let i = 2; current.some((r) => r.name === name); i++)
193
+ name = `${slugify(`${plan.id}-${rule.tool}`)}-${i}`;
194
+ addActauthRule(agent.name, { name, scope: '*/*', tool: rule.tool, decision: rule.decision });
195
+ current.push({ name, scope: '*/*', tool: rule.tool, decision: rule.decision });
196
+ }
197
+ // 2. Abilities it doesn't have yet.
198
+ const installed = [];
199
+ const failed = [];
200
+ for (const spec of check.toInstall) {
201
+ if (!deps.installAbility) {
202
+ failed.push({ name: spec, error: 'installing abilities needs LOOPENGINE_ADMIN_AUTH set on the server' });
203
+ continue;
204
+ }
205
+ try {
206
+ await deps.installAbility(agent.name, spec);
207
+ installed.push(spec);
208
+ }
209
+ catch (err) {
210
+ const message = err instanceof Error ? err.message : String(err);
211
+ if (/already installed/i.test(message))
212
+ continue;
213
+ failed.push({ name: spec, error: installFailureReason(err) });
214
+ }
215
+ }
216
+ // Connected-service tools it doesn't have yet.
217
+ const connected = copyConnectedTools(plan, agent.name, agents, deps.agentDir);
218
+ installed.push(...connected.copied);
219
+ failed.push(...connected.failed);
220
+ // 3. Its steps, as a skill; then load the agent again with all of it.
221
+ writeSkill(agent.name, plan.id, { description: plan.skill.description, body: plan.skill.body });
222
+ let loadError;
223
+ try {
224
+ await deps.reloadAgent(agent.name);
225
+ }
226
+ catch (err) {
227
+ loadError = err instanceof Error ? err.message : String(err);
228
+ }
229
+ // 4. Its schedule, paused until the person turns it on.
230
+ let schedule;
231
+ let scheduleError;
232
+ if (template.setup.schedule) {
233
+ try {
234
+ const startAt = templateScheduleStart(template.setup.schedule);
235
+ const task = await createScheduledTask({
236
+ agent: agent.name,
237
+ message: plan.startMessage,
238
+ schedule: { every: template.setup.schedule.every, ...(startAt ? { start_at: startAt } : {}) },
239
+ task_name: plan.name,
240
+ });
241
+ await pauseScheduledTask(task.task_id);
242
+ schedule = { description: plan.trigger.description, timeZone: workspaceTimeZone(), ...(startAt ? { next_run_at: startAt } : {}) };
243
+ }
244
+ catch (err) {
245
+ scheduleError = err instanceof Error ? err.message : String(err);
246
+ }
247
+ }
248
+ saveWorkflow(deps.agentDir(agent.name), record(schedule ? plan.name : undefined));
249
+ return {
250
+ agent: agent.name,
251
+ newAgent: false,
252
+ workflow: plan.name,
253
+ abilities: { installed, failed },
254
+ keptDecisions: check.conflicts,
255
+ checklist: withMissingSteps(plan, agent.name, templateChecklist(template, agent.name, deps, failed)),
256
+ ...(schedule ? { schedule } : {}),
257
+ ...(scheduleError ? { scheduleError } : {}),
258
+ ...(loadError ? { loadError } : {}),
259
+ };
260
+ }
261
+ /** An agent's workflows, each with its schedule's state when it has one. */
262
+ export async function listAgentWorkflows(agentName, agentDir) {
263
+ const workflows = readWorkflows(agentDir);
264
+ if (!workflows.length)
265
+ return [];
266
+ const tasks = await listScheduledTasks({ agent: agentName });
267
+ return workflows.map((w) => {
268
+ const task = w.scheduleName ? tasks.find((t) => t.task_name === w.scheduleName && t.status !== 'cancelled') : undefined;
269
+ return task
270
+ ? {
271
+ ...w,
272
+ schedule: {
273
+ taskId: task.task_id,
274
+ status: task.status,
275
+ ...(task.next_run_at ? { nextRunAt: task.next_run_at } : {}),
276
+ ...(task.last_run_at ? { lastRunAt: task.last_run_at } : {}),
277
+ ...(task.last_status ? { lastStatus: task.last_status } : {}),
278
+ waiting: Boolean(task.pending) || task.status === 'awaiting_approval',
279
+ },
280
+ }
281
+ : w;
282
+ });
283
+ }
284
+ /** Starts a workflow now: its schedule runs once (a paused one goes back
285
+ * to paused after), or, with no schedule, a one-off run. Questions and
286
+ * approvals go to the agent's messaging app. */
287
+ export async function runWorkflowNow(agentName, agentDir, id) {
288
+ const workflow = readWorkflows(agentDir).find((w) => w.id === id);
289
+ if (!workflow)
290
+ throw new WorkflowError(`No workflow ${id} in ${agentName}.`);
291
+ const views = await listAgentWorkflows(agentName, agentDir);
292
+ const task = views.find((w) => w.id === id)?.schedule;
293
+ if (task) {
294
+ if (task.status === 'running' || task.waiting)
295
+ throw new WorkflowError('It is already running, or waiting for a decision.');
296
+ await runScheduledTaskNow(task.taskId);
297
+ return { taskId: task.taskId };
298
+ }
299
+ const once = await createScheduledTask({ agent: agentName, message: workflow.startMessage, schedule: { run_at: new Date().toISOString() }, task_name: `${workflow.name} (run now)` });
300
+ return { taskId: once.task_id };
301
+ }
302
+ /** Forgets a workflow (its skill, schedule and rules stay). */
303
+ export function forgetWorkflow(agentDir, id) {
304
+ removeWorkflow(agentDir, id);
305
+ }
306
+ export function agentDirIn(projectDir, name) {
307
+ return join(projectDir, 'agents', name);
308
+ }
@@ -0,0 +1 @@
1
+ export declare const workflowPageHtml: string;