@xopcai/xopc 0.0.145 → 0.0.147

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 (380) hide show
  1. package/README.md +2 -2
  2. package/dist/browser-ext/manifest.json +1 -1
  3. package/dist/extensions/demo-memory/src/index.js +6 -4
  4. package/dist/extensions/demo-memory/src/index.js.map +1 -1
  5. package/dist/extensions/telegram/xopc.extension.json +1 -1
  6. package/dist/gateway/static/root/assets/Combination-14nmIgxH.js +41 -0
  7. package/dist/gateway/static/root/assets/activity-store-DXeSSlw1.js +1 -0
  8. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BEUxdhZx.js → agent-avatar-dicebear-CFdJKSjY.js} +1 -1
  9. package/dist/gateway/static/root/assets/agents-DKjbRpbA.js +2 -0
  10. package/dist/gateway/static/root/assets/api-Bs69B5zA.js +1 -0
  11. package/dist/gateway/static/root/assets/{app-management-settings-panel-CRHSZJWi.js → app-management-settings-panel-ag1ViXT_.js} +1 -1
  12. package/dist/gateway/static/root/assets/{appearance-settings-BunADYJb.js → appearance-settings-C6iMpwOM.js} +1 -1
  13. package/dist/gateway/static/root/assets/apps-page-CpX3lBFF.js +1 -0
  14. package/dist/gateway/static/root/assets/{archive-plugin-B6vTkH2A.js → archive-plugin-Dwi3mIBy.js} +1 -1
  15. package/dist/gateway/static/root/assets/{attachment-load-BjZXzaNv.js → attachment-load-BaCDoq40.js} +1 -1
  16. package/dist/gateway/static/root/assets/{automation-suggestion-card-ZDA-Stg1.js → automation-suggestion-card-CwqtFgnw.js} +1 -1
  17. package/dist/gateway/static/root/assets/automations-page-DlupOxFQ.js +6 -0
  18. package/dist/gateway/static/root/assets/{binary-plugins-DYZfLvPz.js → binary-plugins-DoAYhsxa.js} +1 -1
  19. package/dist/gateway/static/root/assets/{block-editor-C3MUlYC9.js → block-editor-Bw72pO7S.js} +31 -31
  20. package/dist/gateway/static/root/assets/browser-settings-page-BwLoUBcZ.js +8 -0
  21. package/dist/gateway/static/root/assets/capability-presets-api-vdbAR65Q.js +1 -0
  22. package/dist/gateway/static/root/assets/capability-presets-settings-panel-BAsKbJgm.js +2 -0
  23. package/dist/gateway/static/root/assets/{channel-recipient-api-fTlTnWB4.js → channel-recipient-api-rTG26jpV.js} +1 -1
  24. package/dist/gateway/static/root/assets/channels-settings-CxFWy0cE.js +1 -0
  25. package/dist/gateway/static/root/assets/channels-status-swr-OKlqTtKJ.js +1 -0
  26. package/dist/gateway/static/root/assets/{connector-logo-BVFNT2zW.js → connector-logo-DIMP4NWY.js} +1 -1
  27. package/dist/gateway/static/root/assets/connectors-page-BjQQwX62.js +2 -0
  28. package/dist/gateway/static/root/assets/{desktop-pet-_jUmoXd3.js → desktop-pet-kQq2e0BN.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-settings-DwGOy4AQ.js → desktop-pet-settings-B6vtozMY.js} +1 -1
  30. package/dist/gateway/static/root/assets/{directory-picker-path-field-akvF0F-u.js → directory-picker-path-field-Dn5-e3v4.js} +1 -1
  31. package/dist/gateway/static/root/assets/dist-C230izr8.js +1 -0
  32. package/dist/gateway/static/root/assets/dist-CxGUk8J-.js +1 -0
  33. package/dist/gateway/static/root/assets/dreaming-settings-BzwIujPG.js +3 -0
  34. package/dist/gateway/static/root/assets/extension-debug-page-D_e_hsEy.js +1 -0
  35. package/dist/gateway/static/root/assets/{extension-page-CwWktRNz.js → extension-page-D15CWHBd.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-settings-page-CuggWOIl.js → extension-settings-page-D4P0n1om.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-api-B7sxIbza.js → gateway-config-api-B4qZctjG.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-settings-DIv0eKMf.js → gateway-settings-BvndV7TP.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-DtoLihQE.js → gateway-startup-retry-BpgMunEb.js} +1 -1
  40. package/dist/gateway/static/root/assets/{goal-create-dialog-C4Kw0mfS.js → goal-create-dialog-DrVHKXHq.js} +1 -1
  41. package/dist/gateway/static/root/assets/goal-detail-page-PHlCwv68.js +4 -0
  42. package/dist/gateway/static/root/assets/{goals-config-api-B0jm5JaA.js → goals-config-api-B6YJB_ct.js} +1 -1
  43. package/dist/gateway/static/root/assets/goals-page-Hg9uyO6e.js +2 -0
  44. package/dist/gateway/static/root/assets/{goals-settings-N5_iL7es.js → goals-settings-BROWCIcL.js} +1 -1
  45. package/dist/gateway/static/root/assets/{heartbeat-settings-XzH3lbRR.js → heartbeat-settings-CoCwIOIg.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-CxkNzw3e.js +259 -0
  47. package/dist/gateway/static/root/assets/index-DjCSOyaN.css +1 -0
  48. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-Bm_AIYQ7.js +1 -0
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-BJWFcxwG.js +2 -0
  50. package/dist/gateway/static/root/assets/local-apps-page-BsiZMrEf.js +1 -0
  51. package/dist/gateway/static/root/assets/{locale-store-CiM-D9jA.js → locale-store-Bp8joZck.js} +1 -1
  52. package/dist/gateway/static/root/assets/logs-page-DOmK61ey.js +7 -0
  53. package/dist/gateway/static/root/assets/{markdown-split-5rJx5flj.js → markdown-split-Dy7QpaAz.js} +1 -1
  54. package/dist/gateway/static/root/assets/{media-plugins-DYXccwAd.js → media-plugins-3Lxliy9D.js} +1 -1
  55. package/dist/gateway/static/root/assets/messages-CQGpD-dC.js +5 -0
  56. package/dist/gateway/static/root/assets/models-hub-panel-DGCZom4o.js +2 -0
  57. package/dist/gateway/static/root/assets/{notes-page-BgLyndCO.js → notes-page-BJtT4vSf.js} +1 -1
  58. package/dist/gateway/static/root/assets/notes-workbench-B_bN7cOR.js +4 -0
  59. package/dist/gateway/static/root/assets/{page-tabs-6Al9_sGz.js → page-tabs-DKB6RDre.js} +1 -1
  60. package/dist/gateway/static/root/assets/{preview-open-alternatives-Cm3G72Hv.js → preview-open-alternatives-Br_YhYHp.js} +1 -1
  61. package/dist/gateway/static/root/assets/project-detail-page-Dq8JouJj.js +1 -0
  62. package/dist/gateway/static/root/assets/projects-page-DvfL7fSe.js +1 -0
  63. package/dist/gateway/static/root/assets/remote-access-hub-DqwMXSXP.js +1 -0
  64. package/dist/gateway/static/root/assets/{save-bar-controls-BYtfzu_F.js → save-bar-controls-BgIE9BBh.js} +1 -1
  65. package/dist/gateway/static/root/assets/{schedule-field-CvIRc6yK.js → schedule-field-BXDP-oMz.js} +1 -1
  66. package/dist/gateway/static/root/assets/{schema-form-CQuKx0n7.js → schema-form-Dhv-SmjG.js} +1 -1
  67. package/dist/gateway/static/root/assets/sessions-page-B_mOZkQk.js +1 -0
  68. package/dist/gateway/static/root/assets/settings-advanced-gate-BHGmH6YS.js +1 -0
  69. package/dist/gateway/static/root/assets/{settings-collapsible-section-CF3HHM-t.js → settings-collapsible-section-CUTExnlK.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-form-section-CwWjoRN6.js → settings-form-section-CmnDhsf0.js} +1 -1
  71. package/dist/gateway/static/root/assets/{settings-loading-skeleton-aUFmJa13.js → settings-loading-skeleton-B79dq-XY.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-page-B2UUU9TF.js → settings-page-Db8uAv3X.js} +1 -1
  73. package/dist/gateway/static/root/assets/{settings-page-layout-DqpbTWqI.js → settings-page-layout-DaFwstQw.js} +1 -1
  74. package/dist/gateway/static/root/assets/setup-status-panel-BUSEj0KK.js +1 -0
  75. package/dist/gateway/static/root/assets/{share-preview-page-BOhlPsS1.js → share-preview-page-Bi00uXzV.js} +1 -1
  76. package/dist/gateway/static/root/assets/shares-settings-DzsTYfuX.js +1 -0
  77. package/dist/gateway/static/root/assets/skill-reload-api-DSsdU081.js +1 -0
  78. package/dist/gateway/static/root/assets/skills-page-DeB1tQZL.js +2 -0
  79. package/dist/gateway/static/root/assets/{system-settings-panel-CWxSvuwD.js → system-settings-panel-Cxaph7hW.js} +1 -1
  80. package/dist/gateway/static/root/assets/{theme-store-C9Dxcppm.js → theme-store-eCTN1RpG.js} +1 -1
  81. package/dist/gateway/static/root/assets/{typed-models-lib-qROAvqTw.js → typed-models-lib-CcZ_tyFY.js} +1 -1
  82. package/dist/gateway/static/root/assets/{use-channel-catalog-DE9cpn4G.js → use-channel-catalog-C4PWbNoi.js} +1 -1
  83. package/dist/gateway/static/root/assets/user-context-page-QTVMx_hG.js +1 -0
  84. package/dist/gateway/static/root/assets/{user-profile-fields-editor-CWxc0ghP.js → user-profile-fields-editor-CPOO9Jz5.js} +1 -1
  85. package/dist/gateway/static/root/assets/{user-profile-settings-panel-DW-Up6vt.js → user-profile-settings-panel-C8cgtSF7.js} +1 -1
  86. package/dist/gateway/static/root/assets/{utils-D53uRXVr.js → utils-CiZK2KwH.js} +1 -1
  87. package/dist/gateway/static/root/assets/voice-api-key-field-b-YZtvUu.js +1 -0
  88. package/dist/gateway/static/root/assets/work-discovery-page-4zVDOl2G.js +1 -0
  89. package/dist/gateway/static/root/assets/work-item-detail-page-CHe2tfkc.js +1 -0
  90. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-D_x9ELJp.js → workflow-run-setup-panel-8SLCYeXE.js} +2 -2
  91. package/dist/gateway/static/root/assets/workflows-page-DEOFqiWn.js +11 -0
  92. package/dist/gateway/static/root/index.html +10 -9
  93. package/dist/package.js +1 -1
  94. package/dist/src/activity/types.d.ts +1 -1
  95. package/dist/src/agent/agent-instance-gateway.d.ts +1 -1
  96. package/dist/src/agent/agent-manager.d.ts +1 -1
  97. package/dist/src/agent/agent-manager.js +5 -4
  98. package/dist/src/agent/agent-manager.js.map +1 -1
  99. package/dist/src/agent/background-review/coordinator.js +0 -1
  100. package/dist/src/agent/background-review/coordinator.js.map +1 -1
  101. package/dist/src/agent/background-review/settings.d.ts +1 -1
  102. package/dist/src/agent/background-review/settings.js +10 -17
  103. package/dist/src/agent/background-review/settings.js.map +1 -1
  104. package/dist/src/agent/bootstrap/filter-bootstrap-files.d.ts +1 -1
  105. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +1 -1
  106. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  107. package/dist/src/agent/bootstrap/load-bootstrap-files.js +2 -3
  108. package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
  109. package/dist/src/agent/code-intelligence/binary.js +1 -1
  110. package/dist/src/agent/context/project-context.d.ts +14 -0
  111. package/dist/src/agent/context/project-context.js +23 -1
  112. package/dist/src/agent/context/project-context.js.map +1 -1
  113. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  114. package/dist/src/agent/context/workspace.d.ts +1 -2
  115. package/dist/src/agent/context/workspace.js +2 -4
  116. package/dist/src/agent/context/workspace.js.map +1 -1
  117. package/dist/src/agent/embedded/run-for-session.js +1 -2
  118. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  119. package/dist/src/agent/embedded/run-turn.js +6 -1
  120. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  121. package/dist/src/agent/embedded/types.d.ts +17 -0
  122. package/dist/src/agent/memory/builtin-memory-store.d.ts +2 -2
  123. package/dist/src/agent/memory/builtin-memory-store.js +2 -2
  124. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  125. package/dist/src/agent/memory/builtin-provider.d.ts +1 -3
  126. package/dist/src/agent/memory/builtin-provider.js +21 -48
  127. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  128. package/dist/src/agent/memory/context/planner.js +48 -9
  129. package/dist/src/agent/memory/context/planner.js.map +1 -1
  130. package/dist/src/agent/memory/context/types.d.ts +7 -1
  131. package/dist/src/agent/memory/context-fence.js +1 -1
  132. package/dist/src/agent/memory/context-fence.js.map +1 -1
  133. package/dist/src/agent/memory/create-memory-manager.js +2 -9
  134. package/dist/src/agent/memory/create-memory-manager.js.map +1 -1
  135. package/dist/src/agent/memory/dreaming/config.js +5 -10
  136. package/dist/src/agent/memory/dreaming/config.js.map +1 -1
  137. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js +2 -2
  138. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js.map +1 -1
  139. package/dist/src/agent/memory/dreaming/scope.d.ts +2 -3
  140. package/dist/src/agent/memory/dreaming/scope.js +8 -8
  141. package/dist/src/agent/memory/dreaming/scope.js.map +1 -1
  142. package/dist/src/agent/memory/manager.d.ts +1 -5
  143. package/dist/src/agent/memory/manager.js +2 -23
  144. package/dist/src/agent/memory/manager.js.map +1 -1
  145. package/dist/src/agent/memory/memory-config.d.ts +3 -3
  146. package/dist/src/agent/memory/memory-config.js +9 -14
  147. package/dist/src/agent/memory/memory-config.js.map +1 -1
  148. package/dist/src/agent/memory/types.d.ts +9 -5
  149. package/dist/src/agent/memory/understanding/service.js +21 -8
  150. package/dist/src/agent/memory/understanding/service.js.map +1 -1
  151. package/dist/src/agent/memory/understanding/types.d.ts +5 -0
  152. package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -1
  153. package/dist/src/agent/memory/user-context-coordinator.js +6 -2
  154. package/dist/src/agent/memory/user-context-coordinator.js.map +1 -1
  155. package/dist/src/agent/orchestration/agent-orchestrator.js +2 -2
  156. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  157. package/dist/src/agent/prompt/memory/index.d.ts +1 -2
  158. package/dist/src/agent/prompt/memory/index.js +6 -7
  159. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  160. package/dist/src/agent/prompt/sections/tooling.js +1 -1
  161. package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
  162. package/dist/src/agent/service/direct-turn-helpers.js +13 -2
  163. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  164. package/dist/src/agent/tools/curated-memory-tool.js +4 -4
  165. package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
  166. package/dist/src/agent/tools/factory.d.ts +1 -1
  167. package/dist/src/agent/tools/factory.js +2 -2
  168. package/dist/src/agent/tools/factory.js.map +1 -1
  169. package/dist/src/agent/tools/memory-tool.js +1 -1
  170. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  171. package/dist/src/agent/workflow/workflow-child-tools.js +2 -3
  172. package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -1
  173. package/dist/src/agent/workspace-runtime/registry.d.ts +5 -5
  174. package/dist/src/agent/workspace-runtime/registry.js +6 -11
  175. package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
  176. package/dist/src/agent-manifest/index.js +2 -2
  177. package/dist/src/agent-manifest/prompt.js +1 -7
  178. package/dist/src/agent-manifest/prompt.js.map +1 -1
  179. package/dist/src/agent-manifest/schema.d.ts +0 -598
  180. package/dist/src/agent-manifest/schema.js +2 -143
  181. package/dist/src/agent-manifest/schema.js.map +1 -1
  182. package/dist/src/agent-manifest/validator.js +0 -12
  183. package/dist/src/agent-manifest/validator.js.map +1 -1
  184. package/dist/src/agent-runtime/index.js +1 -1
  185. package/dist/src/agent-runtime/memory-runtime.d.ts +2 -2
  186. package/dist/src/agent-runtime/memory-runtime.js +8 -7
  187. package/dist/src/agent-runtime/memory-runtime.js.map +1 -1
  188. package/dist/src/agent-runtime/runtime-profile.d.ts +2 -0
  189. package/dist/src/agent-runtime/runtime-profile.js +2 -2
  190. package/dist/src/agent-runtime/runtime-profile.js.map +1 -1
  191. package/dist/src/chat-commands/agent-edit.js +1 -2
  192. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  193. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  194. package/dist/src/cli/commands/init.js +0 -22
  195. package/dist/src/cli/commands/init.js.map +1 -1
  196. package/dist/src/cli/templates.d.ts +1 -1
  197. package/dist/src/cli/templates.js +1 -16
  198. package/dist/src/cli/templates.js.map +1 -1
  199. package/dist/src/commands/agents.config.js +0 -10
  200. package/dist/src/commands/agents.config.js.map +1 -1
  201. package/dist/src/config/agent-profile.js +1 -1
  202. package/dist/src/config/index.js +2 -2
  203. package/dist/src/config/paths.d.ts +0 -1
  204. package/dist/src/config/paths.js +1 -2
  205. package/dist/src/config/paths.js.map +1 -1
  206. package/dist/src/config/schema.d.ts +118 -532
  207. package/dist/src/config/schema.js +32 -26
  208. package/dist/src/config/schema.js.map +1 -1
  209. package/dist/src/extensions/loader.js +2 -1
  210. package/dist/src/extensions/loader.js.map +1 -1
  211. package/dist/src/gateway/agents-admin.d.ts +0 -2
  212. package/dist/src/gateway/agents-admin.js +1 -7
  213. package/dist/src/gateway/agents-admin.js.map +1 -1
  214. package/dist/src/gateway/capability-presets-admin.d.ts +0 -2
  215. package/dist/src/gateway/capability-presets-admin.js.map +1 -1
  216. package/dist/src/gateway/chat-stream/mapper.js +26 -0
  217. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  218. package/dist/src/gateway/chat-stream/protocol.d.ts +16 -1
  219. package/dist/src/gateway/dreaming-automation-reconciler.d.ts +0 -1
  220. package/dist/src/gateway/dreaming-automation-reconciler.js +41 -63
  221. package/dist/src/gateway/dreaming-automation-reconciler.js.map +1 -1
  222. package/dist/src/gateway/heartbeat/service.js +1 -1
  223. package/dist/src/gateway/hono/app.js +3 -1
  224. package/dist/src/gateway/hono/app.js.map +1 -1
  225. package/dist/src/gateway/hono/lib/config-payload.d.ts +0 -130
  226. package/dist/src/gateway/hono/lib/config-payload.js +0 -1
  227. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  228. package/dist/src/gateway/hono/routes/agents.js +6 -12
  229. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  230. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +6 -0
  231. package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
  232. package/dist/src/gateway/hono/routes/capability-presets.js +2 -2
  233. package/dist/src/gateway/hono/routes/capability-presets.js.map +1 -1
  234. package/dist/src/gateway/hono/routes/dreaming.js +9 -20
  235. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  236. package/dist/src/gateway/hono/routes/index.js +4 -0
  237. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  238. package/dist/src/gateway/hono/routes/local-apps.d.ts +5 -0
  239. package/dist/src/gateway/hono/routes/local-apps.js +99 -0
  240. package/dist/src/gateway/hono/routes/local-apps.js.map +1 -0
  241. package/dist/src/gateway/hono/routes/memory.js +48 -65
  242. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  243. package/dist/src/gateway/hono/routes/projects.js +2 -2
  244. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  245. package/dist/src/gateway/hono/routes/work-discovery.d.ts +3 -0
  246. package/dist/src/gateway/hono/routes/work-discovery.js +150 -0
  247. package/dist/src/gateway/hono/routes/work-discovery.js.map +1 -0
  248. package/dist/src/gateway/hono/routes/you.d.ts +3 -0
  249. package/dist/src/gateway/hono/routes/you.js +358 -91
  250. package/dist/src/gateway/hono/routes/you.js.map +1 -1
  251. package/dist/src/gateway/service.d.ts +3 -0
  252. package/dist/src/gateway/service.js +12 -0
  253. package/dist/src/gateway/service.js.map +1 -1
  254. package/dist/src/heartbeat/index.js +1 -1
  255. package/dist/src/knowledge/connected-knowledge-pipeline.js +9 -5
  256. package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -1
  257. package/dist/src/local-apps/acceptance.d.ts +30 -0
  258. package/dist/src/local-apps/acceptance.js +93 -0
  259. package/dist/src/local-apps/acceptance.js.map +1 -0
  260. package/dist/src/local-apps/index.d.ts +5 -0
  261. package/dist/src/local-apps/index.js +4 -0
  262. package/dist/src/local-apps/preview-runtime-bridge.d.ts +4 -0
  263. package/dist/src/local-apps/preview-runtime-bridge.js +171 -0
  264. package/dist/src/local-apps/preview-runtime-bridge.js.map +1 -0
  265. package/dist/src/local-apps/runtime-entry.d.ts +8 -0
  266. package/dist/src/local-apps/runtime-entry.js +25 -0
  267. package/dist/src/local-apps/runtime-entry.js.map +1 -0
  268. package/dist/src/local-apps/scaffold.d.ts +8 -0
  269. package/dist/src/local-apps/scaffold.js +122 -0
  270. package/dist/src/local-apps/scaffold.js.map +1 -0
  271. package/dist/src/local-apps/service.d.ts +37 -0
  272. package/dist/src/local-apps/service.js +553 -0
  273. package/dist/src/local-apps/service.js.map +1 -0
  274. package/dist/src/local-apps/store.d.ts +44 -0
  275. package/dist/src/local-apps/store.js +195 -0
  276. package/dist/src/local-apps/store.js.map +1 -0
  277. package/dist/src/local-apps/types.d.ts +105 -0
  278. package/dist/src/local-apps/types.js +1 -0
  279. package/dist/src/storage/sqlite/index.d.ts +3 -2
  280. package/dist/src/storage/sqlite/index.js +4 -3
  281. package/dist/src/storage/sqlite/memory-index-repository.d.ts +2 -3
  282. package/dist/src/storage/sqlite/memory-index-repository.js +16 -20
  283. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
  284. package/dist/src/storage/sqlite/memory-records-repository.d.ts +18 -10
  285. package/dist/src/storage/sqlite/memory-records-repository.js +79 -34
  286. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  287. package/dist/src/storage/sqlite/memory-reference-consent-repository.d.ts +27 -0
  288. package/dist/src/storage/sqlite/memory-reference-consent-repository.js +109 -0
  289. package/dist/src/storage/sqlite/memory-reference-consent-repository.js.map +1 -0
  290. package/dist/src/storage/sqlite/migrations/038_user_owned_memory.sql +68 -0
  291. package/dist/src/storage/sqlite/migrations/039_memory_reference_consents.sql +19 -0
  292. package/dist/src/storage/sqlite/migrations/040_local_apps.sql +32 -0
  293. package/dist/src/storage/sqlite/migrations/041_local_app_release_lifecycle.sql +17 -0
  294. package/dist/src/storage/sqlite/migrations/042_work_discovery.sql +41 -0
  295. package/dist/src/storage/sqlite/migrations/043_local_app_acceptance_runs.sql +16 -0
  296. package/dist/src/storage/sqlite/migrations/044_repair_work_discovery_foreign_keys.sql +75 -0
  297. package/dist/src/storage/sqlite/migrations/045_extension_ui_grants.sql +13 -0
  298. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  299. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  300. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  301. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  302. package/dist/src/storage/sqlite/schema.js +1 -1
  303. package/dist/src/storage/sqlite/schema.sql +20 -0
  304. package/dist/src/user-context/config.d.ts +227 -0
  305. package/dist/src/user-context/config.js +158 -0
  306. package/dist/src/user-context/config.js.map +1 -0
  307. package/dist/src/user-context/import.d.ts +1 -1
  308. package/dist/src/user-context/import.js +6 -1
  309. package/dist/src/user-context/import.js.map +1 -1
  310. package/dist/src/user-context/owner.d.ts +5 -0
  311. package/dist/src/user-context/owner.js +9 -0
  312. package/dist/src/user-context/owner.js.map +1 -0
  313. package/dist/src/user-context/projection.d.ts +11 -9
  314. package/dist/src/user-context/projection.js +74 -47
  315. package/dist/src/user-context/projection.js.map +1 -1
  316. package/dist/src/user-context/runtime.d.ts +13 -0
  317. package/dist/src/user-context/runtime.js +36 -0
  318. package/dist/src/user-context/runtime.js.map +1 -0
  319. package/dist/src/work-discovery/analyzer.d.ts +11 -0
  320. package/dist/src/work-discovery/analyzer.js +171 -0
  321. package/dist/src/work-discovery/analyzer.js.map +1 -0
  322. package/dist/src/work-discovery/index.d.ts +5 -0
  323. package/dist/src/work-discovery/index.js +5 -0
  324. package/dist/src/work-discovery/probe.d.ts +12 -0
  325. package/dist/src/work-discovery/probe.js +335 -0
  326. package/dist/src/work-discovery/probe.js.map +1 -0
  327. package/dist/src/work-discovery/repository.d.ts +11 -0
  328. package/dist/src/work-discovery/repository.js +100 -0
  329. package/dist/src/work-discovery/repository.js.map +1 -0
  330. package/dist/src/work-discovery/service.d.ts +30 -0
  331. package/dist/src/work-discovery/service.js +387 -0
  332. package/dist/src/work-discovery/service.js.map +1 -0
  333. package/dist/src/work-discovery/types.d.ts +114 -0
  334. package/dist/src/work-discovery/types.js +1 -0
  335. package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
  336. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  337. package/package.json +5 -3
  338. package/skills/engineering/build-xopc-local-app/SKILL.md +45 -0
  339. package/skills/engineering/build-xopc-local-app/references/app-contract.md +33 -0
  340. package/skills/engineering/build-xopc-local-app/scripts/validate-local-app.mjs +72 -0
  341. package/dist/gateway/static/root/assets/Combination-DolwnuKi.js +0 -41
  342. package/dist/gateway/static/root/assets/activity-center-store-3l9ahUqb.js +0 -1
  343. package/dist/gateway/static/root/assets/agents-1bpT0xRO.js +0 -2
  344. package/dist/gateway/static/root/assets/apps-page-Cc4JgVUQ.js +0 -1
  345. package/dist/gateway/static/root/assets/automations-page-mb0fdVpq.js +0 -6
  346. package/dist/gateway/static/root/assets/browser-settings-page-Bb0xhaMN.js +0 -8
  347. package/dist/gateway/static/root/assets/capability-presets-api-BV76yN9a.js +0 -1
  348. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CUcGKZqC.js +0 -2
  349. package/dist/gateway/static/root/assets/channels-settings-BlSDMGdy.js +0 -1
  350. package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +0 -1
  351. package/dist/gateway/static/root/assets/connectors-page-CMgAFGvb.js +0 -2
  352. package/dist/gateway/static/root/assets/dist-C99RGfAH.js +0 -1
  353. package/dist/gateway/static/root/assets/dreaming-settings-BLetQiYY.js +0 -3
  354. package/dist/gateway/static/root/assets/extension-debug-page-Cof2ncxE.js +0 -1
  355. package/dist/gateway/static/root/assets/goal-detail-page-BMmzYpZL.js +0 -4
  356. package/dist/gateway/static/root/assets/goals-page-CGeT77PC.js +0 -2
  357. package/dist/gateway/static/root/assets/index-CSLF84-Q.css +0 -1
  358. package/dist/gateway/static/root/assets/index-I-ZCenVw.js +0 -259
  359. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-x_v0nrIi.js +0 -1
  360. package/dist/gateway/static/root/assets/logs-page-BSh1t8cD.js +0 -7
  361. package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +0 -5
  362. package/dist/gateway/static/root/assets/models-hub-panel-BNpTenAI.js +0 -2
  363. package/dist/gateway/static/root/assets/notes-workbench-teklkhcE.js +0 -4
  364. package/dist/gateway/static/root/assets/project-detail-page-CCB_dKoh.js +0 -1
  365. package/dist/gateway/static/root/assets/projects-page-VOAFAHYj.js +0 -1
  366. package/dist/gateway/static/root/assets/remote-access-hub-USYu64Ru.js +0 -1
  367. package/dist/gateway/static/root/assets/sessions-page-oGC8O5h_.js +0 -1
  368. package/dist/gateway/static/root/assets/settings-advanced-gate-D4rP66R1.js +0 -1
  369. package/dist/gateway/static/root/assets/setup-status-panel-Ck9keROI.js +0 -1
  370. package/dist/gateway/static/root/assets/shares-settings-BYLsCuqj.js +0 -1
  371. package/dist/gateway/static/root/assets/skill-reload-api-BNHUyVML.js +0 -1
  372. package/dist/gateway/static/root/assets/skills-page-CpXbQH4n.js +0 -2
  373. package/dist/gateway/static/root/assets/user-context-page-DbNEK4Yp.js +0 -1
  374. package/dist/gateway/static/root/assets/voice-api-key-field-Cs7MOrg0.js +0 -1
  375. package/dist/gateway/static/root/assets/work-item-detail-page-D1ziG1Pp.js +0 -1
  376. package/dist/gateway/static/root/assets/workflows-page-DHGV_n6i.js +0 -11
  377. package/dist/src/agent/context/workspace-templates/MEMORY.md +0 -33
  378. package/dist/src/agent/memory/access-policy.d.ts +0 -12
  379. package/dist/src/agent/memory/access-policy.js +0 -44
  380. package/dist/src/agent/memory/access-policy.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","names":["apiImpl"],"sources":["../../../src/extensions/loader.ts"],"sourcesContent":["/**\n * Extension Loader and Registry\n * \n * Supports three-tier extension storage:\n * 1. Workspace level (workspace/.extensions/) - highest priority\n * 2. Global level (~/.xopc/extensions/) - shared across workspaces\n * 3. Bundled level (xopc/extensions/) - shipped with xopc\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join, dirname, isAbsolute } from 'path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { createJiti } from 'jiti';\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport { loadConfig } from '../config/loader.js';\nimport {\n resolveAgentWorkspaceDir,\n resolveExtensionsDir,\n resolveWorkspaceExtensionsDir,\n resolveBundledExtensionsDir,\n resolveExtensionSdkPath,\n} from '../config/paths.js';\nimport type { Config } from '../config/config-surface.js';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport type { SessionIndex } from '../session/manager.js';\nimport type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type {\n ExtensionApi,\n ExtensionModule,\n ExtensionManifest,\n ResolvedExtensionConfig,\n DiscoveredExtension,\n} from './types/index.js';\nimport type { ActivationContext } from './activation-planner.js';\nimport { ActivationPlanner, type ActivationLoadPhase } from './activation-planner.js';\nimport { mergeActivationContext } from './activation-context.js';\nimport {\n areExtensionsGloballyDisabled,\n discoverExtensionsFromDisk,\n type DiscoverConfig,\n type ExtensionLoaderOptions,\n} from './discover-extensions.js';\nimport type { ExtensionMetadataSnapshot } from './extension-metadata-snapshot.js';\nimport { ManifestRegistry } from './manifest-registry.js';\nimport { normalizeExtensionManifest } from './normalize-manifest.js';\nimport { checkEngineCompatibility } from './engine-check.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { ExtensionApiImpl, createExtensionLogger, createPathResolver } from './api.js';\nimport { validateSpeechProviderContracts } from './speech-provider-contracts.js';\nimport { validateMediaUnderstandingProviderContracts } from './media-provider-contracts.js';\nimport { createLogger, createServiceLogger } from '../utils/logger.js';\n\n// Security imports\nimport {\n checkExtensionPathSafety,\n isExtensionAllowed,\n provenanceTracker,\n logSecurityIssue,\n DEFAULT_SECURITY_CONFIG,\n type SecurityConfig,\n // Note: ExtensionSourceOrigin is defined locally in this file\n} from './security.js';\n\n// Provider imports\nimport { getProviderRegistry, type ProviderPluginRegistry } from '../providers/plugin-registry.js';\n\n// Slot imports\nimport {\n getSlotRegistry,\n type SlotRegistry,\n type SlotKey,\n} from './slots.js';\n\n// Diagnostics imports\nimport {\n getExtensionCache,\n getExtensionDiagnostics,\n type ExtensionLoaderCache,\n type ExtensionDiagnostics,\n} from './diagnostics.js';\n\nconst EXTENSION_MANIFEST_FILE = 'xopc.extension.json';\n\nconst log = createLogger('ExtensionLoader');\n\nconst DEFAULT_EXTENSION_LOAD_CONCURRENCY = 4;\n\nasync function mapWithConcurrency<T>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n if (items.length === 0) {\n return;\n }\n const queue = [...items];\n const workerCount = Math.min(Math.max(1, concurrency), queue.length);\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n while (queue.length > 0) {\n const item = queue.shift();\n if (item === undefined) {\n break;\n }\n await fn(item);\n }\n }),\n );\n}\n\nexport type { ExtensionLoaderOptions, ExtensionSourceOrigin } from './discover-extensions.js';\nexport { areExtensionsGloballyDisabled } from './discover-extensions.js';\n\n// ============================================================================\n// Extension Registry\n// ============================================================================\n\n// `ExtensionRegistryImpl` moved to `./extension-registry-impl.ts` so `api.ts`\n// can use the class without going through loader.ts (which imports back from\n// api.ts and formed a circular cycle). Re-exported here for backward-compat.\nexport { ExtensionRegistryImpl } from './extension-registry-impl.js';\nimport { ExtensionRegistryImpl } from './extension-registry-impl.js';\n\n// ============================================================================\n// Extension Loader\n// ============================================================================\n\nexport interface ActivationPlanLoadOptions extends Partial<ActivationContext> {\n phase?: ActivationLoadPhase | 'all';\n}\n\nexport class ExtensionLoader {\n private registry: ExtensionRegistryImpl;\n private options: ExtensionLoaderOptions;\n private extensionInstances: Map<string, ExtensionApi> = new Map();\n private jiti: ReturnType<typeof createJiti>;\n private _appConfig?: Config;\n private _runtimeContext?: {\n bus?: MessageBus;\n sessionManager?: SessionIndex;\n scheduleWebchatContinuation?: (sessionKey: string, message: string) => void;\n setLabel?: (entryId: string, label: string | undefined) => void;\n sendUserMessage?: import('./types/index.js').ExtensionRuntime['sendUserMessage'];\n appendEntry?: import('./types/index.js').ExtensionRuntime['appendEntry'];\n sendMessage?: import('./types/index.js').ExtensionRuntime['sendMessage'];\n };\n \n // Security\n private securityConfig: SecurityConfig;\n \n // Provider Registry\n private providerRegistry: ProviderPluginRegistry;\n \n // Slot Registry & Config\n private slotRegistry: SlotRegistry;\n private slotsConfig: Partial<Record<SlotKey, string>> = {};\n \n // Cache and Diagnostics\n private cache: ExtensionLoaderCache;\n private diagnostics: ExtensionDiagnostics;\n\n /** Manifest-only registry cache (no runtime module load). */\n private manifestRegistry: ManifestRegistry | null = null;\n private manifestSnapshot: ExtensionMetadataSnapshot | null = null;\n\n constructor(options?: ExtensionLoaderOptions) {\n this.registry = new ExtensionRegistryImpl();\n this.options = options || (() => {\n const c = loadConfig();\n const aid = resolveDefaultAgentId(c);\n return {\n workspaceDir: resolveAgentWorkspaceDir(c, aid),\n extensionsDir: resolveWorkspaceExtensionsDir(c, aid),\n };\n })();\n\n // Initialize security config\n this.securityConfig = DEFAULT_SECURITY_CONFIG;\n \n // Initialize provider registry\n this.providerRegistry = getProviderRegistry();\n \n // Initialize slot registry\n this.slotRegistry = getSlotRegistry();\n \n // Initialize cache and diagnostics \n this.cache = getExtensionCache();\n this.diagnostics = getExtensionDiagnostics();\n\n // Build jiti aliases for the public, host-provided extension SDK contract.\n // Extensions must import from @xopcai/xopc/extension-sdk; private aliases\n // are intentionally not registered.\n const alias: Record<string, string> = {};\n const sdkPath = resolveExtensionSdkPath();\n if (sdkPath) {\n const sdkDir = dirname(sdkPath);\n const sdkSubpaths = [\n 'core',\n 'lazy',\n 'provider',\n 'channel',\n 'hooks',\n 'tools',\n 'testing',\n 'speech',\n 'media',\n ];\n const prefix = '@xopcai/xopc/extension-sdk';\n alias[prefix] = sdkPath;\n for (const subpath of sdkSubpaths) {\n alias[`${prefix}/${subpath}`] = join(sdkDir, `${subpath}.ts`);\n }\n }\n\n // Initialize jiti with TypeScript support and SDK alias\n this.jiti = createJiti(fileURLToPath(import.meta.url), {\n interopDefault: true,\n extensions: ['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs', '.json'],\n alias,\n });\n }\n\n /**\n * Reuse a pre-built metadata snapshot (avoids duplicate filesystem discovery).\n */\n setManifestSnapshot(snapshot: ExtensionMetadataSnapshot | null): void {\n this.manifestSnapshot = snapshot;\n this.manifestRegistry = snapshot?.manifestRegistry ?? null;\n }\n\n getManifestSnapshot(): ExtensionMetadataSnapshot | null {\n return this.manifestSnapshot;\n }\n\n /**\n * Set security configuration \n */\n setSecurityConfig(config: Partial<SecurityConfig>): void {\n this.securityConfig = { ...this.securityConfig, ...config };\n }\n\n /**\n * Get security configuration \n */\n getSecurityConfig(): SecurityConfig {\n return this.securityConfig;\n }\n\n /**\n * Get provider registry \n */\n getProviderRegistry(): ProviderPluginRegistry {\n return this.providerRegistry;\n }\n\n /**\n * Get slot registry \n */\n getSlotRegistry(): SlotRegistry {\n return this.slotRegistry;\n }\n\n /**\n * Get diagnostics \n */\n getDiagnostics(): ExtensionDiagnostics {\n return this.diagnostics;\n }\n\n /**\n * Set configuration from main Config object \n */\n setConfig(config: Config): void {\n this._appConfig = config;\n // Wire slot config\n const slots = (config.extensions as any)?.slots || {};\n this.slotsConfig = {\n memory: slots.memory,\n tts: slots.tts,\n imageGeneration: slots.imageGeneration,\n webSearch: slots.webSearch,\n };\n\n // Wire security config\n const security = (config.extensions as any)?.security;\n if (security) {\n this.securityConfig = {\n checkPermissions: security.checkPermissions ?? true,\n allowUntrusted: security.allowUntrusted ?? false,\n allow: security.allow ?? [],\n trackProvenance: security.trackProvenance ?? true,\n allowPromptInjection: security.allowPromptInjection ?? false,\n };\n }\n }\n\n /**\n * Inject MessageBus and optional SessionManager for ExtensionApi.runtime (Gateway).\n */\n setRuntimeContext(ctx: {\n bus?: MessageBus;\n sessionManager?: SessionIndex;\n scheduleWebchatContinuation?: (sessionKey: string, message: string) => void;\n setLabel?: (entryId: string, label: string | undefined) => void;\n sendUserMessage?: import('./types/index.js').ExtensionRuntime['sendUserMessage'];\n appendEntry?: import('./types/index.js').ExtensionRuntime['appendEntry'];\n sendMessage?: import('./types/index.js').ExtensionRuntime['sendMessage'];\n }): void {\n this._runtimeContext = ctx;\n // ExtensionApi snapshots `runtime` at construction and is tied to this loader's registry.\n // CLI startup loads extensions with `{ bus }` first; a global cache hit would reuse that\n // api for the gateway loader (no sessionManager, wrong registry) — breaks extensions that need SessionManager.\n this.cache.invalidate();\n }\n\n getRegistry(): ExtensionRegistryImpl {\n return this.registry;\n }\n\n /**\n * Build the manifest registry from filesystem manifests without loading extension code.\n */\n buildManifestRegistry(): ManifestRegistry {\n if (this.manifestRegistry) {\n return this.manifestRegistry;\n }\n const discovered = this.discoverExtensions();\n this.manifestRegistry = ManifestRegistry.fromDiscovered(discovered);\n return this.manifestRegistry;\n }\n\n /**\n * Plan activation from manifest metadata with pure logic.\n */\n planActivation(_context?: Partial<ActivationContext>): ActivationPlanner {\n return new ActivationPlanner(this.buildManifestRegistry());\n }\n\n /**\n * Load only extensions selected by the activation plan.\n */\n async loadByActivationPlan(context?: ActivationPlanLoadOptions): Promise<void> {\n if (areExtensionsGloballyDisabled(this._appConfig as DiscoverConfig | undefined)) {\n log.debug('Extension loading skipped (extensions globally disabled)');\n return;\n }\n\n const registry = this.buildManifestRegistry();\n const planner = new ActivationPlanner(registry);\n const fullContext = mergeActivationContext(this._appConfig, context);\n let activatedIds = planner.getActivatedIds(fullContext).sort((a, b) => a.localeCompare(b));\n\n const phase = context?.phase ?? 'all';\n if (phase === 'startup' || phase === 'deferred') {\n activatedIds = planner.filterActivatedIdsByLoadPhase(activatedIds, phase);\n }\n\n const concurrency = Number.parseInt(\n process.env.XOPC_EXTENSION_LOAD_CONCURRENCY ?? '',\n 10,\n );\n const loadConcurrency = Number.isFinite(concurrency) && concurrency > 0\n ? concurrency\n : DEFAULT_EXTENSION_LOAD_CONCURRENCY;\n\n await mapWithConcurrency(activatedIds, loadConcurrency, async (extensionId) => {\n if (this.extensionInstances.has(extensionId)) {\n return;\n }\n const entry = registry.getEntry(extensionId);\n if (!entry) {\n log.debug(\n { extensionId },\n 'Activation plan references extension id that was not discovered on disk',\n );\n return;\n }\n\n const config: ResolvedExtensionConfig = {\n id: extensionId,\n name: entry.manifest.name || extensionId,\n source: entry.source,\n path: entry.path,\n enabled: true,\n config: this.resolveExtensionConfig(extensionId),\n };\n\n await this.loadExtension(config);\n });\n }\n\n getManifestRegistry(): ManifestRegistry {\n return this.buildManifestRegistry();\n }\n\n invalidateManifestCache(): void {\n this.manifestRegistry = this.manifestSnapshot?.manifestRegistry ?? null;\n }\n\n private resolveExtensionConfig(extensionId: string): Record<string, unknown> {\n if (!this._appConfig) return {};\n const extensionsConfig = (this._appConfig as Record<string, unknown>).extensions as\n | Record<string, unknown>\n | undefined;\n if (!extensionsConfig || typeof extensionsConfig !== 'object') return {};\n const raw = extensionsConfig[extensionId];\n if (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as Record<string, unknown>;\n }\n return {};\n }\n\n /**\n * Discover extensions from all three tiers:\n * 1. Workspace (.extensions/) - highest priority\n * 2. Global (~/.xopc/extensions/) - shared\n * 3. Bundled (xopc/extensions/) - lowest priority\n */\n discoverExtensions(): DiscoveredExtension[] {\n if (this.manifestSnapshot) {\n return this.manifestSnapshot.discovered;\n }\n return discoverExtensionsFromDisk(this.options, this._appConfig as DiscoverConfig | undefined);\n }\n\n /**\n * Load all discovered extensions\n */\n async loadAllExtensions(enabledIds?: string[]): Promise<void> {\n const extensions = this.discoverExtensions();\n\n for (const extension of extensions) {\n // If enabledIds specified, only load those\n if (enabledIds && !enabledIds.includes(extension.id)) {\n continue;\n }\n\n const config: ResolvedExtensionConfig = {\n id: extension.id,\n name: extension.manifest.name || extension.id,\n source: extension.source,\n path: extension.path,\n enabled: true,\n config: {},\n };\n\n await this.loadExtension(config);\n }\n }\n\n async loadExtensions(configs: ResolvedExtensionConfig[]): Promise<void> {\n for (const extensionConfig of configs) {\n if (extensionConfig.enabled) {\n await this.loadExtension(extensionConfig);\n }\n }\n }\n\n async loadExtension(config: ResolvedExtensionConfig): Promise<ExtensionApi | null> {\n try {\n // Check cache first\n const cacheKey = this.cache.buildKey(this.options, [config.id]);\n const cached = this.cache.get<ExtensionApi>(cacheKey);\n if (cached) {\n log.debug({ extensionId: config.id }, 'Extension loaded from cache');\n return cached;\n }\n\n // Check if already loaded\n if (this.extensionInstances.has(config.id)) {\n return this.extensionInstances.get(config.id)!;\n }\n\n // Resolve extension path using resolveExtensionPath\n let extensionPath: string | null;\n if (isAbsolute(config.path)) {\n extensionPath = config.path;\n } else {\n // Try to resolve extension ID to actual path\n extensionPath = resolveExtensionPath(config.path, this.options) ||\n resolveExtensionPath(config.id, this.options);\n }\n \n if (!extensionPath) {\n log.error({ extensionId: config.id, path: config.path }, `Could not resolve extension path`);\n this.diagnostics.error(config.id, `Could not resolve extension path: ${config.path}`);\n return null;\n }\n\n log.debug({ extensionId: config.id, extensionPath }, 'Resolved extension path');\n\n // Security check\n const source = config.source || 'bundled';\n const safetyResult = checkExtensionPathSafety(extensionPath, extensionPath, source);\n \n if (!safetyResult.safe) {\n logSecurityIssue(config.id, safetyResult);\n \n if (this.securityConfig.checkPermissions && source !== 'bundled') {\n // Check allowlist\n if (!isExtensionAllowed(config.id, this.securityConfig)) {\n this.diagnostics.error(config.id, `Extension not allowed: ${safetyResult.detail}`);\n log.error({ extensionId: config.id, reason: safetyResult.reason }, 'Extension blocked by security policy');\n return null;\n }\n }\n }\n\n // Track provenance \n provenanceTracker.track(config.id, source);\n\n const manifest = this.loadManifest(extensionPath);\n if (!manifest) {\n log.error({ extensionId: config.id, extensionPath }, `Failed to load manifest for extension`);\n this.diagnostics.error(config.id, `Failed to load manifest`);\n return null;\n }\n\n if (!manifest.main || !/\\.(js|mjs|cjs)$/i.test(manifest.main)) {\n const reason = `Extension manifest main must point at built JavaScript (.js/.mjs/.cjs): ${manifest.main ?? '(missing)'}`;\n log.warn({ extensionId: config.id, main: manifest.main }, reason);\n this.diagnostics.error(config.id, reason);\n return null;\n }\n\n if (!manifest.engines?.xopc) {\n const reason = 'Extension manifest must declare engines.xopc';\n log.warn({ extensionId: config.id }, reason);\n this.diagnostics.error(config.id, reason);\n return null;\n }\n const range = manifest.engines.xopc;\n const engineResult = checkEngineCompatibility(PACKAGE_VERSION, range);\n if (engineResult.parseWarning) {\n const reason = engineResult.reason ?? `Could not parse engines.xopc: ${range}`;\n log.warn({ extensionId: config.id, range, reason }, 'Extension engine range parse failure — skipping load');\n this.diagnostics.error(config.id, reason);\n return null;\n }\n if (!engineResult.compatible) {\n log.warn(\n { extensionId: config.id, range, currentVersion: PACKAGE_VERSION },\n 'Extension engine requirement not met (engines.xopc) — skipping load',\n );\n this.diagnostics.error(\n config.id,\n engineResult.reason ?? 'Incompatible xopc version (engines.xopc)',\n );\n return null;\n }\n\n // Validate extension config against schema (basic validation)\n if (manifest.configSchema) {\n try {\n const schema = manifest.configSchema as Record<string, unknown>;\n const extensionConfig = config.config as Record<string, unknown>;\n \n // Basic validation: check required fields and types\n if (schema.type === 'object' && schema.properties) {\n const props = schema.properties as Record<string, Record<string, unknown>>;\n const required = (schema.required as string[]) || [];\n \n for (const field of required) {\n if (extensionConfig[field] === undefined) {\n log.error({ \n extensionId: config.id, \n field \n }, 'Extension config validation failed: missing required field');\n return null;\n }\n }\n \n for (const [key, value] of Object.entries(extensionConfig)) {\n const propSchema = props[key];\n if (propSchema) {\n if (propSchema.type && !this.validateType(value, propSchema.type as string)) {\n log.error({ \n extensionId: config.id, \n field: key,\n expected: propSchema.type,\n actual: typeof value\n }, 'Extension config validation failed: type mismatch');\n return null;\n }\n }\n }\n }\n \n log.debug({ extensionId: config.id }, 'Extension config validated');\n } catch (err) {\n log.warn({ err, extensionId: config.id }, 'Config schema validation skipped');\n }\n }\n\n // Create extension API\n const extensionDir = dirname(extensionPath);\n const api = this.createExtensionApi(manifest, config, extensionDir);\n\n // Load extension module\n const module = await this.loadModule(extensionPath, manifest);\n if (!module) {\n log.error({ extensionId: config.id }, `Failed to load module for extension`);\n this.diagnostics.error(config.id, `Failed to load module`);\n return null;\n }\n\n // Check and claim slots\n const slotClaimed = this.claimExtensionSlots(config.id, manifest);\n if (!slotClaimed) {\n log.warn({ extensionId: config.id }, 'Failed to claim required slots');\n }\n\n // Initialize extension\n await this.initializeExtension(module, api, manifest);\n\n validateSpeechProviderContracts({\n extensionId: config.id,\n manifest,\n registeredProviderIds: (api as ExtensionApiImpl).getRegisteredSpeechProviderIds(),\n logger: api.logger,\n });\n\n validateMediaUnderstandingProviderContracts({\n extensionId: config.id,\n manifest,\n registeredProviderIds: (api as ExtensionApiImpl).getRegisteredMediaUnderstandingProviderIds(),\n logger: api.logger,\n });\n\n // Register to registry\n this.registry.addExtension({\n id: config.id,\n name: manifest.name,\n version: manifest.version,\n path: extensionPath,\n module,\n config: config.config,\n enabled: true,\n source: config.source,\n });\n\n // Register extension tools to registry (so AgentManager can access them)\n // Note: api is actually ExtensionApiImpl at runtime\n const apiImpl = api as unknown as { _getTools: () => Map<string, AgentTool> };\n const extensionTools = apiImpl._getTools();\n for (const tool of extensionTools.values()) {\n this.registry.addTool(tool);\n }\n\n this.extensionInstances.set(config.id, api);\n \n // Cache the loaded extension\n this.cache.set(cacheKey, api);\n\n this.diagnostics.info(config.id, `Loaded extension: ${manifest.name}`);\n log.info({ name: manifest.name, id: manifest.id, source: config.source }, `Loaded extension`);\n\n return api;\n } catch (error) {\n log.error({ err: error, extensionId: config.id }, `Error loading extension`);\n this.diagnostics.error(config.id, `Error loading extension: ${error}`);\n return null;\n }\n }\n\n /**\n * Claim extension slots based on manifest kind\n * Respects configured preferred plugin from config.extensions.slots\n */\n private claimExtensionSlots(extensionId: string, manifest: ExtensionManifest): boolean {\n const kind = manifest.kind as string;\n \n // Map extension kind to slot\n const slotMap: Record<string, SlotKey> = {\n 'memory': 'memory',\n 'tts': 'tts',\n 'speech-provider': 'tts',\n 'web-search': 'webSearch',\n };\n \n const slotKey = slotMap[kind];\n if (!slotKey) {\n return true; // No slot required\n }\n \n // Check if slot is reserved for a different plugin in config\n const preferredPlugin = this.slotsConfig[slotKey];\n if (preferredPlugin && preferredPlugin !== extensionId) {\n log.info(\n { extensionId, slotKey, preferredPlugin },\n `Skipping slot claim: slot \"${slotKey}\" is reserved for \"${preferredPlugin}\"`\n );\n this.diagnostics.info(\n extensionId,\n `Slot \"${slotKey}\" is reserved for \"${preferredPlugin}\", skipping claim`\n );\n return false;\n }\n \n const claimed = this.slotRegistry.claim(slotKey, extensionId, null);\n if (!claimed) {\n this.diagnostics.warn(extensionId, `Slot \"${slotKey}\" already claimed by another extension`);\n }\n return claimed;\n }\n\n loadManifest(extensionPath: string): ExtensionManifest | null {\n const manifestPath = join(extensionPath, EXTENSION_MANIFEST_FILE);\n\n // First try to load xopc.extension.json\n if (existsSync(manifestPath)) {\n try {\n const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n log.error({ manifestPath }, 'Manifest root must be a JSON object');\n return null;\n }\n return normalizeExtensionManifest(raw as Record<string, unknown>);\n } catch (error) {\n log.error({ err: error, manifestPath }, `Failed to parse manifest`);\n return null;\n }\n }\n\n return null;\n }\n\n private moduleEntryCandidates(manifest: ExtensionManifest): string[] {\n if (!manifest.main) return [];\n return [manifest.main];\n }\n\n private async loadModule(\n extensionPath: string,\n manifest: ExtensionManifest,\n ): Promise<ExtensionModule | null> {\n const entryPoints = this.moduleEntryCandidates(manifest);\n\n for (const entry of entryPoints) {\n const fullPath = isAbsolute(entry) ? entry : join(extensionPath, entry);\n\n if (existsSync(fullPath)) {\n try {\n if (!/\\.(js|mjs|cjs)$/.test(fullPath)) {\n throw new Error(`Extension runtime entry must be built JavaScript: ${entry}`);\n }\n const mod = await import(pathToFileURL(fullPath).href);\n return mod.default || mod;\n } catch (error) {\n log.warn({ err: error, path: fullPath }, `Failed to load module`);\n }\n }\n }\n\n return null;\n }\n\n private createExtensionApi(\n manifest: ExtensionManifest,\n resolved: ResolvedExtensionConfig,\n extensionDir: string,\n ): ExtensionApi {\n const logger = createExtensionLogger(`[${manifest.id}]`);\n const resolvePath = createPathResolver(extensionDir, this.options.workspaceDir || '');\n\n const appConfig = this._appConfig ?? ({} as Config);\n const extensionOnly = (resolved.config ?? {}) as Record<string, unknown>;\n\n const runtime =\n this._appConfig && this._runtimeContext\n ? {\n config: this._appConfig,\n bus: this._runtimeContext.bus,\n log: logger,\n sessionManager: this._runtimeContext.sessionManager,\n scheduleWebchatContinuation: this._runtimeContext.scheduleWebchatContinuation,\n setLabel: this._runtimeContext.setLabel,\n sendUserMessage: this._runtimeContext.sendUserMessage,\n appendEntry: this._runtimeContext.appendEntry,\n sendMessage: this._runtimeContext.sendMessage,\n }\n : this._appConfig\n ? {\n config: this._appConfig,\n log: logger,\n }\n : undefined;\n\n const api = new ExtensionApiImpl(\n manifest.id,\n manifest.name,\n manifest.version,\n extensionDir,\n appConfig,\n extensionOnly,\n logger,\n resolvePath,\n this.registry,\n manifest.contracts ?? {},\n runtime,\n );\n if (manifest.reload?.configPrefixes?.length) {\n api._setReloadConfigPrefixes(manifest.reload.configPrefixes);\n }\n const manifestCommands = manifest.ui?.contributions?.commands;\n if (manifestCommands?.length) {\n (api as ExtensionApiImpl)._setManifestCommands(manifestCommands);\n }\n return api;\n }\n\n private async initializeExtension(\n module: ExtensionModule,\n api: ExtensionApi,\n _manifest: ExtensionManifest,\n ): Promise<void> {\n if (typeof module === 'function') {\n // Module is a function that receives the API\n await module(api);\n } else if (typeof module === 'object' && module.register) {\n // Module is a ExtensionDefinition with register method\n await module.register(api);\n }\n }\n\n async startServices(): Promise<void> {\n const services = Array.from(this.registry.services.values());\n\n for (const service of services) {\n const serviceLog = createServiceLogger(service.id);\n try {\n await service.start?.();\n serviceLog.info(`Started service`);\n } catch (error) {\n serviceLog.error({ err: error }, `Failed to start service`);\n }\n }\n }\n\n async stopServices(): Promise<void> {\n const services = Array.from(this.registry.services.values()).reverse();\n\n for (const service of services) {\n if (service.stop) {\n const serviceLog = createServiceLogger(service.id);\n try {\n await service.stop();\n serviceLog.info(`Stopped service`);\n } catch (error) {\n serviceLog.error({ err: error }, `Failed to stop service`);\n }\n }\n }\n }\n\n /**\n * Basic type validation for config values\n */\n private validateType(value: unknown, expectedType: string): boolean {\n if (expectedType === 'string') return typeof value === 'string';\n if (expectedType === 'number' || expectedType === 'integer') return typeof value === 'number';\n if (expectedType === 'boolean') return typeof value === 'boolean';\n if (expectedType === 'array') return Array.isArray(value);\n if (expectedType === 'object') return typeof value === 'object' && value !== null && !Array.isArray(value);\n return true;\n }\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\nexport function resolveExtensionPath(id: string, _options: ExtensionLoaderOptions): string | null {\n const c = loadConfig();\n const aid = resolveDefaultAgentId(c);\n // Priority 1: Workspace\n const workspacePath = join(resolveWorkspaceExtensionsDir(c, aid), id);\n if (existsSync(workspacePath)) return workspacePath;\n\n // Priority 2: Global\n const globalPath = join(resolveExtensionsDir(), id);\n if (existsSync(globalPath)) return globalPath;\n\n // Priority 3: Bundled\n const bundledDir = resolveBundledExtensionsDir();\n if (bundledDir) {\n const bundledPath = join(bundledDir, id);\n if (existsSync(bundledPath)) return bundledPath;\n }\n\n return null;\n}\n\nexport function normalizeExtensionConfig(\n rawConfig: Record<string, unknown>,\n): ResolvedExtensionConfig[] {\n const extensions: ResolvedExtensionConfig[] = [];\n\n const enabled = (rawConfig.enabled as string[]) || [];\n const disabled = (rawConfig.disabled as string[]) || [];\n\n // Parse enabled extensions\n for (const id of enabled) {\n const config = (rawConfig[id] as Record<string, unknown>) || {};\n extensions.push({\n id,\n name: id,\n source: 'config',\n path: id,\n enabled: true,\n config,\n });\n }\n\n // Parse disabled extensions\n for (const id of disabled) {\n if (!extensions.find((p) => p.id === id)) {\n const config = (rawConfig[id] as Record<string, unknown>) || {};\n extensions.push({\n id,\n name: id,\n source: 'config',\n path: id,\n enabled: false,\n config,\n });\n }\n }\n\n return extensions;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"loader.js","names":["apiImpl"],"sources":["../../../src/extensions/loader.ts"],"sourcesContent":["/**\n * Extension Loader and Registry\n * \n * Supports three-tier extension storage:\n * 1. Workspace level (workspace/.extensions/) - highest priority\n * 2. Global level (~/.xopc/extensions/) - shared across workspaces\n * 3. Bundled level (xopc/extensions/) - shipped with xopc\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join, dirname, isAbsolute } from 'path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { createJiti } from 'jiti';\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport { loadConfig } from '../config/loader.js';\nimport {\n resolveAgentWorkspaceDir,\n resolveExtensionsDir,\n resolveWorkspaceExtensionsDir,\n resolveBundledExtensionsDir,\n resolveExtensionSdkPath,\n} from '../config/paths.js';\nimport type { Config } from '../config/config-surface.js';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport type { SessionIndex } from '../session/manager.js';\nimport type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type {\n ExtensionApi,\n ExtensionModule,\n ExtensionManifest,\n ResolvedExtensionConfig,\n DiscoveredExtension,\n} from './types/index.js';\nimport type { ActivationContext } from './activation-planner.js';\nimport { ActivationPlanner, type ActivationLoadPhase } from './activation-planner.js';\nimport { mergeActivationContext } from './activation-context.js';\nimport {\n areExtensionsGloballyDisabled,\n discoverExtensionsFromDisk,\n type DiscoverConfig,\n type ExtensionLoaderOptions,\n} from './discover-extensions.js';\nimport type { ExtensionMetadataSnapshot } from './extension-metadata-snapshot.js';\nimport { ManifestRegistry } from './manifest-registry.js';\nimport { normalizeExtensionManifest } from './normalize-manifest.js';\nimport { checkEngineCompatibility } from './engine-check.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { ExtensionApiImpl, createExtensionLogger, createPathResolver } from './api.js';\nimport { validateSpeechProviderContracts } from './speech-provider-contracts.js';\nimport { validateMediaUnderstandingProviderContracts } from './media-provider-contracts.js';\nimport { createLogger, createServiceLogger } from '../utils/logger.js';\n\n// Security imports\nimport {\n checkExtensionPathSafety,\n isExtensionAllowed,\n provenanceTracker,\n logSecurityIssue,\n DEFAULT_SECURITY_CONFIG,\n type SecurityConfig,\n // Note: ExtensionSourceOrigin is defined locally in this file\n} from './security.js';\n\n// Provider imports\nimport { getProviderRegistry, type ProviderPluginRegistry } from '../providers/plugin-registry.js';\n\n// Slot imports\nimport {\n getSlotRegistry,\n type SlotRegistry,\n type SlotKey,\n} from './slots.js';\n\n// Diagnostics imports\nimport {\n getExtensionCache,\n getExtensionDiagnostics,\n type ExtensionLoaderCache,\n type ExtensionDiagnostics,\n} from './diagnostics.js';\n\nconst EXTENSION_MANIFEST_FILE = 'xopc.extension.json';\n\nconst log = createLogger('ExtensionLoader');\n\nconst DEFAULT_EXTENSION_LOAD_CONCURRENCY = 4;\n\nasync function mapWithConcurrency<T>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n if (items.length === 0) {\n return;\n }\n const queue = [...items];\n const workerCount = Math.min(Math.max(1, concurrency), queue.length);\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n while (queue.length > 0) {\n const item = queue.shift();\n if (item === undefined) {\n break;\n }\n await fn(item);\n }\n }),\n );\n}\n\nexport type { ExtensionLoaderOptions, ExtensionSourceOrigin } from './discover-extensions.js';\nexport { areExtensionsGloballyDisabled } from './discover-extensions.js';\n\n// ============================================================================\n// Extension Registry\n// ============================================================================\n\n// `ExtensionRegistryImpl` moved to `./extension-registry-impl.ts` so `api.ts`\n// can use the class without going through loader.ts (which imports back from\n// api.ts and formed a circular cycle). Re-exported here for backward-compat.\nexport { ExtensionRegistryImpl } from './extension-registry-impl.js';\nimport { ExtensionRegistryImpl } from './extension-registry-impl.js';\n\n// ============================================================================\n// Extension Loader\n// ============================================================================\n\nexport interface ActivationPlanLoadOptions extends Partial<ActivationContext> {\n phase?: ActivationLoadPhase | 'all';\n}\n\nexport class ExtensionLoader {\n private registry: ExtensionRegistryImpl;\n private options: ExtensionLoaderOptions;\n private extensionInstances: Map<string, ExtensionApi> = new Map();\n private jiti: ReturnType<typeof createJiti>;\n private _appConfig?: Config;\n private _runtimeContext?: {\n bus?: MessageBus;\n sessionManager?: SessionIndex;\n scheduleWebchatContinuation?: (sessionKey: string, message: string) => void;\n setLabel?: (entryId: string, label: string | undefined) => void;\n sendUserMessage?: import('./types/index.js').ExtensionRuntime['sendUserMessage'];\n appendEntry?: import('./types/index.js').ExtensionRuntime['appendEntry'];\n sendMessage?: import('./types/index.js').ExtensionRuntime['sendMessage'];\n };\n \n // Security\n private securityConfig: SecurityConfig;\n \n // Provider Registry\n private providerRegistry: ProviderPluginRegistry;\n \n // Slot Registry & Config\n private slotRegistry: SlotRegistry;\n private slotsConfig: Partial<Record<SlotKey, string>> = {};\n \n // Cache and Diagnostics\n private cache: ExtensionLoaderCache;\n private diagnostics: ExtensionDiagnostics;\n\n /** Manifest-only registry cache (no runtime module load). */\n private manifestRegistry: ManifestRegistry | null = null;\n private manifestSnapshot: ExtensionMetadataSnapshot | null = null;\n\n constructor(options?: ExtensionLoaderOptions) {\n this.registry = new ExtensionRegistryImpl();\n this.options = options || (() => {\n const c = loadConfig();\n const aid = resolveDefaultAgentId(c);\n return {\n workspaceDir: resolveAgentWorkspaceDir(c, aid),\n extensionsDir: resolveWorkspaceExtensionsDir(c, aid),\n };\n })();\n\n // Initialize security config\n this.securityConfig = DEFAULT_SECURITY_CONFIG;\n \n // Initialize provider registry\n this.providerRegistry = getProviderRegistry();\n \n // Initialize slot registry\n this.slotRegistry = getSlotRegistry();\n \n // Initialize cache and diagnostics \n this.cache = getExtensionCache();\n this.diagnostics = getExtensionDiagnostics();\n\n // Build jiti aliases for the public, host-provided extension SDK contract.\n // Extensions must import from @xopcai/xopc/extension-sdk; private aliases\n // are intentionally not registered.\n const alias: Record<string, string> = {};\n const sdkPath = resolveExtensionSdkPath();\n if (sdkPath) {\n const sdkDir = dirname(sdkPath);\n const sdkSubpaths = [\n 'core',\n 'lazy',\n 'provider',\n 'channel',\n 'hooks',\n 'tools',\n 'testing',\n 'speech',\n 'media',\n ];\n const prefix = '@xopcai/xopc/extension-sdk';\n alias[prefix] = sdkPath;\n for (const subpath of sdkSubpaths) {\n alias[`${prefix}/${subpath}`] = join(sdkDir, `${subpath}.ts`);\n }\n }\n\n // Initialize jiti with TypeScript support and SDK alias\n this.jiti = createJiti(fileURLToPath(import.meta.url), {\n interopDefault: true,\n extensions: ['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs', '.json'],\n alias,\n });\n }\n\n /**\n * Reuse a pre-built metadata snapshot (avoids duplicate filesystem discovery).\n */\n setManifestSnapshot(snapshot: ExtensionMetadataSnapshot | null): void {\n this.manifestSnapshot = snapshot;\n this.manifestRegistry = snapshot?.manifestRegistry ?? null;\n }\n\n getManifestSnapshot(): ExtensionMetadataSnapshot | null {\n return this.manifestSnapshot;\n }\n\n /**\n * Set security configuration \n */\n setSecurityConfig(config: Partial<SecurityConfig>): void {\n this.securityConfig = { ...this.securityConfig, ...config };\n }\n\n /**\n * Get security configuration \n */\n getSecurityConfig(): SecurityConfig {\n return this.securityConfig;\n }\n\n /**\n * Get provider registry \n */\n getProviderRegistry(): ProviderPluginRegistry {\n return this.providerRegistry;\n }\n\n /**\n * Get slot registry \n */\n getSlotRegistry(): SlotRegistry {\n return this.slotRegistry;\n }\n\n /**\n * Get diagnostics \n */\n getDiagnostics(): ExtensionDiagnostics {\n return this.diagnostics;\n }\n\n /**\n * Set configuration from main Config object \n */\n setConfig(config: Config): void {\n this._appConfig = config;\n // Wire slot config\n const slots = (config.extensions as any)?.slots || {};\n this.slotsConfig = {\n memory: slots.memory,\n tts: slots.tts,\n imageGeneration: slots.imageGeneration,\n webSearch: slots.webSearch,\n };\n\n // Wire security config\n const security = (config.extensions as any)?.security;\n if (security) {\n this.securityConfig = {\n checkPermissions: security.checkPermissions ?? true,\n allowUntrusted: security.allowUntrusted ?? false,\n allow: security.allow ?? [],\n trackProvenance: security.trackProvenance ?? true,\n allowPromptInjection: security.allowPromptInjection ?? false,\n };\n }\n }\n\n /**\n * Inject MessageBus and optional SessionManager for ExtensionApi.runtime (Gateway).\n */\n setRuntimeContext(ctx: {\n bus?: MessageBus;\n sessionManager?: SessionIndex;\n scheduleWebchatContinuation?: (sessionKey: string, message: string) => void;\n setLabel?: (entryId: string, label: string | undefined) => void;\n sendUserMessage?: import('./types/index.js').ExtensionRuntime['sendUserMessage'];\n appendEntry?: import('./types/index.js').ExtensionRuntime['appendEntry'];\n sendMessage?: import('./types/index.js').ExtensionRuntime['sendMessage'];\n }): void {\n this._runtimeContext = ctx;\n // ExtensionApi snapshots `runtime` at construction and is tied to this loader's registry.\n // CLI startup loads extensions with `{ bus }` first; a global cache hit would reuse that\n // api for the gateway loader (no sessionManager, wrong registry) — breaks extensions that need SessionManager.\n this.cache.invalidate();\n }\n\n getRegistry(): ExtensionRegistryImpl {\n return this.registry;\n }\n\n /**\n * Build the manifest registry from filesystem manifests without loading extension code.\n */\n buildManifestRegistry(): ManifestRegistry {\n if (this.manifestRegistry) {\n return this.manifestRegistry;\n }\n const discovered = this.discoverExtensions();\n this.manifestRegistry = ManifestRegistry.fromDiscovered(discovered);\n return this.manifestRegistry;\n }\n\n /**\n * Plan activation from manifest metadata with pure logic.\n */\n planActivation(_context?: Partial<ActivationContext>): ActivationPlanner {\n return new ActivationPlanner(this.buildManifestRegistry());\n }\n\n /**\n * Load only extensions selected by the activation plan.\n */\n async loadByActivationPlan(context?: ActivationPlanLoadOptions): Promise<void> {\n if (areExtensionsGloballyDisabled(this._appConfig as DiscoverConfig | undefined)) {\n log.debug('Extension loading skipped (extensions globally disabled)');\n return;\n }\n\n const registry = this.buildManifestRegistry();\n const planner = new ActivationPlanner(registry);\n const fullContext = mergeActivationContext(this._appConfig, context);\n let activatedIds = planner.getActivatedIds(fullContext).sort((a, b) => a.localeCompare(b));\n\n const phase = context?.phase ?? 'all';\n if (phase === 'startup' || phase === 'deferred') {\n activatedIds = planner.filterActivatedIdsByLoadPhase(activatedIds, phase);\n }\n\n const concurrency = Number.parseInt(\n process.env.XOPC_EXTENSION_LOAD_CONCURRENCY ?? '',\n 10,\n );\n const loadConcurrency = Number.isFinite(concurrency) && concurrency > 0\n ? concurrency\n : DEFAULT_EXTENSION_LOAD_CONCURRENCY;\n\n await mapWithConcurrency(activatedIds, loadConcurrency, async (extensionId) => {\n if (this.extensionInstances.has(extensionId)) {\n return;\n }\n const entry = registry.getEntry(extensionId);\n if (!entry) {\n log.debug(\n { extensionId },\n 'Activation plan references extension id that was not discovered on disk',\n );\n return;\n }\n\n const config: ResolvedExtensionConfig = {\n id: extensionId,\n name: entry.manifest.name || extensionId,\n source: entry.source,\n path: entry.path,\n enabled: true,\n config: this.resolveExtensionConfig(extensionId),\n };\n\n await this.loadExtension(config);\n });\n }\n\n getManifestRegistry(): ManifestRegistry {\n return this.buildManifestRegistry();\n }\n\n invalidateManifestCache(): void {\n this.manifestSnapshot = null;\n this.manifestRegistry = null;\n }\n\n private resolveExtensionConfig(extensionId: string): Record<string, unknown> {\n if (!this._appConfig) return {};\n const extensionsConfig = (this._appConfig as Record<string, unknown>).extensions as\n | Record<string, unknown>\n | undefined;\n if (!extensionsConfig || typeof extensionsConfig !== 'object') return {};\n const raw = extensionsConfig[extensionId];\n if (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as Record<string, unknown>;\n }\n return {};\n }\n\n /**\n * Discover extensions from all three tiers:\n * 1. Workspace (.extensions/) - highest priority\n * 2. Global (~/.xopc/extensions/) - shared\n * 3. Bundled (xopc/extensions/) - lowest priority\n */\n discoverExtensions(): DiscoveredExtension[] {\n if (this.manifestSnapshot) {\n return this.manifestSnapshot.discovered;\n }\n return discoverExtensionsFromDisk(this.options, this._appConfig as DiscoverConfig | undefined);\n }\n\n /**\n * Load all discovered extensions\n */\n async loadAllExtensions(enabledIds?: string[]): Promise<void> {\n const extensions = this.discoverExtensions();\n\n for (const extension of extensions) {\n // If enabledIds specified, only load those\n if (enabledIds && !enabledIds.includes(extension.id)) {\n continue;\n }\n\n const config: ResolvedExtensionConfig = {\n id: extension.id,\n name: extension.manifest.name || extension.id,\n source: extension.source,\n path: extension.path,\n enabled: true,\n config: {},\n };\n\n await this.loadExtension(config);\n }\n }\n\n async loadExtensions(configs: ResolvedExtensionConfig[]): Promise<void> {\n for (const extensionConfig of configs) {\n if (extensionConfig.enabled) {\n await this.loadExtension(extensionConfig);\n }\n }\n }\n\n async loadExtension(config: ResolvedExtensionConfig): Promise<ExtensionApi | null> {\n try {\n // Check cache first\n const cacheKey = this.cache.buildKey(this.options, [config.id]);\n const cached = this.cache.get<ExtensionApi>(cacheKey);\n if (cached) {\n log.debug({ extensionId: config.id }, 'Extension loaded from cache');\n return cached;\n }\n\n // Check if already loaded\n if (this.extensionInstances.has(config.id)) {\n return this.extensionInstances.get(config.id)!;\n }\n\n // Resolve extension path using resolveExtensionPath\n let extensionPath: string | null;\n if (isAbsolute(config.path)) {\n extensionPath = config.path;\n } else {\n // Try to resolve extension ID to actual path\n extensionPath = resolveExtensionPath(config.path, this.options) ||\n resolveExtensionPath(config.id, this.options);\n }\n \n if (!extensionPath) {\n log.error({ extensionId: config.id, path: config.path }, `Could not resolve extension path`);\n this.diagnostics.error(config.id, `Could not resolve extension path: ${config.path}`);\n return null;\n }\n\n log.debug({ extensionId: config.id, extensionPath }, 'Resolved extension path');\n\n // Security check\n const source = config.source || 'bundled';\n const safetyResult = checkExtensionPathSafety(extensionPath, extensionPath, source);\n \n if (!safetyResult.safe) {\n logSecurityIssue(config.id, safetyResult);\n \n if (this.securityConfig.checkPermissions && source !== 'bundled') {\n // Check allowlist\n if (!isExtensionAllowed(config.id, this.securityConfig)) {\n this.diagnostics.error(config.id, `Extension not allowed: ${safetyResult.detail}`);\n log.error({ extensionId: config.id, reason: safetyResult.reason }, 'Extension blocked by security policy');\n return null;\n }\n }\n }\n\n // Track provenance \n provenanceTracker.track(config.id, source);\n\n const manifest = this.loadManifest(extensionPath);\n if (!manifest) {\n log.error({ extensionId: config.id, extensionPath }, `Failed to load manifest for extension`);\n this.diagnostics.error(config.id, `Failed to load manifest`);\n return null;\n }\n\n if (!manifest.main || !/\\.(js|mjs|cjs)$/i.test(manifest.main)) {\n const reason = `Extension manifest main must point at built JavaScript (.js/.mjs/.cjs): ${manifest.main ?? '(missing)'}`;\n log.warn({ extensionId: config.id, main: manifest.main }, reason);\n this.diagnostics.error(config.id, reason);\n return null;\n }\n\n if (!manifest.engines?.xopc) {\n const reason = 'Extension manifest must declare engines.xopc';\n log.warn({ extensionId: config.id }, reason);\n this.diagnostics.error(config.id, reason);\n return null;\n }\n const range = manifest.engines.xopc;\n const engineResult = checkEngineCompatibility(PACKAGE_VERSION, range);\n if (engineResult.parseWarning) {\n const reason = engineResult.reason ?? `Could not parse engines.xopc: ${range}`;\n log.warn({ extensionId: config.id, range, reason }, 'Extension engine range parse failure — skipping load');\n this.diagnostics.error(config.id, reason);\n return null;\n }\n if (!engineResult.compatible) {\n log.warn(\n { extensionId: config.id, range, currentVersion: PACKAGE_VERSION },\n 'Extension engine requirement not met (engines.xopc) — skipping load',\n );\n this.diagnostics.error(\n config.id,\n engineResult.reason ?? 'Incompatible xopc version (engines.xopc)',\n );\n return null;\n }\n\n // Validate extension config against schema (basic validation)\n if (manifest.configSchema) {\n try {\n const schema = manifest.configSchema as Record<string, unknown>;\n const extensionConfig = config.config as Record<string, unknown>;\n \n // Basic validation: check required fields and types\n if (schema.type === 'object' && schema.properties) {\n const props = schema.properties as Record<string, Record<string, unknown>>;\n const required = (schema.required as string[]) || [];\n \n for (const field of required) {\n if (extensionConfig[field] === undefined) {\n log.error({ \n extensionId: config.id, \n field \n }, 'Extension config validation failed: missing required field');\n return null;\n }\n }\n \n for (const [key, value] of Object.entries(extensionConfig)) {\n const propSchema = props[key];\n if (propSchema) {\n if (propSchema.type && !this.validateType(value, propSchema.type as string)) {\n log.error({ \n extensionId: config.id, \n field: key,\n expected: propSchema.type,\n actual: typeof value\n }, 'Extension config validation failed: type mismatch');\n return null;\n }\n }\n }\n }\n \n log.debug({ extensionId: config.id }, 'Extension config validated');\n } catch (err) {\n log.warn({ err, extensionId: config.id }, 'Config schema validation skipped');\n }\n }\n\n // Create extension API\n const extensionDir = dirname(extensionPath);\n const api = this.createExtensionApi(manifest, config, extensionDir);\n\n // Load extension module\n const module = await this.loadModule(extensionPath, manifest);\n if (!module) {\n log.error({ extensionId: config.id }, `Failed to load module for extension`);\n this.diagnostics.error(config.id, `Failed to load module`);\n return null;\n }\n\n // Check and claim slots\n const slotClaimed = this.claimExtensionSlots(config.id, manifest);\n if (!slotClaimed) {\n log.warn({ extensionId: config.id }, 'Failed to claim required slots');\n }\n\n // Initialize extension\n await this.initializeExtension(module, api, manifest);\n\n validateSpeechProviderContracts({\n extensionId: config.id,\n manifest,\n registeredProviderIds: (api as ExtensionApiImpl).getRegisteredSpeechProviderIds(),\n logger: api.logger,\n });\n\n validateMediaUnderstandingProviderContracts({\n extensionId: config.id,\n manifest,\n registeredProviderIds: (api as ExtensionApiImpl).getRegisteredMediaUnderstandingProviderIds(),\n logger: api.logger,\n });\n\n // Register to registry\n this.registry.addExtension({\n id: config.id,\n name: manifest.name,\n version: manifest.version,\n path: extensionPath,\n module,\n config: config.config,\n enabled: true,\n source: config.source,\n });\n\n // Register extension tools to registry (so AgentManager can access them)\n // Note: api is actually ExtensionApiImpl at runtime\n const apiImpl = api as unknown as { _getTools: () => Map<string, AgentTool> };\n const extensionTools = apiImpl._getTools();\n for (const tool of extensionTools.values()) {\n this.registry.addTool(tool);\n }\n\n this.extensionInstances.set(config.id, api);\n \n // Cache the loaded extension\n this.cache.set(cacheKey, api);\n\n this.diagnostics.info(config.id, `Loaded extension: ${manifest.name}`);\n log.info({ name: manifest.name, id: manifest.id, source: config.source }, `Loaded extension`);\n\n return api;\n } catch (error) {\n log.error({ err: error, extensionId: config.id }, `Error loading extension`);\n this.diagnostics.error(config.id, `Error loading extension: ${error}`);\n return null;\n }\n }\n\n /**\n * Claim extension slots based on manifest kind\n * Respects configured preferred plugin from config.extensions.slots\n */\n private claimExtensionSlots(extensionId: string, manifest: ExtensionManifest): boolean {\n const kind = manifest.kind as string;\n \n // Map extension kind to slot\n const slotMap: Record<string, SlotKey> = {\n 'memory': 'memory',\n 'tts': 'tts',\n 'speech-provider': 'tts',\n 'web-search': 'webSearch',\n };\n \n const slotKey = slotMap[kind];\n if (!slotKey) {\n return true; // No slot required\n }\n \n // Check if slot is reserved for a different plugin in config\n const preferredPlugin = this.slotsConfig[slotKey];\n if (preferredPlugin && preferredPlugin !== extensionId) {\n log.info(\n { extensionId, slotKey, preferredPlugin },\n `Skipping slot claim: slot \"${slotKey}\" is reserved for \"${preferredPlugin}\"`\n );\n this.diagnostics.info(\n extensionId,\n `Slot \"${slotKey}\" is reserved for \"${preferredPlugin}\", skipping claim`\n );\n return false;\n }\n \n const claimed = this.slotRegistry.claim(slotKey, extensionId, null);\n if (!claimed) {\n this.diagnostics.warn(extensionId, `Slot \"${slotKey}\" already claimed by another extension`);\n }\n return claimed;\n }\n\n loadManifest(extensionPath: string): ExtensionManifest | null {\n const manifestPath = join(extensionPath, EXTENSION_MANIFEST_FILE);\n\n // First try to load xopc.extension.json\n if (existsSync(manifestPath)) {\n try {\n const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n log.error({ manifestPath }, 'Manifest root must be a JSON object');\n return null;\n }\n return normalizeExtensionManifest(raw as Record<string, unknown>);\n } catch (error) {\n log.error({ err: error, manifestPath }, `Failed to parse manifest`);\n return null;\n }\n }\n\n return null;\n }\n\n private moduleEntryCandidates(manifest: ExtensionManifest): string[] {\n if (!manifest.main) return [];\n return [manifest.main];\n }\n\n private async loadModule(\n extensionPath: string,\n manifest: ExtensionManifest,\n ): Promise<ExtensionModule | null> {\n const entryPoints = this.moduleEntryCandidates(manifest);\n\n for (const entry of entryPoints) {\n const fullPath = isAbsolute(entry) ? entry : join(extensionPath, entry);\n\n if (existsSync(fullPath)) {\n try {\n if (!/\\.(js|mjs|cjs)$/.test(fullPath)) {\n throw new Error(`Extension runtime entry must be built JavaScript: ${entry}`);\n }\n const mod = await import(pathToFileURL(fullPath).href);\n return mod.default || mod;\n } catch (error) {\n log.warn({ err: error, path: fullPath }, `Failed to load module`);\n }\n }\n }\n\n return null;\n }\n\n private createExtensionApi(\n manifest: ExtensionManifest,\n resolved: ResolvedExtensionConfig,\n extensionDir: string,\n ): ExtensionApi {\n const logger = createExtensionLogger(`[${manifest.id}]`);\n const resolvePath = createPathResolver(extensionDir, this.options.workspaceDir || '');\n\n const appConfig = this._appConfig ?? ({} as Config);\n const extensionOnly = (resolved.config ?? {}) as Record<string, unknown>;\n\n const runtime =\n this._appConfig && this._runtimeContext\n ? {\n config: this._appConfig,\n bus: this._runtimeContext.bus,\n log: logger,\n sessionManager: this._runtimeContext.sessionManager,\n scheduleWebchatContinuation: this._runtimeContext.scheduleWebchatContinuation,\n setLabel: this._runtimeContext.setLabel,\n sendUserMessage: this._runtimeContext.sendUserMessage,\n appendEntry: this._runtimeContext.appendEntry,\n sendMessage: this._runtimeContext.sendMessage,\n }\n : this._appConfig\n ? {\n config: this._appConfig,\n log: logger,\n }\n : undefined;\n\n const api = new ExtensionApiImpl(\n manifest.id,\n manifest.name,\n manifest.version,\n extensionDir,\n appConfig,\n extensionOnly,\n logger,\n resolvePath,\n this.registry,\n manifest.contracts ?? {},\n runtime,\n );\n if (manifest.reload?.configPrefixes?.length) {\n api._setReloadConfigPrefixes(manifest.reload.configPrefixes);\n }\n const manifestCommands = manifest.ui?.contributions?.commands;\n if (manifestCommands?.length) {\n (api as ExtensionApiImpl)._setManifestCommands(manifestCommands);\n }\n return api;\n }\n\n private async initializeExtension(\n module: ExtensionModule,\n api: ExtensionApi,\n _manifest: ExtensionManifest,\n ): Promise<void> {\n if (typeof module === 'function') {\n // Module is a function that receives the API\n await module(api);\n } else if (typeof module === 'object' && module.register) {\n // Module is a ExtensionDefinition with register method\n await module.register(api);\n }\n }\n\n async startServices(): Promise<void> {\n const services = Array.from(this.registry.services.values());\n\n for (const service of services) {\n const serviceLog = createServiceLogger(service.id);\n try {\n await service.start?.();\n serviceLog.info(`Started service`);\n } catch (error) {\n serviceLog.error({ err: error }, `Failed to start service`);\n }\n }\n }\n\n async stopServices(): Promise<void> {\n const services = Array.from(this.registry.services.values()).reverse();\n\n for (const service of services) {\n if (service.stop) {\n const serviceLog = createServiceLogger(service.id);\n try {\n await service.stop();\n serviceLog.info(`Stopped service`);\n } catch (error) {\n serviceLog.error({ err: error }, `Failed to stop service`);\n }\n }\n }\n }\n\n /**\n * Basic type validation for config values\n */\n private validateType(value: unknown, expectedType: string): boolean {\n if (expectedType === 'string') return typeof value === 'string';\n if (expectedType === 'number' || expectedType === 'integer') return typeof value === 'number';\n if (expectedType === 'boolean') return typeof value === 'boolean';\n if (expectedType === 'array') return Array.isArray(value);\n if (expectedType === 'object') return typeof value === 'object' && value !== null && !Array.isArray(value);\n return true;\n }\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\nexport function resolveExtensionPath(id: string, _options: ExtensionLoaderOptions): string | null {\n const c = loadConfig();\n const aid = resolveDefaultAgentId(c);\n // Priority 1: Workspace\n const workspacePath = join(resolveWorkspaceExtensionsDir(c, aid), id);\n if (existsSync(workspacePath)) return workspacePath;\n\n // Priority 2: Global\n const globalPath = join(resolveExtensionsDir(), id);\n if (existsSync(globalPath)) return globalPath;\n\n // Priority 3: Bundled\n const bundledDir = resolveBundledExtensionsDir();\n if (bundledDir) {\n const bundledPath = join(bundledDir, id);\n if (existsSync(bundledPath)) return bundledPath;\n }\n\n return null;\n}\n\nexport function normalizeExtensionConfig(\n rawConfig: Record<string, unknown>,\n): ResolvedExtensionConfig[] {\n const extensions: ResolvedExtensionConfig[] = [];\n\n const enabled = (rawConfig.enabled as string[]) || [];\n const disabled = (rawConfig.disabled as string[]) || [];\n\n // Parse enabled extensions\n for (const id of enabled) {\n const config = (rawConfig[id] as Record<string, unknown>) || {};\n extensions.push({\n id,\n name: id,\n source: 'config',\n path: id,\n enabled: true,\n config,\n });\n }\n\n // Parse disabled extensions\n for (const id of disabled) {\n if (!extensions.find((p) => p.id === id)) {\n const config = (rawConfig[id] as Record<string, unknown>) || {};\n extensions.push({\n id,\n name: id,\n source: 'config',\n path: id,\n enabled: false,\n config,\n });\n }\n }\n\n return extensions;\n}\n"],"mappings":"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"}
@@ -111,8 +111,6 @@ export type UpdateAgentBody = {
111
111
  skills?: string[] | null;
112
112
  /** Replace `agents.list[].tools`; `null` resets to no explicit policies. */
113
113
  tools?: NonNullable<Config['agents']['list']>[number]['tools'] | null;
114
- /** Replace `agents.list[].memory`; every agent manifest owns its memory policy. */
115
- memory?: NonNullable<Config['agents']['list']>[number]['memory'];
116
114
  };
117
115
  export declare function prepareUpdateAgent(cfg: Config, agentIdRaw: string, body: UpdateAgentBody): AgentAdminResult<{
118
116
  nextConfig: Config;
@@ -1,5 +1,5 @@
1
- import { resolveEffectiveAgentManifest } from "../agent-manifest/resolver.js";
2
1
  import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentDir, resolveAgentProfileDir, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveUserPath, validateAgentIdForNewAgent } from "../agent/agent-scope.js";
2
+ import { resolveEffectiveAgentManifest } from "../agent-manifest/resolver.js";
3
3
  import { resolveEffectiveAgentProfile } from "../config/agent-profile.js";
4
4
  import { WORKSPACE_FILES, init_paths, resolveUserProfilePath } from "../config/paths.js";
5
5
  import { AGENT_PROFILE_MARKDOWN_SYSTEM_FILES, REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET } from "../agent/context/workspace.js";
@@ -276,11 +276,6 @@ function prepareUpdateAgent(cfg, agentIdRaw, body) {
276
276
  workspace: { root: `~/.xopc/workspace/${agentId}` },
277
277
  tools: { builtin: {} },
278
278
  skills: { mode: "all" },
279
- memory: {
280
- mode: "confirmWrite",
281
- sources: ["session", "curated"],
282
- writePolicy: { curated: "confirm" }
283
- },
284
279
  workflows: {},
285
280
  boundaries: {
286
281
  requiresConfirmation: [],
@@ -369,7 +364,6 @@ function prepareUpdateAgent(cfg, agentIdRaw, body) {
369
364
  }
370
365
  if (body.tools !== void 0) if (body.tools === null) entry.tools = { builtin: {} };
371
366
  else entry.tools = body.tools;
372
- if (body.memory !== void 0) entry.memory = body.memory;
373
367
  list[idx] = entry;
374
368
  let next = {
375
369
  ...cfg,
@@ -1 +1 @@
1
- {"version":3,"file":"agents-admin.js","names":["pathResolve"],"sources":["../../../src/gateway/agents-admin.ts"],"sourcesContent":["/**\n * Gateway REST helpers for multi-agent management.\n */\n\nimport { cp, mkdir, readdir, readFile, realpath, stat, unlink, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve as pathResolve } from 'node:path';\n\nimport {\n listAgentEntries,\n normalizeAgentId,\n resolveAgentDir,\n resolveAgentProfileDir,\n resolveAgentWorkspaceDir,\n resolveDefaultAgentId,\n resolveUserPath,\n validateAgentIdForNewAgent,\n} from '../agent/agent-scope.js';\nimport {\n AGENT_PROFILE_MARKDOWN_SYSTEM_FILES,\n REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET,\n} from '../agent/context/workspace.js';\nimport { seedAgentProfileMarkdownFiles } from '../agent/context/workspace-seed.js';\nimport {\n resolveEffectiveAgentManifest,\n type ResolveManifestResult,\n} from '../agent-manifest/resolver.js';\nimport {\n applyAgentConfig,\n findAgentEntryIndex,\n getAgentDeleteBlocker,\n pruneAgentConfig,\n removeAgentDirsFromDisk,\n} from '../commands/agents.config.js';\nimport type { AgentModelsConfig, Config } from '../config/schema.js';\nimport { resolveUserProfilePath, WORKSPACE_FILES } from '../config/paths.js';\nimport { resolveEffectiveAgentProfile } from '../config/agent-profile.js';\nimport type { AgentTypedModel } from '../config/schema.js';\nimport { resolveEffectiveTypedModels } from '../config/agent-typed-models.js';\nimport { GATEWAY_BUILTIN_TOOL_IDS } from './agent-builtin-tools.js';\nimport { isPathUnderWorkspace, resolveWorkspaceSafePath } from './workspace-editor-path.js';\n\nconst EDITABLE_PROFILE_MARKDOWN_NAMES = new Set<string>([...AGENT_PROFILE_MARKDOWN_SYSTEM_FILES]);\n\nexport type GatewayAgentTypedModelsInfo = {\n defaultRole: string;\n preset: AgentTypedModel[];\n entry?: AgentTypedModel[];\n effective: AgentTypedModel[];\n};\n\nexport type GatewayAgentRow = {\n id: string;\n name?: string;\n description?: string;\n language?: string;\n role?: string;\n responsibilities?: {\n primary: string[];\n secondary?: string[];\n };\n /** Value from `IDENTITY.md` **Avatar:** line when present (may be URL, `xopc:…`, etc.). */\n avatar?: string;\n workspace: string;\n /** Absolute directory for profile Markdown (`SOUL.md`, …) and gateway avatars: `agents/<id>/profile/`. */\n profileDir: string;\n model?: { primary?: string; fallbacks?: string[] };\n typedModels: GatewayAgentTypedModelsInfo;\n extends: string[];\n isDefault: boolean;\n skills: {\n preset: string[];\n entry?: string[];\n effectiveAllowlist?: string[];\n };\n tools: {\n presetDenied: string[];\n entryDisable: string[];\n effectiveDisable: string[];\n };\n};\n\nexport type GatewayAgentsListResponse = {\n defaultId: string;\n agents: GatewayAgentRow[];\n builtinToolIds: string[];\n};\n\nexport type GatewayAgentEffectiveManifestResponse = ResolveManifestResult;\n\nfunction collectAgentIdsForList(cfg: Config): string[] {\n const entries = listAgentEntries(cfg).filter((e) => e.enabled !== false);\n const defaultId = resolveDefaultAgentId(cfg);\n if (entries.length === 0) {\n return [defaultId];\n }\n const ids = new Set<string>();\n for (const e of entries) {\n ids.add(normalizeAgentId(e.id));\n }\n ids.add(defaultId);\n return [...ids];\n}\n\nfunction rolesToTypedModels(\n roles: Record<string, { model: string; fallbacks?: string[]; description?: string }> | undefined,\n): AgentTypedModel[] {\n return Object.entries(roles ?? {})\n .map(([id, role]) => ({ id, ...role }))\n .sort((a, b) => a.id.localeCompare(b.id));\n}\n\n/** Extract `**Avatar:**` value from profile IDENTITY.md (same line shape as the gateway console parser). */\nexport function extractAvatarFromIdentityMarkdown(content: string): string | undefined {\n return parseIdentityMarkdown(content).avatar || undefined;\n}\n\nexport function parseIdentityMarkdown(content: string): {\n name?: string;\n description?: string;\n language?: string;\n avatar?: string;\n} {\n const out: { name?: string; description?: string; language?: string; avatar?: string } = {};\n for (const line of content.split('\\n')) {\n const match = line.match(/^[-*]\\s+\\*\\*(Name|Description|Language|Avatar):\\*\\*\\s*(.*)/i);\n if (match) {\n const key = match[1]?.toLowerCase();\n const v = match[2]?.trim() ?? '';\n if (!v || /^_\\(.*\\)_$/.test(v)) continue;\n if (key === 'name') out.name = v;\n if (key === 'description') out.description = v;\n if (key === 'language') out.language = v;\n if (key === 'avatar') out.avatar = v;\n }\n }\n return out;\n}\n\nexport async function listGatewayAgents(\n cfg: Config,\n _options: { locale?: string } = {},\n): Promise<GatewayAgentsListResponse> {\n const defaultId = resolveDefaultAgentId(cfg);\n const agents: GatewayAgentRow[] = [];\n const presetSkills: string[] | undefined = undefined;\n const presetDenied: string[] = [];\n const presetTypedModels: ReturnType<typeof rolesToTypedModels> = [];\n for (const id of collectAgentIdsForList(cfg)) {\n const profile = resolveEffectiveAgentProfile(cfg, id);\n const entry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id);\n const model =\n profile.primaryModelRef || profile.fallbacks.length > 0\n ? {\n ...(profile.primaryModelRef ? { primary: profile.primaryModelRef } : {}),\n ...(profile.fallbacks.length > 0 ? { fallbacks: profile.fallbacks } : {}),\n }\n : undefined;\n const entrySkills = entry?.skills.mode === 'allowlist' ? entry.skills.allow ?? [] : undefined;\n const entryDisable = Object.entries(entry?.tools.builtin ?? {})\n .filter(([, policy]) => policy.mode === 'deny')\n .map(([name]) => name);\n const entryTypedModels = rolesToTypedModels(entry?.models?.roles);\n const effectiveTypedModels = [...resolveEffectiveTypedModels(cfg, id).values()].sort((a, b) =>\n a.id.localeCompare(b.id),\n );\n let identity: ReturnType<typeof parseIdentityMarkdown> = {};\n try {\n const identityPath = join(resolveAgentProfileDir(cfg, id), WORKSPACE_FILES.IDENTITY);\n const content = await readFile(identityPath, 'utf-8');\n identity = parseIdentityMarkdown(content);\n } catch {\n /* missing IDENTITY.md or unreadable */\n }\n agents.push({\n id,\n ...(identity.name ? { name: identity.name } : {}),\n ...(identity.description ? { description: identity.description } : {}),\n ...(identity.language ? { language: identity.language } : {}),\n ...(profile.manifest.identity.role ? { role: profile.manifest.identity.role } : {}),\n responsibilities: {\n primary: [...profile.manifest.responsibilities.primary],\n ...(profile.manifest.responsibilities.secondary\n ? { secondary: [...profile.manifest.responsibilities.secondary] }\n : {}),\n },\n ...(identity.avatar ? { avatar: identity.avatar } : {}),\n workspace: profile.resolvedWorkspacePath,\n profileDir: resolveAgentProfileDir(cfg, id),\n ...(model ? { model } : {}),\n typedModels: {\n defaultRole: profile.manifest.models.defaultRole,\n preset: [...presetTypedModels],\n ...(entryTypedModels.length > 0 ? { entry: entryTypedModels } : {}),\n effective: effectiveTypedModels,\n },\n extends: [...(entry?.extends ?? [])],\n isDefault: id === defaultId,\n skills: {\n preset: presetSkills ? [...presetSkills] : [],\n ...(entrySkills !== undefined ? { entry: [...entrySkills] } : {}),\n ...(profile.skillsAllowlist !== undefined\n ? { effectiveAllowlist: [...profile.skillsAllowlist] }\n : {}),\n },\n tools: {\n presetDenied: [...presetDenied],\n entryDisable: [...entryDisable],\n effectiveDisable: [...profile.tools.denied].sort((a, b) => a.localeCompare(b)),\n },\n });\n }\n agents.sort((a, b) => a.id.localeCompare(b.id));\n return { defaultId, agents, builtinToolIds: [...GATEWAY_BUILTIN_TOOL_IDS] };\n}\n\nexport function getGatewayAgentEffectiveManifest(\n cfg: Config,\n agentIdRaw: string,\n): AgentAdminResult<GatewayAgentEffectiveManifestResponse> {\n const agentId = normalizeAgentId(agentIdRaw);\n const entry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === agentId);\n if (!entry) {\n return { ok: false, error: `agent \"${agentId}\" not found`, status: 404 };\n }\n try {\n return {\n ok: true,\n data: resolveEffectiveAgentManifest({\n agent: entry,\n presets: cfg.agents.capabilityPresets,\n defaultPresetId: cfg.agents.defaultPreset,\n }),\n };\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n status: 400,\n };\n }\n}\n\nexport type CreateAgentBody = {\n /** Optional id seed; normalized agent id defaults from `profileFiles[\"IDENTITY.md\"]` name when omitted. */\n id?: string;\n workspace: string;\n models?: AgentModelsConfig;\n /** Reusable capability plans applied after the global baseline. */\n extends?: string[];\n /** Initial `agents.list[].skills` for the new entry. */\n skills?: string[];\n /** Initial `agents.list[].tools` for the new entry. */\n tools?: NonNullable<Config['agents']['list']>[number]['tools'];\n /** Profile markdown files to write after seeding (e.g. `IDENTITY.md`, `SOUL.md`). */\n profileFiles?: Record<string, string>;\n /** Clone from an existing agent id — copies config entry fields and profile directory. */\n cloneFrom?: string;\n};\n\nexport type AgentAdminHttpStatus = 400 | 404 | 409;\n\nexport type AgentAdminResult<T> =\n | { ok: true; data: T }\n | { ok: false; error: string; status?: AgentAdminHttpStatus };\n\nexport function prepareCreateAgent(\n cfg: Config,\n body: CreateAgentBody,\n): AgentAdminResult<{ nextConfig: Config; agentId: string; workspace: string }> {\n const workspace = body.workspace?.trim() ?? '';\n if (!workspace) {\n return { ok: false, error: 'workspace is required', status: 400 };\n }\n if (body.profileFiles !== undefined) {\n if (typeof body.profileFiles !== 'object' || body.profileFiles === null || Array.isArray(body.profileFiles)) {\n return { ok: false, error: 'profileFiles must be an object', status: 400 };\n }\n for (const [name, content] of Object.entries(body.profileFiles)) {\n const bad = assertAllowedFile(name);\n if (bad) {\n return bad;\n }\n if (typeof content !== 'string') {\n return { ok: false, error: `profileFiles[\"${name}\"] must be a string`, status: 400 };\n }\n }\n }\n if (!body.cloneFrom && typeof body.profileFiles?.[WORKSPACE_FILES.IDENTITY] !== 'string') {\n return { ok: false, error: `profileFiles[\"${WORKSPACE_FILES.IDENTITY}\"] is required`, status: 400 };\n }\n\n const identity = body.profileFiles?.[WORKSPACE_FILES.IDENTITY]\n ? parseIdentityMarkdown(body.profileFiles[WORKSPACE_FILES.IDENTITY])\n : {};\n const nameSeed = identity.name ?? body.id ?? '';\n const idRes = validateAgentIdForNewAgent(body.id, nameSeed);\n if (idRes.ok === false) {\n return { ok: false, error: idRes.error, status: 400 };\n }\n const agentId = idRes.agentId;\n if (findAgentEntryIndex(listAgentEntries(cfg), agentId) >= 0) {\n return { ok: false, error: `agent \"${agentId}\" already exists`, status: 409 };\n }\n\n // Resolve fields from cloneFrom source when present\n let cloneSourceEntry: ReturnType<typeof listAgentEntries>[number] | undefined;\n if (body.cloneFrom) {\n const srcId = normalizeAgentId(body.cloneFrom);\n cloneSourceEntry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === srcId);\n if (!cloneSourceEntry && srcId !== resolveDefaultAgentId(cfg)) {\n return { ok: false, error: `source agent \"${srcId}\" not found`, status: 404 };\n }\n }\n\n const wsAbs = resolveUserPath(workspace);\n\n const rawModels = body.models ?? cloneSourceEntry?.models;\n const inheritedPresetIds = body.extends ?? cloneSourceEntry?.extends ?? [];\n for (const presetId of inheritedPresetIds) {\n if (!cfg.agents.capabilityPresets[presetId]) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 400 };\n }\n }\n const effectiveModels =\n rawModels?.roles\n ? {\n defaultRole: rawModels.defaultRole ?? Object.keys(rawModels.roles)[0] ?? 'deep',\n roles: rawModels.roles,\n }\n : undefined;\n if (effectiveModels && !effectiveModels.roles[effectiveModels.defaultRole]) {\n return { ok: false, error: 'models.defaultRole must reference models.roles', status: 400 };\n }\n\n let next = applyAgentConfig(cfg, {\n agentId,\n workspace: wsAbs,\n extends: [...inheritedPresetIds],\n ...(effectiveModels ? { models: effectiveModels } : {}),\n ...(body.skills !== undefined\n ? { skills: body.skills.map((s) => String(s).trim()).filter(Boolean) }\n : cloneSourceEntry?.skills.mode === 'allowlist' && body.cloneFrom\n ? { skills: [...(cloneSourceEntry.skills.allow ?? [])] }\n : {}),\n ...(body.tools !== undefined ? { tools: body.tools } : cloneSourceEntry?.tools && body.cloneFrom ? { tools: cloneSourceEntry.tools } : {}),\n });\n\n return { ok: true, data: { nextConfig: next, agentId, workspace: wsAbs } };\n}\n\nexport async function finalizeCreateAgentDirs(\n cfg: Config,\n agentId: string,\n opts?: { profileFiles?: Record<string, string>; cloneFrom?: string },\n): Promise<AgentAdminResult<void>> {\n const wsPath = resolveAgentWorkspaceDir(cfg, agentId);\n const profilePath = resolveAgentProfileDir(cfg, agentId);\n const adPath = resolveAgentDir(cfg, agentId);\n await mkdir(wsPath, { recursive: true });\n await mkdir(profilePath, { recursive: true });\n await mkdir(adPath, { recursive: true });\n const id = normalizeAgentId(agentId);\n const displayName = opts?.profileFiles?.[WORKSPACE_FILES.IDENTITY]\n ? (parseIdentityMarkdown(opts.profileFiles[WORKSPACE_FILES.IDENTITY]).name ?? id)\n : id;\n\n // When cloning, copy the entire source profile directory instead of seeding\n if (opts?.cloneFrom) {\n const srcProfilePath = resolveAgentProfileDir(cfg, normalizeAgentId(opts.cloneFrom));\n try {\n const srcStat = await stat(srcProfilePath);\n if (srcStat.isDirectory()) {\n const srcFiles = await readdir(srcProfilePath);\n for (const fileName of srcFiles) {\n const srcFile = join(srcProfilePath, fileName);\n const dstFile = join(profilePath, fileName);\n try {\n const fileStat = await stat(srcFile);\n if (fileStat.isFile()) {\n await cp(srcFile, dstFile);\n }\n } catch {\n /* skip unreadable files */\n }\n }\n }\n } catch {\n // Source profile dir doesn't exist — fall through to normal seed\n seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });\n }\n } else {\n seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });\n }\n\n const profileFiles = opts?.profileFiles;\n if (profileFiles && Object.keys(profileFiles).length > 0) {\n for (const [name, content] of Object.entries(profileFiles)) {\n const written = await writeAgentProfileFile(cfg, agentId, name, content);\n if (written.ok === false) {\n return written;\n }\n }\n }\n\n return { ok: true, data: undefined };\n}\n\nexport type UpdateAgentBody = {\n workspace?: string;\n extends?: string[];\n models?: {\n defaultRole?: string | null;\n roles?: Record<string, { model: string; fallbacks?: string[]; description?: string }>;\n } | null;\n setDefault?: boolean;\n /** Replace `agents.list[].skills`; `null` resets to all skills. */\n skills?: string[] | null;\n /** Replace `agents.list[].tools`; `null` resets to no explicit policies. */\n tools?: NonNullable<Config['agents']['list']>[number]['tools'] | null;\n /** Replace `agents.list[].memory`; every agent manifest owns its memory policy. */\n memory?: NonNullable<Config['agents']['list']>[number]['memory'];\n};\n\nexport function prepareUpdateAgent(\n cfg: Config,\n agentIdRaw: string,\n body: UpdateAgentBody,\n): AgentAdminResult<{ nextConfig: Config }> {\n const agentId = normalizeAgentId(agentIdRaw);\n let list = [...listAgentEntries(cfg)];\n let idx = findAgentEntryIndex(list, agentId);\n if (idx < 0 && agentId === resolveDefaultAgentId(cfg)) {\n list = [...list, {\n id: agentId,\n enabled: true as const,\n identity: { name: agentId, role: 'Agent', language: 'en', tone: 'direct' },\n responsibilities: { primary: ['Help the user complete tasks'] },\n workspace: { root: `~/.xopc/workspace/${agentId}` },\n tools: { builtin: {} },\n skills: { mode: 'all' },\n memory: { mode: 'confirmWrite', sources: ['session', 'curated'], writePolicy: { curated: 'confirm' } },\n workflows: {},\n boundaries: { requiresConfirmation: [], forbidden: [], escalation: [] },\n }];\n idx = list.length - 1;\n }\n if (idx < 0) {\n return { ok: false, error: `agent \"${agentId}\" not found`, status: 404 };\n }\n\n type Entry = (typeof list)[number];\n const entry: Entry = { ...list[idx] };\n\n if (body.workspace !== undefined) {\n const w = body.workspace.trim();\n if (w) {\n entry.workspace = { root: resolveUserPath(w) };\n }\n }\n if (body.extends !== undefined) {\n const nextExtends = Array.from(new Set(body.extends.map((id) => normalizeAgentId(id)).filter(Boolean)));\n for (const presetId of nextExtends) {\n if (!cfg.agents.capabilityPresets[presetId]) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 400 };\n }\n }\n if (nextExtends.length > 0) {\n entry.extends = nextExtends;\n } else {\n delete entry.extends;\n }\n }\n if (body.models !== undefined) {\n if (body.models === null) {\n delete entry.models;\n } else {\n if (body.models.defaultRole !== undefined) {\n if (body.models.defaultRole === null || !body.models.defaultRole.trim()) {\n return { ok: false, error: 'models.defaultRole must be a non-empty string', status: 400 };\n }\n entry.models = {\n defaultRole: body.models.defaultRole.trim(),\n roles: entry.models?.roles ?? {},\n ...(entry.models?.policy ? { policy: entry.models.policy } : {}),\n };\n }\n if (Object.hasOwn(body.models, 'roles')) {\n if (body.models.roles !== undefined) {\n const roles = Object.fromEntries(\n Object.entries(body.models.roles)\n .map(([id, row]) => ({\n id: id.trim(),\n model: row.model.trim(),\n fallbacks: (row.fallbacks ?? []).map((ref) => ref.trim()).filter(Boolean),\n description: row.description?.trim(),\n }))\n .filter((row) => row.id && row.model)\n .map((row) => [\n row.id,\n {\n model: row.model,\n ...(row.fallbacks.length > 0 ? { fallbacks: row.fallbacks } : {}),\n ...(row.description ? { description: row.description } : {}),\n },\n ]),\n );\n entry.models = {\n defaultRole: entry.models?.defaultRole ?? Object.keys(roles)[0] ?? 'deep',\n roles,\n ...(entry.models?.policy ? { policy: entry.models.policy } : {}),\n };\n }\n }\n const roles = entry.models?.roles ?? {};\n const defaultRole = entry.models?.defaultRole ?? Object.keys(roles)[0];\n if (!defaultRole || !roles[defaultRole]) {\n return { ok: false, error: 'models.defaultRole must reference models.roles', status: 400 };\n }\n entry.models = { ...entry.models, defaultRole };\n }\n }\n\n if (body.skills !== undefined) {\n if (body.skills === null) {\n entry.skills = { mode: 'all' };\n } else {\n const next = body.skills.map((s) => String(s).trim()).filter(Boolean);\n if (next.length === 0) {\n entry.skills = { mode: 'allowlist', allow: [] };\n } else {\n entry.skills = { mode: 'allowlist', allow: next };\n }\n }\n }\n\n if (body.tools !== undefined) {\n if (body.tools === null) {\n entry.tools = { builtin: {} };\n } else {\n entry.tools = body.tools;\n }\n }\n\n if (body.memory !== undefined) {\n entry.memory = body.memory;\n }\n\n list[idx] = entry;\n let next: Config = {\n ...cfg,\n agents: {\n ...cfg.agents,\n list,\n },\n };\n\n if (body.setDefault === true) {\n next = {\n ...next,\n agents: {\n ...next.agents,\n default: agentId,\n },\n };\n }\n return { ok: true, data: { nextConfig: next } };\n}\n\nexport function prepareDeleteAgent(\n cfg: Config,\n agentIdRaw: string,\n): AgentAdminResult<{ nextConfig: Config; agentId: string }> {\n const agentId = normalizeAgentId(agentIdRaw);\n const blocker = getAgentDeleteBlocker(cfg, agentId);\n if (blocker) {\n return { ok: false, error: blocker, status: 400 };\n }\n if (findAgentEntryIndex(listAgentEntries(cfg), agentId) < 0) {\n return { ok: false, error: `agent \"${agentId}\" not found`, status: 404 };\n }\n const { config: pruned } = pruneAgentConfig(cfg, agentId);\n return { ok: true, data: { nextConfig: pruned, agentId } };\n}\n\nexport async function runAfterDeletePurge(cfg: Config, agentId: string): Promise<void> {\n await removeAgentDirsFromDisk(cfg, agentId);\n}\n\nexport type AgentFileEntry = {\n name: string;\n missing: boolean;\n size?: number;\n updatedAtMs?: number;\n};\n\nasync function profileMarkdownRootReal(cfg: Config, agentId: string): Promise<string> {\n const dir = resolveAgentProfileDir(cfg, agentId);\n await mkdir(dir, { recursive: true });\n try {\n return await realpath(dir);\n } catch {\n return pathResolve(dir);\n }\n}\n\nfunction assertAllowedFile(name: string): AgentAdminResult<never> | null {\n if (!name || name.includes('/') || name.includes('\\\\') || !EDITABLE_PROFILE_MARKDOWN_NAMES.has(name)) {\n return { ok: false, error: `unsupported file \"${name}\"`, status: 400 };\n }\n return null;\n}\n\nexport async function listAgentProfileFiles(\n cfg: Config,\n agentId: string,\n): Promise<AgentAdminResult<{ agentId: string; profileDir: string; files: AgentFileEntry[] }>> {\n const id = normalizeAgentId(agentId);\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n const root = await profileMarkdownRootReal(cfg, id);\n const names = [...EDITABLE_PROFILE_MARKDOWN_NAMES];\n const files: AgentFileEntry[] = [];\n for (const name of names.sort((a, b) => a.localeCompare(b))) {\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n continue;\n }\n try {\n const st = await stat(abs);\n if (!st.isFile()) {\n continue;\n }\n files.push({\n name,\n missing: false,\n size: st.size,\n updatedAtMs: st.mtimeMs,\n });\n } catch {\n if (!REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET.has(name)) {\n continue;\n }\n files.push({ name, missing: true });\n }\n }\n files.sort((a, b) => a.name.localeCompare(b.name));\n return { ok: true, data: { agentId: id, profileDir: root, files } };\n}\n\nexport async function readAgentProfileFile(\n cfg: Config,\n agentId: string,\n name: string,\n): Promise<AgentAdminResult<{ agentId: string; content: string; path: string }>> {\n const bad = assertAllowedFile(name);\n if (bad) {\n return bad;\n }\n const id = normalizeAgentId(agentId);\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n const root = await profileMarkdownRootReal(cfg, id);\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n return { ok: false, error: 'invalid path', status: 400 };\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return { ok: true, data: { agentId: id, content, path: abs } };\n } catch {\n return { ok: false, error: 'file not found', status: 404 };\n }\n}\n\nexport async function writeAgentProfileFile(\n cfg: Config,\n agentId: string,\n name: string,\n content: string,\n): Promise<AgentAdminResult<{ agentId: string; path: string }>> {\n const bad = assertAllowedFile(name);\n if (bad) {\n return bad;\n }\n const id = normalizeAgentId(agentId);\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n const root = await profileMarkdownRootReal(cfg, id);\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n return { ok: false, error: 'invalid path', status: 400 };\n }\n const rootReal = await profileMarkdownRootReal(cfg, id);\n if (!isPathUnderWorkspace(rootReal, abs)) {\n return { ok: false, error: 'path escapes profile markdown root', status: 400 };\n }\n await writeFile(abs, content, 'utf-8');\n return { ok: true, data: { agentId: id, path: abs } };\n}\n\nexport async function readUserProfileFile(): Promise<AgentAdminResult<{ content: string; path: string }>> {\n const path = resolveUserProfilePath();\n try {\n const content = await readFile(path, 'utf-8');\n return { ok: true, data: { content, path } };\n } catch {\n return { ok: true, data: { content: '', path } };\n }\n}\n\nexport async function writeUserProfileFile(\n content: string,\n): Promise<AgentAdminResult<{ path: string }>> {\n const path = resolveUserProfilePath();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, 'utf-8');\n return { ok: true, data: { path } };\n}\n\n// ---------------------------------------------------------------------------\n// Binary agent avatar (profile markdown root dir, not a SOUL/IDENTITY markdown file)\n// ---------------------------------------------------------------------------\n\nconst AGENT_AVATAR_MAX_BYTES = 512 * 1024;\nconst AGENT_AVATAR_BASENAME = 'agent-avatar';\n\nconst AGENT_AVATAR_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp'] as const;\n\nfunction agentAvatarFilenames(): string[] {\n return AGENT_AVATAR_EXTENSIONS.map((ext) => `${AGENT_AVATAR_BASENAME}${ext}`);\n}\n\nfunction mimeToExt(mime: string): '.png' | '.jpg' | '.jpeg' | '.webp' | null {\n const m = mime.toLowerCase().trim();\n if (m === 'image/png') return '.png';\n if (m === 'image/jpeg' || m === 'image/jpg') return '.jpg';\n if (m === 'image/webp') return '.webp';\n return null;\n}\n\nfunction detectImageMimeFromBytes(buf: Uint8Array): 'image/png' | 'image/jpeg' | 'image/webp' | null {\n if (buf.length >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {\n return 'image/png';\n }\n if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {\n return 'image/jpeg';\n }\n if (\n buf.length >= 12 &&\n buf[0] === 0x52 &&\n buf[1] === 0x49 &&\n buf[2] === 0x46 &&\n buf[3] === 0x46 &&\n buf[8] === 0x57 &&\n buf[9] === 0x45 &&\n buf[10] === 0x42 &&\n buf[11] === 0x50\n ) {\n return 'image/webp';\n }\n return null;\n}\n\nfunction assertAgentExistsForAvatar(cfg: Config, id: string): AgentAdminResult<never> | null {\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n return null;\n}\n\nexport async function readAgentAvatarFile(\n cfg: Config,\n agentId: string,\n): Promise<AgentAdminResult<{ agentId: string; buffer: Buffer; contentType: string; path: string }>> {\n const missingAgent = assertAgentExistsForAvatar(cfg, agentId);\n if (missingAgent) {\n return missingAgent;\n }\n const id = normalizeAgentId(agentId);\n const root = await profileMarkdownRootReal(cfg, id);\n for (const name of agentAvatarFilenames()) {\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n continue;\n }\n try {\n const st = await stat(abs);\n if (!st.isFile() || st.size <= 0 || st.size > AGENT_AVATAR_MAX_BYTES) {\n continue;\n }\n const buffer = await readFile(abs);\n const detected = detectImageMimeFromBytes(buffer);\n if (!detected) {\n continue;\n }\n return { ok: true, data: { agentId: id, buffer, contentType: detected, path: abs } };\n } catch {\n /* try next */\n }\n }\n return { ok: false, error: 'avatar not found', status: 404 };\n}\n\nexport async function writeAgentAvatarFromBase64(\n cfg: Config,\n agentId: string,\n base64: string,\n mimeType: string,\n): Promise<AgentAdminResult<{ agentId: string; path: string }>> {\n const missingAgent = assertAgentExistsForAvatar(cfg, agentId);\n if (missingAgent) {\n return missingAgent;\n }\n const id = normalizeAgentId(agentId);\n const ext = mimeToExt(mimeType);\n if (!ext) {\n return { ok: false, error: 'unsupported mimeType (use image/png, image/jpeg, or image/webp)', status: 400 };\n }\n let raw: Buffer;\n try {\n raw = Buffer.from(base64, 'base64');\n } catch {\n return { ok: false, error: 'invalid base64', status: 400 };\n }\n if (raw.length === 0 || raw.length > AGENT_AVATAR_MAX_BYTES) {\n return { ok: false, error: `avatar must be non-empty and at most ${AGENT_AVATAR_MAX_BYTES} bytes`, status: 400 };\n }\n const detected = detectImageMimeFromBytes(raw);\n if (!detected || !extMatchesDetectedMime(ext, detected)) {\n return { ok: false, error: 'file content does not match declared image type', status: 400 };\n }\n\n const root = await profileMarkdownRootReal(cfg, id);\n const rootReal = await profileMarkdownRootReal(cfg, id);\n const targetName = `${AGENT_AVATAR_BASENAME}${ext}`;\n const abs = resolveWorkspaceSafePath(root, targetName);\n if (!abs || !isPathUnderWorkspace(rootReal, abs)) {\n return { ok: false, error: 'invalid path', status: 400 };\n }\n for (const name of agentAvatarFilenames()) {\n if (name === targetName) {\n continue;\n }\n const other = resolveWorkspaceSafePath(root, name);\n if (other && isPathUnderWorkspace(rootReal, other)) {\n try {\n await unlink(other);\n } catch {\n /* absent */\n }\n }\n }\n await writeFile(abs, raw);\n return { ok: true, data: { agentId: id, path: abs } };\n}\n\nfunction mimeToExtToMime(ext: '.png' | '.jpg' | '.jpeg' | '.webp'): 'image/png' | 'image/jpeg' | 'image/webp' {\n if (ext === '.png') return 'image/png';\n if (ext === '.webp') return 'image/webp';\n return 'image/jpeg';\n}\n\nfunction extMatchesDetectedMime(\n ext: '.png' | '.jpg' | '.jpeg' | '.webp',\n detected: 'image/png' | 'image/jpeg' | 'image/webp',\n): boolean {\n return detected === mimeToExtToMime(ext);\n}\n\n/** Remove any `agent-avatar.*` in the agent profile markdown root. Idempotent: ok even when no file existed. */\nexport async function deleteAgentAvatarFile(cfg: Config, agentId: string): Promise<AgentAdminResult<{ agentId: string }>> {\n const missingAgent = assertAgentExistsForAvatar(cfg, agentId);\n if (missingAgent) {\n return missingAgent;\n }\n const id = normalizeAgentId(agentId);\n const root = await profileMarkdownRootReal(cfg, id);\n const rootReal = await profileMarkdownRootReal(cfg, id);\n for (const name of agentAvatarFilenames()) {\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs || !isPathUnderWorkspace(rootReal, abs)) {\n continue;\n }\n try {\n await unlink(abs);\n } catch {\n /* absent */\n }\n }\n return { ok: true, data: { agentId: id } };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agents-admin.js","names":["pathResolve"],"sources":["../../../src/gateway/agents-admin.ts"],"sourcesContent":["/**\n * Gateway REST helpers for multi-agent management.\n */\n\nimport { cp, mkdir, readdir, readFile, realpath, stat, unlink, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve as pathResolve } from 'node:path';\n\nimport {\n listAgentEntries,\n normalizeAgentId,\n resolveAgentDir,\n resolveAgentProfileDir,\n resolveAgentWorkspaceDir,\n resolveDefaultAgentId,\n resolveUserPath,\n validateAgentIdForNewAgent,\n} from '../agent/agent-scope.js';\nimport {\n AGENT_PROFILE_MARKDOWN_SYSTEM_FILES,\n REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET,\n} from '../agent/context/workspace.js';\nimport { seedAgentProfileMarkdownFiles } from '../agent/context/workspace-seed.js';\nimport {\n resolveEffectiveAgentManifest,\n type ResolveManifestResult,\n} from '../agent-manifest/resolver.js';\nimport {\n applyAgentConfig,\n findAgentEntryIndex,\n getAgentDeleteBlocker,\n pruneAgentConfig,\n removeAgentDirsFromDisk,\n} from '../commands/agents.config.js';\nimport type { AgentModelsConfig, Config } from '../config/schema.js';\nimport { resolveUserProfilePath, WORKSPACE_FILES } from '../config/paths.js';\nimport { resolveEffectiveAgentProfile } from '../config/agent-profile.js';\nimport type { AgentTypedModel } from '../config/schema.js';\nimport { resolveEffectiveTypedModels } from '../config/agent-typed-models.js';\nimport { GATEWAY_BUILTIN_TOOL_IDS } from './agent-builtin-tools.js';\nimport { isPathUnderWorkspace, resolveWorkspaceSafePath } from './workspace-editor-path.js';\n\nconst EDITABLE_PROFILE_MARKDOWN_NAMES = new Set<string>([...AGENT_PROFILE_MARKDOWN_SYSTEM_FILES]);\n\nexport type GatewayAgentTypedModelsInfo = {\n defaultRole: string;\n preset: AgentTypedModel[];\n entry?: AgentTypedModel[];\n effective: AgentTypedModel[];\n};\n\nexport type GatewayAgentRow = {\n id: string;\n name?: string;\n description?: string;\n language?: string;\n role?: string;\n responsibilities?: {\n primary: string[];\n secondary?: string[];\n };\n /** Value from `IDENTITY.md` **Avatar:** line when present (may be URL, `xopc:…`, etc.). */\n avatar?: string;\n workspace: string;\n /** Absolute directory for profile Markdown (`SOUL.md`, …) and gateway avatars: `agents/<id>/profile/`. */\n profileDir: string;\n model?: { primary?: string; fallbacks?: string[] };\n typedModels: GatewayAgentTypedModelsInfo;\n extends: string[];\n isDefault: boolean;\n skills: {\n preset: string[];\n entry?: string[];\n effectiveAllowlist?: string[];\n };\n tools: {\n presetDenied: string[];\n entryDisable: string[];\n effectiveDisable: string[];\n };\n};\n\nexport type GatewayAgentsListResponse = {\n defaultId: string;\n agents: GatewayAgentRow[];\n builtinToolIds: string[];\n};\n\nexport type GatewayAgentEffectiveManifestResponse = ResolveManifestResult;\n\nfunction collectAgentIdsForList(cfg: Config): string[] {\n const entries = listAgentEntries(cfg).filter((e) => e.enabled !== false);\n const defaultId = resolveDefaultAgentId(cfg);\n if (entries.length === 0) {\n return [defaultId];\n }\n const ids = new Set<string>();\n for (const e of entries) {\n ids.add(normalizeAgentId(e.id));\n }\n ids.add(defaultId);\n return [...ids];\n}\n\nfunction rolesToTypedModels(\n roles: Record<string, { model: string; fallbacks?: string[]; description?: string }> | undefined,\n): AgentTypedModel[] {\n return Object.entries(roles ?? {})\n .map(([id, role]) => ({ id, ...role }))\n .sort((a, b) => a.id.localeCompare(b.id));\n}\n\n/** Extract `**Avatar:**` value from profile IDENTITY.md (same line shape as the gateway console parser). */\nexport function extractAvatarFromIdentityMarkdown(content: string): string | undefined {\n return parseIdentityMarkdown(content).avatar || undefined;\n}\n\nexport function parseIdentityMarkdown(content: string): {\n name?: string;\n description?: string;\n language?: string;\n avatar?: string;\n} {\n const out: { name?: string; description?: string; language?: string; avatar?: string } = {};\n for (const line of content.split('\\n')) {\n const match = line.match(/^[-*]\\s+\\*\\*(Name|Description|Language|Avatar):\\*\\*\\s*(.*)/i);\n if (match) {\n const key = match[1]?.toLowerCase();\n const v = match[2]?.trim() ?? '';\n if (!v || /^_\\(.*\\)_$/.test(v)) continue;\n if (key === 'name') out.name = v;\n if (key === 'description') out.description = v;\n if (key === 'language') out.language = v;\n if (key === 'avatar') out.avatar = v;\n }\n }\n return out;\n}\n\nexport async function listGatewayAgents(\n cfg: Config,\n _options: { locale?: string } = {},\n): Promise<GatewayAgentsListResponse> {\n const defaultId = resolveDefaultAgentId(cfg);\n const agents: GatewayAgentRow[] = [];\n const presetSkills: string[] | undefined = undefined;\n const presetDenied: string[] = [];\n const presetTypedModels: ReturnType<typeof rolesToTypedModels> = [];\n for (const id of collectAgentIdsForList(cfg)) {\n const profile = resolveEffectiveAgentProfile(cfg, id);\n const entry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id);\n const model =\n profile.primaryModelRef || profile.fallbacks.length > 0\n ? {\n ...(profile.primaryModelRef ? { primary: profile.primaryModelRef } : {}),\n ...(profile.fallbacks.length > 0 ? { fallbacks: profile.fallbacks } : {}),\n }\n : undefined;\n const entrySkills = entry?.skills.mode === 'allowlist' ? entry.skills.allow ?? [] : undefined;\n const entryDisable = Object.entries(entry?.tools.builtin ?? {})\n .filter(([, policy]) => policy.mode === 'deny')\n .map(([name]) => name);\n const entryTypedModels = rolesToTypedModels(entry?.models?.roles);\n const effectiveTypedModels = [...resolveEffectiveTypedModels(cfg, id).values()].sort((a, b) =>\n a.id.localeCompare(b.id),\n );\n let identity: ReturnType<typeof parseIdentityMarkdown> = {};\n try {\n const identityPath = join(resolveAgentProfileDir(cfg, id), WORKSPACE_FILES.IDENTITY);\n const content = await readFile(identityPath, 'utf-8');\n identity = parseIdentityMarkdown(content);\n } catch {\n /* missing IDENTITY.md or unreadable */\n }\n agents.push({\n id,\n ...(identity.name ? { name: identity.name } : {}),\n ...(identity.description ? { description: identity.description } : {}),\n ...(identity.language ? { language: identity.language } : {}),\n ...(profile.manifest.identity.role ? { role: profile.manifest.identity.role } : {}),\n responsibilities: {\n primary: [...profile.manifest.responsibilities.primary],\n ...(profile.manifest.responsibilities.secondary\n ? { secondary: [...profile.manifest.responsibilities.secondary] }\n : {}),\n },\n ...(identity.avatar ? { avatar: identity.avatar } : {}),\n workspace: profile.resolvedWorkspacePath,\n profileDir: resolveAgentProfileDir(cfg, id),\n ...(model ? { model } : {}),\n typedModels: {\n defaultRole: profile.manifest.models.defaultRole,\n preset: [...presetTypedModels],\n ...(entryTypedModels.length > 0 ? { entry: entryTypedModels } : {}),\n effective: effectiveTypedModels,\n },\n extends: [...(entry?.extends ?? [])],\n isDefault: id === defaultId,\n skills: {\n preset: presetSkills ? [...presetSkills] : [],\n ...(entrySkills !== undefined ? { entry: [...entrySkills] } : {}),\n ...(profile.skillsAllowlist !== undefined\n ? { effectiveAllowlist: [...profile.skillsAllowlist] }\n : {}),\n },\n tools: {\n presetDenied: [...presetDenied],\n entryDisable: [...entryDisable],\n effectiveDisable: [...profile.tools.denied].sort((a, b) => a.localeCompare(b)),\n },\n });\n }\n agents.sort((a, b) => a.id.localeCompare(b.id));\n return { defaultId, agents, builtinToolIds: [...GATEWAY_BUILTIN_TOOL_IDS] };\n}\n\nexport function getGatewayAgentEffectiveManifest(\n cfg: Config,\n agentIdRaw: string,\n): AgentAdminResult<GatewayAgentEffectiveManifestResponse> {\n const agentId = normalizeAgentId(agentIdRaw);\n const entry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === agentId);\n if (!entry) {\n return { ok: false, error: `agent \"${agentId}\" not found`, status: 404 };\n }\n try {\n return {\n ok: true,\n data: resolveEffectiveAgentManifest({\n agent: entry,\n presets: cfg.agents.capabilityPresets,\n defaultPresetId: cfg.agents.defaultPreset,\n }),\n };\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n status: 400,\n };\n }\n}\n\nexport type CreateAgentBody = {\n /** Optional id seed; normalized agent id defaults from `profileFiles[\"IDENTITY.md\"]` name when omitted. */\n id?: string;\n workspace: string;\n models?: AgentModelsConfig;\n /** Reusable capability plans applied after the global baseline. */\n extends?: string[];\n /** Initial `agents.list[].skills` for the new entry. */\n skills?: string[];\n /** Initial `agents.list[].tools` for the new entry. */\n tools?: NonNullable<Config['agents']['list']>[number]['tools'];\n /** Profile markdown files to write after seeding (e.g. `IDENTITY.md`, `SOUL.md`). */\n profileFiles?: Record<string, string>;\n /** Clone from an existing agent id — copies config entry fields and profile directory. */\n cloneFrom?: string;\n};\n\nexport type AgentAdminHttpStatus = 400 | 404 | 409;\n\nexport type AgentAdminResult<T> =\n | { ok: true; data: T }\n | { ok: false; error: string; status?: AgentAdminHttpStatus };\n\nexport function prepareCreateAgent(\n cfg: Config,\n body: CreateAgentBody,\n): AgentAdminResult<{ nextConfig: Config; agentId: string; workspace: string }> {\n const workspace = body.workspace?.trim() ?? '';\n if (!workspace) {\n return { ok: false, error: 'workspace is required', status: 400 };\n }\n if (body.profileFiles !== undefined) {\n if (typeof body.profileFiles !== 'object' || body.profileFiles === null || Array.isArray(body.profileFiles)) {\n return { ok: false, error: 'profileFiles must be an object', status: 400 };\n }\n for (const [name, content] of Object.entries(body.profileFiles)) {\n const bad = assertAllowedFile(name);\n if (bad) {\n return bad;\n }\n if (typeof content !== 'string') {\n return { ok: false, error: `profileFiles[\"${name}\"] must be a string`, status: 400 };\n }\n }\n }\n if (!body.cloneFrom && typeof body.profileFiles?.[WORKSPACE_FILES.IDENTITY] !== 'string') {\n return { ok: false, error: `profileFiles[\"${WORKSPACE_FILES.IDENTITY}\"] is required`, status: 400 };\n }\n\n const identity = body.profileFiles?.[WORKSPACE_FILES.IDENTITY]\n ? parseIdentityMarkdown(body.profileFiles[WORKSPACE_FILES.IDENTITY])\n : {};\n const nameSeed = identity.name ?? body.id ?? '';\n const idRes = validateAgentIdForNewAgent(body.id, nameSeed);\n if (idRes.ok === false) {\n return { ok: false, error: idRes.error, status: 400 };\n }\n const agentId = idRes.agentId;\n if (findAgentEntryIndex(listAgentEntries(cfg), agentId) >= 0) {\n return { ok: false, error: `agent \"${agentId}\" already exists`, status: 409 };\n }\n\n // Resolve fields from cloneFrom source when present\n let cloneSourceEntry: ReturnType<typeof listAgentEntries>[number] | undefined;\n if (body.cloneFrom) {\n const srcId = normalizeAgentId(body.cloneFrom);\n cloneSourceEntry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === srcId);\n if (!cloneSourceEntry && srcId !== resolveDefaultAgentId(cfg)) {\n return { ok: false, error: `source agent \"${srcId}\" not found`, status: 404 };\n }\n }\n\n const wsAbs = resolveUserPath(workspace);\n\n const rawModels = body.models ?? cloneSourceEntry?.models;\n const inheritedPresetIds = body.extends ?? cloneSourceEntry?.extends ?? [];\n for (const presetId of inheritedPresetIds) {\n if (!cfg.agents.capabilityPresets[presetId]) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 400 };\n }\n }\n const effectiveModels =\n rawModels?.roles\n ? {\n defaultRole: rawModels.defaultRole ?? Object.keys(rawModels.roles)[0] ?? 'deep',\n roles: rawModels.roles,\n }\n : undefined;\n if (effectiveModels && !effectiveModels.roles[effectiveModels.defaultRole]) {\n return { ok: false, error: 'models.defaultRole must reference models.roles', status: 400 };\n }\n\n let next = applyAgentConfig(cfg, {\n agentId,\n workspace: wsAbs,\n extends: [...inheritedPresetIds],\n ...(effectiveModels ? { models: effectiveModels } : {}),\n ...(body.skills !== undefined\n ? { skills: body.skills.map((s) => String(s).trim()).filter(Boolean) }\n : cloneSourceEntry?.skills.mode === 'allowlist' && body.cloneFrom\n ? { skills: [...(cloneSourceEntry.skills.allow ?? [])] }\n : {}),\n ...(body.tools !== undefined ? { tools: body.tools } : cloneSourceEntry?.tools && body.cloneFrom ? { tools: cloneSourceEntry.tools } : {}),\n });\n\n return { ok: true, data: { nextConfig: next, agentId, workspace: wsAbs } };\n}\n\nexport async function finalizeCreateAgentDirs(\n cfg: Config,\n agentId: string,\n opts?: { profileFiles?: Record<string, string>; cloneFrom?: string },\n): Promise<AgentAdminResult<void>> {\n const wsPath = resolveAgentWorkspaceDir(cfg, agentId);\n const profilePath = resolveAgentProfileDir(cfg, agentId);\n const adPath = resolveAgentDir(cfg, agentId);\n await mkdir(wsPath, { recursive: true });\n await mkdir(profilePath, { recursive: true });\n await mkdir(adPath, { recursive: true });\n const id = normalizeAgentId(agentId);\n const displayName = opts?.profileFiles?.[WORKSPACE_FILES.IDENTITY]\n ? (parseIdentityMarkdown(opts.profileFiles[WORKSPACE_FILES.IDENTITY]).name ?? id)\n : id;\n\n // When cloning, copy the entire source profile directory instead of seeding\n if (opts?.cloneFrom) {\n const srcProfilePath = resolveAgentProfileDir(cfg, normalizeAgentId(opts.cloneFrom));\n try {\n const srcStat = await stat(srcProfilePath);\n if (srcStat.isDirectory()) {\n const srcFiles = await readdir(srcProfilePath);\n for (const fileName of srcFiles) {\n const srcFile = join(srcProfilePath, fileName);\n const dstFile = join(profilePath, fileName);\n try {\n const fileStat = await stat(srcFile);\n if (fileStat.isFile()) {\n await cp(srcFile, dstFile);\n }\n } catch {\n /* skip unreadable files */\n }\n }\n }\n } catch {\n // Source profile dir doesn't exist — fall through to normal seed\n seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });\n }\n } else {\n seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });\n }\n\n const profileFiles = opts?.profileFiles;\n if (profileFiles && Object.keys(profileFiles).length > 0) {\n for (const [name, content] of Object.entries(profileFiles)) {\n const written = await writeAgentProfileFile(cfg, agentId, name, content);\n if (written.ok === false) {\n return written;\n }\n }\n }\n\n return { ok: true, data: undefined };\n}\n\nexport type UpdateAgentBody = {\n workspace?: string;\n extends?: string[];\n models?: {\n defaultRole?: string | null;\n roles?: Record<string, { model: string; fallbacks?: string[]; description?: string }>;\n } | null;\n setDefault?: boolean;\n /** Replace `agents.list[].skills`; `null` resets to all skills. */\n skills?: string[] | null;\n /** Replace `agents.list[].tools`; `null` resets to no explicit policies. */\n tools?: NonNullable<Config['agents']['list']>[number]['tools'] | null;\n};\n\nexport function prepareUpdateAgent(\n cfg: Config,\n agentIdRaw: string,\n body: UpdateAgentBody,\n): AgentAdminResult<{ nextConfig: Config }> {\n const agentId = normalizeAgentId(agentIdRaw);\n let list = [...listAgentEntries(cfg)];\n let idx = findAgentEntryIndex(list, agentId);\n if (idx < 0 && agentId === resolveDefaultAgentId(cfg)) {\n list = [...list, {\n id: agentId,\n enabled: true as const,\n identity: { name: agentId, role: 'Agent', language: 'en', tone: 'direct' },\n responsibilities: { primary: ['Help the user complete tasks'] },\n workspace: { root: `~/.xopc/workspace/${agentId}` },\n tools: { builtin: {} },\n skills: { mode: 'all' },\n workflows: {},\n boundaries: { requiresConfirmation: [], forbidden: [], escalation: [] },\n }];\n idx = list.length - 1;\n }\n if (idx < 0) {\n return { ok: false, error: `agent \"${agentId}\" not found`, status: 404 };\n }\n\n type Entry = (typeof list)[number];\n const entry: Entry = { ...list[idx] };\n\n if (body.workspace !== undefined) {\n const w = body.workspace.trim();\n if (w) {\n entry.workspace = { root: resolveUserPath(w) };\n }\n }\n if (body.extends !== undefined) {\n const nextExtends = Array.from(new Set(body.extends.map((id) => normalizeAgentId(id)).filter(Boolean)));\n for (const presetId of nextExtends) {\n if (!cfg.agents.capabilityPresets[presetId]) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 400 };\n }\n }\n if (nextExtends.length > 0) {\n entry.extends = nextExtends;\n } else {\n delete entry.extends;\n }\n }\n if (body.models !== undefined) {\n if (body.models === null) {\n delete entry.models;\n } else {\n if (body.models.defaultRole !== undefined) {\n if (body.models.defaultRole === null || !body.models.defaultRole.trim()) {\n return { ok: false, error: 'models.defaultRole must be a non-empty string', status: 400 };\n }\n entry.models = {\n defaultRole: body.models.defaultRole.trim(),\n roles: entry.models?.roles ?? {},\n ...(entry.models?.policy ? { policy: entry.models.policy } : {}),\n };\n }\n if (Object.hasOwn(body.models, 'roles')) {\n if (body.models.roles !== undefined) {\n const roles = Object.fromEntries(\n Object.entries(body.models.roles)\n .map(([id, row]) => ({\n id: id.trim(),\n model: row.model.trim(),\n fallbacks: (row.fallbacks ?? []).map((ref) => ref.trim()).filter(Boolean),\n description: row.description?.trim(),\n }))\n .filter((row) => row.id && row.model)\n .map((row) => [\n row.id,\n {\n model: row.model,\n ...(row.fallbacks.length > 0 ? { fallbacks: row.fallbacks } : {}),\n ...(row.description ? { description: row.description } : {}),\n },\n ]),\n );\n entry.models = {\n defaultRole: entry.models?.defaultRole ?? Object.keys(roles)[0] ?? 'deep',\n roles,\n ...(entry.models?.policy ? { policy: entry.models.policy } : {}),\n };\n }\n }\n const roles = entry.models?.roles ?? {};\n const defaultRole = entry.models?.defaultRole ?? Object.keys(roles)[0];\n if (!defaultRole || !roles[defaultRole]) {\n return { ok: false, error: 'models.defaultRole must reference models.roles', status: 400 };\n }\n entry.models = { ...entry.models, defaultRole };\n }\n }\n\n if (body.skills !== undefined) {\n if (body.skills === null) {\n entry.skills = { mode: 'all' };\n } else {\n const next = body.skills.map((s) => String(s).trim()).filter(Boolean);\n if (next.length === 0) {\n entry.skills = { mode: 'allowlist', allow: [] };\n } else {\n entry.skills = { mode: 'allowlist', allow: next };\n }\n }\n }\n\n if (body.tools !== undefined) {\n if (body.tools === null) {\n entry.tools = { builtin: {} };\n } else {\n entry.tools = body.tools;\n }\n }\n\n list[idx] = entry;\n let next: Config = {\n ...cfg,\n agents: {\n ...cfg.agents,\n list,\n },\n };\n\n if (body.setDefault === true) {\n next = {\n ...next,\n agents: {\n ...next.agents,\n default: agentId,\n },\n };\n }\n return { ok: true, data: { nextConfig: next } };\n}\n\nexport function prepareDeleteAgent(\n cfg: Config,\n agentIdRaw: string,\n): AgentAdminResult<{ nextConfig: Config; agentId: string }> {\n const agentId = normalizeAgentId(agentIdRaw);\n const blocker = getAgentDeleteBlocker(cfg, agentId);\n if (blocker) {\n return { ok: false, error: blocker, status: 400 };\n }\n if (findAgentEntryIndex(listAgentEntries(cfg), agentId) < 0) {\n return { ok: false, error: `agent \"${agentId}\" not found`, status: 404 };\n }\n const { config: pruned } = pruneAgentConfig(cfg, agentId);\n return { ok: true, data: { nextConfig: pruned, agentId } };\n}\n\nexport async function runAfterDeletePurge(cfg: Config, agentId: string): Promise<void> {\n await removeAgentDirsFromDisk(cfg, agentId);\n}\n\nexport type AgentFileEntry = {\n name: string;\n missing: boolean;\n size?: number;\n updatedAtMs?: number;\n};\n\nasync function profileMarkdownRootReal(cfg: Config, agentId: string): Promise<string> {\n const dir = resolveAgentProfileDir(cfg, agentId);\n await mkdir(dir, { recursive: true });\n try {\n return await realpath(dir);\n } catch {\n return pathResolve(dir);\n }\n}\n\nfunction assertAllowedFile(name: string): AgentAdminResult<never> | null {\n if (!name || name.includes('/') || name.includes('\\\\') || !EDITABLE_PROFILE_MARKDOWN_NAMES.has(name)) {\n return { ok: false, error: `unsupported file \"${name}\"`, status: 400 };\n }\n return null;\n}\n\nexport async function listAgentProfileFiles(\n cfg: Config,\n agentId: string,\n): Promise<AgentAdminResult<{ agentId: string; profileDir: string; files: AgentFileEntry[] }>> {\n const id = normalizeAgentId(agentId);\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n const root = await profileMarkdownRootReal(cfg, id);\n const names = [...EDITABLE_PROFILE_MARKDOWN_NAMES];\n const files: AgentFileEntry[] = [];\n for (const name of names.sort((a, b) => a.localeCompare(b))) {\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n continue;\n }\n try {\n const st = await stat(abs);\n if (!st.isFile()) {\n continue;\n }\n files.push({\n name,\n missing: false,\n size: st.size,\n updatedAtMs: st.mtimeMs,\n });\n } catch {\n if (!REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET.has(name)) {\n continue;\n }\n files.push({ name, missing: true });\n }\n }\n files.sort((a, b) => a.name.localeCompare(b.name));\n return { ok: true, data: { agentId: id, profileDir: root, files } };\n}\n\nexport async function readAgentProfileFile(\n cfg: Config,\n agentId: string,\n name: string,\n): Promise<AgentAdminResult<{ agentId: string; content: string; path: string }>> {\n const bad = assertAllowedFile(name);\n if (bad) {\n return bad;\n }\n const id = normalizeAgentId(agentId);\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n const root = await profileMarkdownRootReal(cfg, id);\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n return { ok: false, error: 'invalid path', status: 400 };\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return { ok: true, data: { agentId: id, content, path: abs } };\n } catch {\n return { ok: false, error: 'file not found', status: 404 };\n }\n}\n\nexport async function writeAgentProfileFile(\n cfg: Config,\n agentId: string,\n name: string,\n content: string,\n): Promise<AgentAdminResult<{ agentId: string; path: string }>> {\n const bad = assertAllowedFile(name);\n if (bad) {\n return bad;\n }\n const id = normalizeAgentId(agentId);\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n const root = await profileMarkdownRootReal(cfg, id);\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n return { ok: false, error: 'invalid path', status: 400 };\n }\n const rootReal = await profileMarkdownRootReal(cfg, id);\n if (!isPathUnderWorkspace(rootReal, abs)) {\n return { ok: false, error: 'path escapes profile markdown root', status: 400 };\n }\n await writeFile(abs, content, 'utf-8');\n return { ok: true, data: { agentId: id, path: abs } };\n}\n\nexport async function readUserProfileFile(): Promise<AgentAdminResult<{ content: string; path: string }>> {\n const path = resolveUserProfilePath();\n try {\n const content = await readFile(path, 'utf-8');\n return { ok: true, data: { content, path } };\n } catch {\n return { ok: true, data: { content: '', path } };\n }\n}\n\nexport async function writeUserProfileFile(\n content: string,\n): Promise<AgentAdminResult<{ path: string }>> {\n const path = resolveUserProfilePath();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, 'utf-8');\n return { ok: true, data: { path } };\n}\n\n// ---------------------------------------------------------------------------\n// Binary agent avatar (profile markdown root dir, not a SOUL/IDENTITY markdown file)\n// ---------------------------------------------------------------------------\n\nconst AGENT_AVATAR_MAX_BYTES = 512 * 1024;\nconst AGENT_AVATAR_BASENAME = 'agent-avatar';\n\nconst AGENT_AVATAR_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp'] as const;\n\nfunction agentAvatarFilenames(): string[] {\n return AGENT_AVATAR_EXTENSIONS.map((ext) => `${AGENT_AVATAR_BASENAME}${ext}`);\n}\n\nfunction mimeToExt(mime: string): '.png' | '.jpg' | '.jpeg' | '.webp' | null {\n const m = mime.toLowerCase().trim();\n if (m === 'image/png') return '.png';\n if (m === 'image/jpeg' || m === 'image/jpg') return '.jpg';\n if (m === 'image/webp') return '.webp';\n return null;\n}\n\nfunction detectImageMimeFromBytes(buf: Uint8Array): 'image/png' | 'image/jpeg' | 'image/webp' | null {\n if (buf.length >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {\n return 'image/png';\n }\n if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {\n return 'image/jpeg';\n }\n if (\n buf.length >= 12 &&\n buf[0] === 0x52 &&\n buf[1] === 0x49 &&\n buf[2] === 0x46 &&\n buf[3] === 0x46 &&\n buf[8] === 0x57 &&\n buf[9] === 0x45 &&\n buf[10] === 0x42 &&\n buf[11] === 0x50\n ) {\n return 'image/webp';\n }\n return null;\n}\n\nfunction assertAgentExistsForAvatar(cfg: Config, id: string): AgentAdminResult<never> | null {\n if (collectAgentIdsForList(cfg).every((x) => x !== id)) {\n return { ok: false, error: `agent \"${id}\" not found`, status: 404 };\n }\n return null;\n}\n\nexport async function readAgentAvatarFile(\n cfg: Config,\n agentId: string,\n): Promise<AgentAdminResult<{ agentId: string; buffer: Buffer; contentType: string; path: string }>> {\n const missingAgent = assertAgentExistsForAvatar(cfg, agentId);\n if (missingAgent) {\n return missingAgent;\n }\n const id = normalizeAgentId(agentId);\n const root = await profileMarkdownRootReal(cfg, id);\n for (const name of agentAvatarFilenames()) {\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs) {\n continue;\n }\n try {\n const st = await stat(abs);\n if (!st.isFile() || st.size <= 0 || st.size > AGENT_AVATAR_MAX_BYTES) {\n continue;\n }\n const buffer = await readFile(abs);\n const detected = detectImageMimeFromBytes(buffer);\n if (!detected) {\n continue;\n }\n return { ok: true, data: { agentId: id, buffer, contentType: detected, path: abs } };\n } catch {\n /* try next */\n }\n }\n return { ok: false, error: 'avatar not found', status: 404 };\n}\n\nexport async function writeAgentAvatarFromBase64(\n cfg: Config,\n agentId: string,\n base64: string,\n mimeType: string,\n): Promise<AgentAdminResult<{ agentId: string; path: string }>> {\n const missingAgent = assertAgentExistsForAvatar(cfg, agentId);\n if (missingAgent) {\n return missingAgent;\n }\n const id = normalizeAgentId(agentId);\n const ext = mimeToExt(mimeType);\n if (!ext) {\n return { ok: false, error: 'unsupported mimeType (use image/png, image/jpeg, or image/webp)', status: 400 };\n }\n let raw: Buffer;\n try {\n raw = Buffer.from(base64, 'base64');\n } catch {\n return { ok: false, error: 'invalid base64', status: 400 };\n }\n if (raw.length === 0 || raw.length > AGENT_AVATAR_MAX_BYTES) {\n return { ok: false, error: `avatar must be non-empty and at most ${AGENT_AVATAR_MAX_BYTES} bytes`, status: 400 };\n }\n const detected = detectImageMimeFromBytes(raw);\n if (!detected || !extMatchesDetectedMime(ext, detected)) {\n return { ok: false, error: 'file content does not match declared image type', status: 400 };\n }\n\n const root = await profileMarkdownRootReal(cfg, id);\n const rootReal = await profileMarkdownRootReal(cfg, id);\n const targetName = `${AGENT_AVATAR_BASENAME}${ext}`;\n const abs = resolveWorkspaceSafePath(root, targetName);\n if (!abs || !isPathUnderWorkspace(rootReal, abs)) {\n return { ok: false, error: 'invalid path', status: 400 };\n }\n for (const name of agentAvatarFilenames()) {\n if (name === targetName) {\n continue;\n }\n const other = resolveWorkspaceSafePath(root, name);\n if (other && isPathUnderWorkspace(rootReal, other)) {\n try {\n await unlink(other);\n } catch {\n /* absent */\n }\n }\n }\n await writeFile(abs, raw);\n return { ok: true, data: { agentId: id, path: abs } };\n}\n\nfunction mimeToExtToMime(ext: '.png' | '.jpg' | '.jpeg' | '.webp'): 'image/png' | 'image/jpeg' | 'image/webp' {\n if (ext === '.png') return 'image/png';\n if (ext === '.webp') return 'image/webp';\n return 'image/jpeg';\n}\n\nfunction extMatchesDetectedMime(\n ext: '.png' | '.jpg' | '.jpeg' | '.webp',\n detected: 'image/png' | 'image/jpeg' | 'image/webp',\n): boolean {\n return detected === mimeToExtToMime(ext);\n}\n\n/** Remove any `agent-avatar.*` in the agent profile markdown root. Idempotent: ok even when no file existed. */\nexport async function deleteAgentAvatarFile(cfg: Config, agentId: string): Promise<AgentAdminResult<{ agentId: string }>> {\n const missingAgent = assertAgentExistsForAvatar(cfg, agentId);\n if (missingAgent) {\n return missingAgent;\n }\n const id = normalizeAgentId(agentId);\n const root = await profileMarkdownRootReal(cfg, id);\n const rootReal = await profileMarkdownRootReal(cfg, id);\n for (const name of agentAvatarFilenames()) {\n const abs = resolveWorkspaceSafePath(root, name);\n if (!abs || !isPathUnderWorkspace(rootReal, abs)) {\n continue;\n }\n try {\n await unlink(abs);\n } catch {\n /* absent */\n }\n }\n return { ok: true, data: { agentId: id } };\n}\n"],"mappings":"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"}
@@ -35,7 +35,6 @@ export type CreateCapabilityPresetBody = {
35
35
  models?: CapabilityPreset['models'];
36
36
  tools?: CapabilityPreset['tools'];
37
37
  skills?: CapabilityPreset['skills'];
38
- memory?: CapabilityPreset['memory'];
39
38
  workflows?: CapabilityPreset['workflows'];
40
39
  boundaries?: CapabilityPreset['boundaries'];
41
40
  runtime?: CapabilityPreset['runtime'];
@@ -49,7 +48,6 @@ export type UpdateCapabilityPresetBody = {
49
48
  models?: CapabilityPreset['models'] | null;
50
49
  tools?: CapabilityPreset['tools'] | null;
51
50
  skills?: CapabilityPreset['skills'] | null;
52
- memory?: CapabilityPreset['memory'] | null;
53
51
  workflows?: CapabilityPreset['workflows'] | null;
54
52
  boundaries?: CapabilityPreset['boundaries'] | null;
55
53
  runtime?: CapabilityPreset['runtime'] | null;
@@ -1 +1 @@
1
- {"version":3,"file":"capability-presets-admin.js","names":[],"sources":["../../../src/gateway/capability-presets-admin.ts"],"sourcesContent":["import {\n CapabilityPresetSchema,\n DEFAULT_CAPABILITY_PRESET_ID,\n type CapabilityPreset,\n} from '../agent-manifest/schema.js';\nimport { linearizePresetIds } from '../agent-manifest/preset-chain.js';\nimport type { Config } from '../config/schema.js';\nimport { GATEWAY_BUILTIN_TOOL_IDS } from './agent-builtin-tools.js';\n\nexport type CapabilityPresetAdminResult<T> =\n | { ok: true; data: T }\n | { ok: false; error: string; status?: 400 | 404 | 409 | 500 };\n\nexport type CapabilityPresetAgentUsage = {\n agentId: string;\n agentName?: string;\n direct: boolean;\n};\n\nexport type CapabilityPresetRow = CapabilityPreset & {\n usage: CapabilityPresetAgentUsage[];\n};\n\nexport type CapabilityPresetsListResponse = {\n defaultPresetId: string;\n presets: CapabilityPresetRow[];\n agents: Array<{ id: string; name?: string; extends: string[] }>;\n builtinToolIds: string[];\n};\n\nexport type CreateCapabilityPresetBody = {\n id?: string;\n name?: string;\n description?: string;\n version?: number;\n extends?: CapabilityPreset['extends'];\n models?: CapabilityPreset['models'];\n tools?: CapabilityPreset['tools'];\n skills?: CapabilityPreset['skills'];\n memory?: CapabilityPreset['memory'];\n workflows?: CapabilityPreset['workflows'];\n boundaries?: CapabilityPreset['boundaries'];\n runtime?: CapabilityPreset['runtime'];\n locks?: CapabilityPreset['locks'];\n};\n\nexport type UpdateCapabilityPresetBody = {\n name?: string;\n description?: string | null;\n version?: number;\n extends?: string[] | null;\n models?: CapabilityPreset['models'] | null;\n tools?: CapabilityPreset['tools'] | null;\n skills?: CapabilityPreset['skills'] | null;\n memory?: CapabilityPreset['memory'] | null;\n workflows?: CapabilityPreset['workflows'] | null;\n boundaries?: CapabilityPreset['boundaries'] | null;\n runtime?: CapabilityPreset['runtime'] | null;\n locks?: string[] | null;\n};\n\nfunction presetMap(cfg: Config): Record<string, CapabilityPreset> {\n return cfg.agents.capabilityPresets ?? {};\n}\n\nfunction normalizePresetId(id: string): string {\n return id.trim().toLowerCase();\n}\n\nfunction agentUsage(cfg: Config, presetId: string): CapabilityPresetAgentUsage[] {\n const defaultPresetId = cfg.agents.defaultPreset || DEFAULT_CAPABILITY_PRESET_ID;\n return cfg.agents.list\n .filter((agent) => {\n const roots = [\n ...(cfg.agents.capabilityPresets[defaultPresetId] ? [defaultPresetId] : []),\n ...(agent.extends ?? []).filter((id) => id !== defaultPresetId),\n ];\n return linearizePresetIds(roots, cfg.agents.capabilityPresets).includes(presetId);\n })\n .map((agent) => ({\n agentId: agent.id,\n ...(agent.identity.name ? { agentName: agent.identity.name } : {}),\n direct: presetId === defaultPresetId || (agent.extends ?? []).includes(presetId),\n }))\n .sort((a, b) => a.agentId.localeCompare(b.agentId));\n}\n\nfunction validatePresetReferences(\n presets: Record<string, CapabilityPreset>,\n): CapabilityPresetAdminResult<void> {\n for (const [id, preset] of Object.entries(presets)) {\n for (const parent of preset.extends ?? []) {\n if (!presets[parent]) {\n return { ok: false, error: `capability preset \"${id}\" extends missing preset \"${parent}\"`, status: 400 };\n }\n }\n }\n\n const visiting = new Set<string>();\n const visited = new Set<string>();\n const visit = (id: string, stack: string[]): CapabilityPresetAdminResult<void> => {\n if (visiting.has(id)) {\n return { ok: false, error: `capability preset cycle detected: ${[...stack, id].join(' -> ')}`, status: 400 };\n }\n if (visited.has(id)) {\n return { ok: true, data: undefined };\n }\n visiting.add(id);\n for (const parent of presets[id]?.extends ?? []) {\n const res = visit(parent, [...stack, id]);\n if (!res.ok) return res;\n }\n visiting.delete(id);\n visited.add(id);\n return { ok: true, data: undefined };\n };\n\n for (const id of Object.keys(presets)) {\n const res = visit(id, []);\n if (!res.ok) return res;\n }\n return { ok: true, data: undefined };\n}\n\nfunction validateAgentPresetReferences(cfg: Config): CapabilityPresetAdminResult<void> {\n const presets = presetMap(cfg);\n for (const agent of cfg.agents.list) {\n for (const presetId of agent.extends ?? []) {\n if (!presets[presetId]) {\n return { ok: false, error: `agent \"${agent.id}\" extends missing preset \"${presetId}\"`, status: 400 };\n }\n }\n }\n return { ok: true, data: undefined };\n}\n\nfunction nextConfigWithPresets(\n cfg: Config,\n presets: Record<string, CapabilityPreset>,\n): CapabilityPresetAdminResult<{ nextConfig: Config }> {\n const refs = validatePresetReferences(presets);\n if (refs.ok === false) return { ok: false, error: refs.error, status: refs.status };\n const nextConfig: Config = {\n ...cfg,\n agents: {\n ...cfg.agents,\n capabilityPresets: presets,\n },\n };\n const agentRefs = validateAgentPresetReferences(nextConfig);\n if (agentRefs.ok === false) return { ok: false, error: agentRefs.error, status: agentRefs.status };\n return { ok: true, data: { nextConfig } };\n}\n\nexport function listCapabilityPresets(cfg: Config): CapabilityPresetsListResponse {\n const presets = Object.values(presetMap(cfg))\n .map((preset) => ({ ...preset, usage: agentUsage(cfg, preset.id) }))\n .sort((a, b) => a.id.localeCompare(b.id));\n const agents = cfg.agents.list\n .map((agent) => ({\n id: agent.id,\n ...(agent.identity.name ? { name: agent.identity.name } : {}),\n extends: [...(agent.extends ?? [])],\n }))\n .sort((a, b) => a.id.localeCompare(b.id));\n return { defaultPresetId: cfg.agents.defaultPreset, presets, agents, builtinToolIds: [...GATEWAY_BUILTIN_TOOL_IDS] };\n}\n\nexport function prepareCreateCapabilityPreset(\n cfg: Config,\n body: CreateCapabilityPresetBody,\n): CapabilityPresetAdminResult<{ nextConfig: Config; presetId: string }> {\n const id = normalizePresetId(body.id ?? '');\n const name = body.name?.trim() || id;\n const description = body.description?.trim();\n const { id: _id, name: _name, description: _description, ...policy } = body;\n const parsed = CapabilityPresetSchema.safeParse({\n ...policy,\n id,\n name,\n version: body.version ?? 1,\n ...(description ? { description } : {}),\n });\n if (!parsed.success) {\n return { ok: false, error: parsed.error.issues[0]?.message ?? 'capability preset is invalid', status: 400 };\n }\n const presets = presetMap(cfg);\n if (presets[id]) {\n return { ok: false, error: `capability preset \"${id}\" already exists`, status: 409 };\n }\n const next = nextConfigWithPresets(cfg, { ...presets, [id]: parsed.data });\n if (next.ok === false) return { ok: false, error: next.error, status: next.status };\n return { ok: true, data: { ...next.data, presetId: id } };\n}\n\nexport function prepareUpdateCapabilityPreset(\n cfg: Config,\n presetIdRaw: string,\n body: UpdateCapabilityPresetBody,\n): CapabilityPresetAdminResult<{ nextConfig: Config }> {\n const presetId = normalizePresetId(presetIdRaw);\n const presets = presetMap(cfg);\n const current = presets[presetId];\n if (!current) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 404 };\n }\n const nextPreset: CapabilityPreset = { ...current };\n\n if (body.name !== undefined) {\n const name = body.name.trim();\n if (!name) {\n return { ok: false, error: 'name must be a non-empty string', status: 400 };\n }\n nextPreset.name = name;\n }\n if (body.description !== undefined) {\n if (body.description === null || !body.description.trim()) {\n delete nextPreset.description;\n } else {\n nextPreset.description = body.description.trim();\n }\n }\n nextPreset.version = body.version ?? current.version + 1;\n for (const key of ['extends', 'models', 'tools', 'skills', 'memory', 'workflows', 'boundaries', 'runtime', 'locks'] as const) {\n if (body[key] === undefined) continue;\n if (body[key] === null) {\n delete nextPreset[key];\n } else {\n (nextPreset as Record<string, unknown>)[key] = body[key];\n }\n }\n\n const parsed = CapabilityPresetSchema.safeParse(nextPreset);\n if (!parsed.success) {\n return { ok: false, error: parsed.error.issues[0]?.message ?? 'capability preset is invalid', status: 400 };\n }\n return nextConfigWithPresets(cfg, { ...presets, [presetId]: parsed.data });\n}\n\nexport function prepareDeleteCapabilityPreset(\n cfg: Config,\n presetIdRaw: string,\n): CapabilityPresetAdminResult<{ nextConfig: Config }> {\n const presetId = normalizePresetId(presetIdRaw);\n const presets = presetMap(cfg);\n if (!presets[presetId]) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 404 };\n }\n if (presetId === cfg.agents.defaultPreset) {\n return { ok: false, error: `capability preset \"${presetId}\" is the global default and cannot be deleted`, status: 409 };\n }\n const usage = agentUsage(cfg, presetId);\n if (usage.length > 0) {\n return {\n ok: false,\n error: `capability preset \"${presetId}\" is used by agents: ${usage.map((u) => u.agentId).join(', ')}`,\n status: 409,\n };\n }\n const nextPresets = { ...presets };\n delete nextPresets[presetId];\n return nextConfigWithPresets(cfg, nextPresets);\n}\n"],"mappings":";;;;aAIqC;mBACkC;AAwDvE,SAAS,UAAU,KAA+C;AAChE,QAAO,IAAI,OAAO,qBAAqB,EAAE;;AAG3C,SAAS,kBAAkB,IAAoB;AAC7C,QAAO,GAAG,MAAM,CAAC,aAAa;;AAGhC,SAAS,WAAW,KAAa,UAAgD;CAC/E,MAAM,kBAAkB,IAAI,OAAO,iBAAA;AACnC,QAAO,IAAI,OAAO,KACf,QAAQ,UAAU;AAKjB,SAAO,mBAAmB,CAHxB,GAAI,IAAI,OAAO,kBAAkB,mBAAmB,CAAC,gBAAgB,GAAG,EAAE,EAC1E,IAAI,MAAM,WAAW,EAAE,EAAE,QAAQ,OAAO,OAAO,gBAAgB,CAElC,EAAE,IAAI,OAAO,kBAAkB,CAAC,SAAS,SAAS;GACjF,CACD,KAAK,WAAW;EACf,SAAS,MAAM;EACf,GAAI,MAAM,SAAS,OAAO,EAAE,WAAW,MAAM,SAAS,MAAM,GAAG,EAAE;EACjE,QAAQ,aAAa,oBAAoB,MAAM,WAAW,EAAE,EAAE,SAAS,SAAS;EACjF,EAAE,CACF,MAAM,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,QAAQ,CAAC;;AAGvD,SAAS,yBACP,SACmC;AACnC,MAAK,MAAM,CAAC,IAAI,WAAW,OAAO,QAAQ,QAAQ,CAChD,MAAK,MAAM,UAAU,OAAO,WAAW,EAAE,CACvC,KAAI,CAAC,QAAQ,QACX,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,GAAG,4BAA4B,OAAO;EAAI,QAAQ;EAAK;CAK9G,MAAM,2BAAW,IAAI,KAAa;CAClC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,SAAS,IAAY,UAAuD;AAChF,MAAI,SAAS,IAAI,GAAG,CAClB,QAAO;GAAE,IAAI;GAAO,OAAO,qCAAqC,CAAC,GAAG,OAAO,GAAG,CAAC,KAAK,OAAO;GAAI,QAAQ;GAAK;AAE9G,MAAI,QAAQ,IAAI,GAAG,CACjB,QAAO;GAAE,IAAI;GAAM,MAAM,KAAA;GAAW;AAEtC,WAAS,IAAI,GAAG;AAChB,OAAK,MAAM,UAAU,QAAQ,KAAK,WAAW,EAAE,EAAE;GAC/C,MAAM,MAAM,MAAM,QAAQ,CAAC,GAAG,OAAO,GAAG,CAAC;AACzC,OAAI,CAAC,IAAI,GAAI,QAAO;;AAEtB,WAAS,OAAO,GAAG;AACnB,UAAQ,IAAI,GAAG;AACf,SAAO;GAAE,IAAI;GAAM,MAAM,KAAA;GAAW;;AAGtC,MAAK,MAAM,MAAM,OAAO,KAAK,QAAQ,EAAE;EACrC,MAAM,MAAM,MAAM,IAAI,EAAE,CAAC;AACzB,MAAI,CAAC,IAAI,GAAI,QAAO;;AAEtB,QAAO;EAAE,IAAI;EAAM,MAAM,KAAA;EAAW;;AAGtC,SAAS,8BAA8B,KAAgD;CACrF,MAAM,UAAU,UAAU,IAAI;AAC9B,MAAK,MAAM,SAAS,IAAI,OAAO,KAC7B,MAAK,MAAM,YAAY,MAAM,WAAW,EAAE,CACxC,KAAI,CAAC,QAAQ,UACX,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,MAAM,GAAG,4BAA4B,SAAS;EAAI,QAAQ;EAAK;AAI1G,QAAO;EAAE,IAAI;EAAM,MAAM,KAAA;EAAW;;AAGtC,SAAS,sBACP,KACA,SACqD;CACrD,MAAM,OAAO,yBAAyB,QAAQ;AAC9C,KAAI,KAAK,OAAO,MAAO,QAAO;EAAE,IAAI;EAAO,OAAO,KAAK;EAAO,QAAQ,KAAK;EAAQ;CACnF,MAAM,aAAqB;EACzB,GAAG;EACH,QAAQ;GACN,GAAG,IAAI;GACP,mBAAmB;GACpB;EACF;CACD,MAAM,YAAY,8BAA8B,WAAW;AAC3D,KAAI,UAAU,OAAO,MAAO,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU;EAAO,QAAQ,UAAU;EAAQ;AAClG,QAAO;EAAE,IAAI;EAAM,MAAM,EAAE,YAAY;EAAE;;AAG3C,SAAgB,sBAAsB,KAA4C;CAChF,MAAM,UAAU,OAAO,OAAO,UAAU,IAAI,CAAC,CAC1C,KAAK,YAAY;EAAE,GAAG;EAAQ,OAAO,WAAW,KAAK,OAAO,GAAG;EAAE,EAAE,CACnE,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC;CAC3C,MAAM,SAAS,IAAI,OAAO,KACvB,KAAK,WAAW;EACf,IAAI,MAAM;EACV,GAAI,MAAM,SAAS,OAAO,EAAE,MAAM,MAAM,SAAS,MAAM,GAAG,EAAE;EAC5D,SAAS,CAAC,GAAI,MAAM,WAAW,EAAE,CAAE;EACpC,EAAE,CACF,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC;AAC3C,QAAO;EAAE,iBAAiB,IAAI,OAAO;EAAe;EAAS;EAAQ,gBAAgB,CAAC,GAAG,yBAAyB;EAAE;;AAGtH,SAAgB,8BACd,KACA,MACuE;CACvE,MAAM,KAAK,kBAAkB,KAAK,MAAM,GAAG;CAC3C,MAAM,OAAO,KAAK,MAAM,MAAM,IAAI;CAClC,MAAM,cAAc,KAAK,aAAa,MAAM;CAC5C,MAAM,EAAE,IAAI,KAAK,MAAM,OAAO,aAAa,cAAc,GAAG,WAAW;CACvE,MAAM,SAAS,uBAAuB,UAAU;EAC9C,GAAG;EACH;EACA;EACA,SAAS,KAAK,WAAW;EACzB,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;EACvC,CAAC;AACF,KAAI,CAAC,OAAO,QACV,QAAO;EAAE,IAAI;EAAO,OAAO,OAAO,MAAM,OAAO,IAAI,WAAW;EAAgC,QAAQ;EAAK;CAE7G,MAAM,UAAU,UAAU,IAAI;AAC9B,KAAI,QAAQ,IACV,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,GAAG;EAAmB,QAAQ;EAAK;CAEtF,MAAM,OAAO,sBAAsB,KAAK;EAAE,GAAG;GAAU,KAAK,OAAO;EAAM,CAAC;AAC1E,KAAI,KAAK,OAAO,MAAO,QAAO;EAAE,IAAI;EAAO,OAAO,KAAK;EAAO,QAAQ,KAAK;EAAQ;AACnF,QAAO;EAAE,IAAI;EAAM,MAAM;GAAE,GAAG,KAAK;GAAM,UAAU;GAAI;EAAE;;AAG3D,SAAgB,8BACd,KACA,aACA,MACqD;CACrD,MAAM,WAAW,kBAAkB,YAAY;CAC/C,MAAM,UAAU,UAAU,IAAI;CAC9B,MAAM,UAAU,QAAQ;AACxB,KAAI,CAAC,QACH,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,SAAS;EAAc,QAAQ;EAAK;CAEvF,MAAM,aAA+B,EAAE,GAAG,SAAS;AAEnD,KAAI,KAAK,SAAS,KAAA,GAAW;EAC3B,MAAM,OAAO,KAAK,KAAK,MAAM;AAC7B,MAAI,CAAC,KACH,QAAO;GAAE,IAAI;GAAO,OAAO;GAAmC,QAAQ;GAAK;AAE7E,aAAW,OAAO;;AAEpB,KAAI,KAAK,gBAAgB,KAAA,EACvB,KAAI,KAAK,gBAAgB,QAAQ,CAAC,KAAK,YAAY,MAAM,CACvD,QAAO,WAAW;KAElB,YAAW,cAAc,KAAK,YAAY,MAAM;AAGpD,YAAW,UAAU,KAAK,WAAW,QAAQ,UAAU;AACvD,MAAK,MAAM,OAAO;EAAC;EAAW;EAAU;EAAS;EAAU;EAAU;EAAa;EAAc;EAAW;EAAQ,EAAW;AAC5H,MAAI,KAAK,SAAS,KAAA,EAAW;AAC7B,MAAI,KAAK,SAAS,KAChB,QAAO,WAAW;MAEjB,YAAuC,OAAO,KAAK;;CAIxD,MAAM,SAAS,uBAAuB,UAAU,WAAW;AAC3D,KAAI,CAAC,OAAO,QACV,QAAO;EAAE,IAAI;EAAO,OAAO,OAAO,MAAM,OAAO,IAAI,WAAW;EAAgC,QAAQ;EAAK;AAE7G,QAAO,sBAAsB,KAAK;EAAE,GAAG;GAAU,WAAW,OAAO;EAAM,CAAC;;AAG5E,SAAgB,8BACd,KACA,aACqD;CACrD,MAAM,WAAW,kBAAkB,YAAY;CAC/C,MAAM,UAAU,UAAU,IAAI;AAC9B,KAAI,CAAC,QAAQ,UACX,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,SAAS;EAAc,QAAQ;EAAK;AAEvF,KAAI,aAAa,IAAI,OAAO,cAC1B,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,SAAS;EAAgD,QAAQ;EAAK;CAEzH,MAAM,QAAQ,WAAW,KAAK,SAAS;AACvC,KAAI,MAAM,SAAS,EACjB,QAAO;EACL,IAAI;EACJ,OAAO,sBAAsB,SAAS,uBAAuB,MAAM,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK;EACnG,QAAQ;EACT;CAEH,MAAM,cAAc,EAAE,GAAG,SAAS;AAClC,QAAO,YAAY;AACnB,QAAO,sBAAsB,KAAK,YAAY"}
1
+ {"version":3,"file":"capability-presets-admin.js","names":[],"sources":["../../../src/gateway/capability-presets-admin.ts"],"sourcesContent":["import {\n CapabilityPresetSchema,\n DEFAULT_CAPABILITY_PRESET_ID,\n type CapabilityPreset,\n} from '../agent-manifest/schema.js';\nimport { linearizePresetIds } from '../agent-manifest/preset-chain.js';\nimport type { Config } from '../config/schema.js';\nimport { GATEWAY_BUILTIN_TOOL_IDS } from './agent-builtin-tools.js';\n\nexport type CapabilityPresetAdminResult<T> =\n | { ok: true; data: T }\n | { ok: false; error: string; status?: 400 | 404 | 409 | 500 };\n\nexport type CapabilityPresetAgentUsage = {\n agentId: string;\n agentName?: string;\n direct: boolean;\n};\n\nexport type CapabilityPresetRow = CapabilityPreset & {\n usage: CapabilityPresetAgentUsage[];\n};\n\nexport type CapabilityPresetsListResponse = {\n defaultPresetId: string;\n presets: CapabilityPresetRow[];\n agents: Array<{ id: string; name?: string; extends: string[] }>;\n builtinToolIds: string[];\n};\n\nexport type CreateCapabilityPresetBody = {\n id?: string;\n name?: string;\n description?: string;\n version?: number;\n extends?: CapabilityPreset['extends'];\n models?: CapabilityPreset['models'];\n tools?: CapabilityPreset['tools'];\n skills?: CapabilityPreset['skills'];\n workflows?: CapabilityPreset['workflows'];\n boundaries?: CapabilityPreset['boundaries'];\n runtime?: CapabilityPreset['runtime'];\n locks?: CapabilityPreset['locks'];\n};\n\nexport type UpdateCapabilityPresetBody = {\n name?: string;\n description?: string | null;\n version?: number;\n extends?: string[] | null;\n models?: CapabilityPreset['models'] | null;\n tools?: CapabilityPreset['tools'] | null;\n skills?: CapabilityPreset['skills'] | null;\n workflows?: CapabilityPreset['workflows'] | null;\n boundaries?: CapabilityPreset['boundaries'] | null;\n runtime?: CapabilityPreset['runtime'] | null;\n locks?: string[] | null;\n};\n\nfunction presetMap(cfg: Config): Record<string, CapabilityPreset> {\n return cfg.agents.capabilityPresets ?? {};\n}\n\nfunction normalizePresetId(id: string): string {\n return id.trim().toLowerCase();\n}\n\nfunction agentUsage(cfg: Config, presetId: string): CapabilityPresetAgentUsage[] {\n const defaultPresetId = cfg.agents.defaultPreset || DEFAULT_CAPABILITY_PRESET_ID;\n return cfg.agents.list\n .filter((agent) => {\n const roots = [\n ...(cfg.agents.capabilityPresets[defaultPresetId] ? [defaultPresetId] : []),\n ...(agent.extends ?? []).filter((id) => id !== defaultPresetId),\n ];\n return linearizePresetIds(roots, cfg.agents.capabilityPresets).includes(presetId);\n })\n .map((agent) => ({\n agentId: agent.id,\n ...(agent.identity.name ? { agentName: agent.identity.name } : {}),\n direct: presetId === defaultPresetId || (agent.extends ?? []).includes(presetId),\n }))\n .sort((a, b) => a.agentId.localeCompare(b.agentId));\n}\n\nfunction validatePresetReferences(\n presets: Record<string, CapabilityPreset>,\n): CapabilityPresetAdminResult<void> {\n for (const [id, preset] of Object.entries(presets)) {\n for (const parent of preset.extends ?? []) {\n if (!presets[parent]) {\n return { ok: false, error: `capability preset \"${id}\" extends missing preset \"${parent}\"`, status: 400 };\n }\n }\n }\n\n const visiting = new Set<string>();\n const visited = new Set<string>();\n const visit = (id: string, stack: string[]): CapabilityPresetAdminResult<void> => {\n if (visiting.has(id)) {\n return { ok: false, error: `capability preset cycle detected: ${[...stack, id].join(' -> ')}`, status: 400 };\n }\n if (visited.has(id)) {\n return { ok: true, data: undefined };\n }\n visiting.add(id);\n for (const parent of presets[id]?.extends ?? []) {\n const res = visit(parent, [...stack, id]);\n if (!res.ok) return res;\n }\n visiting.delete(id);\n visited.add(id);\n return { ok: true, data: undefined };\n };\n\n for (const id of Object.keys(presets)) {\n const res = visit(id, []);\n if (!res.ok) return res;\n }\n return { ok: true, data: undefined };\n}\n\nfunction validateAgentPresetReferences(cfg: Config): CapabilityPresetAdminResult<void> {\n const presets = presetMap(cfg);\n for (const agent of cfg.agents.list) {\n for (const presetId of agent.extends ?? []) {\n if (!presets[presetId]) {\n return { ok: false, error: `agent \"${agent.id}\" extends missing preset \"${presetId}\"`, status: 400 };\n }\n }\n }\n return { ok: true, data: undefined };\n}\n\nfunction nextConfigWithPresets(\n cfg: Config,\n presets: Record<string, CapabilityPreset>,\n): CapabilityPresetAdminResult<{ nextConfig: Config }> {\n const refs = validatePresetReferences(presets);\n if (refs.ok === false) return { ok: false, error: refs.error, status: refs.status };\n const nextConfig: Config = {\n ...cfg,\n agents: {\n ...cfg.agents,\n capabilityPresets: presets,\n },\n };\n const agentRefs = validateAgentPresetReferences(nextConfig);\n if (agentRefs.ok === false) return { ok: false, error: agentRefs.error, status: agentRefs.status };\n return { ok: true, data: { nextConfig } };\n}\n\nexport function listCapabilityPresets(cfg: Config): CapabilityPresetsListResponse {\n const presets = Object.values(presetMap(cfg))\n .map((preset) => ({ ...preset, usage: agentUsage(cfg, preset.id) }))\n .sort((a, b) => a.id.localeCompare(b.id));\n const agents = cfg.agents.list\n .map((agent) => ({\n id: agent.id,\n ...(agent.identity.name ? { name: agent.identity.name } : {}),\n extends: [...(agent.extends ?? [])],\n }))\n .sort((a, b) => a.id.localeCompare(b.id));\n return { defaultPresetId: cfg.agents.defaultPreset, presets, agents, builtinToolIds: [...GATEWAY_BUILTIN_TOOL_IDS] };\n}\n\nexport function prepareCreateCapabilityPreset(\n cfg: Config,\n body: CreateCapabilityPresetBody,\n): CapabilityPresetAdminResult<{ nextConfig: Config; presetId: string }> {\n const id = normalizePresetId(body.id ?? '');\n const name = body.name?.trim() || id;\n const description = body.description?.trim();\n const { id: _id, name: _name, description: _description, ...policy } = body;\n const parsed = CapabilityPresetSchema.safeParse({\n ...policy,\n id,\n name,\n version: body.version ?? 1,\n ...(description ? { description } : {}),\n });\n if (!parsed.success) {\n return { ok: false, error: parsed.error.issues[0]?.message ?? 'capability preset is invalid', status: 400 };\n }\n const presets = presetMap(cfg);\n if (presets[id]) {\n return { ok: false, error: `capability preset \"${id}\" already exists`, status: 409 };\n }\n const next = nextConfigWithPresets(cfg, { ...presets, [id]: parsed.data });\n if (next.ok === false) return { ok: false, error: next.error, status: next.status };\n return { ok: true, data: { ...next.data, presetId: id } };\n}\n\nexport function prepareUpdateCapabilityPreset(\n cfg: Config,\n presetIdRaw: string,\n body: UpdateCapabilityPresetBody,\n): CapabilityPresetAdminResult<{ nextConfig: Config }> {\n const presetId = normalizePresetId(presetIdRaw);\n const presets = presetMap(cfg);\n const current = presets[presetId];\n if (!current) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 404 };\n }\n const nextPreset: CapabilityPreset = { ...current };\n\n if (body.name !== undefined) {\n const name = body.name.trim();\n if (!name) {\n return { ok: false, error: 'name must be a non-empty string', status: 400 };\n }\n nextPreset.name = name;\n }\n if (body.description !== undefined) {\n if (body.description === null || !body.description.trim()) {\n delete nextPreset.description;\n } else {\n nextPreset.description = body.description.trim();\n }\n }\n nextPreset.version = body.version ?? current.version + 1;\n for (const key of ['extends', 'models', 'tools', 'skills', 'memory', 'workflows', 'boundaries', 'runtime', 'locks'] as const) {\n if (body[key] === undefined) continue;\n if (body[key] === null) {\n delete nextPreset[key];\n } else {\n (nextPreset as Record<string, unknown>)[key] = body[key];\n }\n }\n\n const parsed = CapabilityPresetSchema.safeParse(nextPreset);\n if (!parsed.success) {\n return { ok: false, error: parsed.error.issues[0]?.message ?? 'capability preset is invalid', status: 400 };\n }\n return nextConfigWithPresets(cfg, { ...presets, [presetId]: parsed.data });\n}\n\nexport function prepareDeleteCapabilityPreset(\n cfg: Config,\n presetIdRaw: string,\n): CapabilityPresetAdminResult<{ nextConfig: Config }> {\n const presetId = normalizePresetId(presetIdRaw);\n const presets = presetMap(cfg);\n if (!presets[presetId]) {\n return { ok: false, error: `capability preset \"${presetId}\" not found`, status: 404 };\n }\n if (presetId === cfg.agents.defaultPreset) {\n return { ok: false, error: `capability preset \"${presetId}\" is the global default and cannot be deleted`, status: 409 };\n }\n const usage = agentUsage(cfg, presetId);\n if (usage.length > 0) {\n return {\n ok: false,\n error: `capability preset \"${presetId}\" is used by agents: ${usage.map((u) => u.agentId).join(', ')}`,\n status: 409,\n };\n }\n const nextPresets = { ...presets };\n delete nextPresets[presetId];\n return nextConfigWithPresets(cfg, nextPresets);\n}\n"],"mappings":";;;;aAIqC;mBACkC;AAsDvE,SAAS,UAAU,KAA+C;AAChE,QAAO,IAAI,OAAO,qBAAqB,EAAE;;AAG3C,SAAS,kBAAkB,IAAoB;AAC7C,QAAO,GAAG,MAAM,CAAC,aAAa;;AAGhC,SAAS,WAAW,KAAa,UAAgD;CAC/E,MAAM,kBAAkB,IAAI,OAAO,iBAAA;AACnC,QAAO,IAAI,OAAO,KACf,QAAQ,UAAU;AAKjB,SAAO,mBAAmB,CAHxB,GAAI,IAAI,OAAO,kBAAkB,mBAAmB,CAAC,gBAAgB,GAAG,EAAE,EAC1E,IAAI,MAAM,WAAW,EAAE,EAAE,QAAQ,OAAO,OAAO,gBAAgB,CAElC,EAAE,IAAI,OAAO,kBAAkB,CAAC,SAAS,SAAS;GACjF,CACD,KAAK,WAAW;EACf,SAAS,MAAM;EACf,GAAI,MAAM,SAAS,OAAO,EAAE,WAAW,MAAM,SAAS,MAAM,GAAG,EAAE;EACjE,QAAQ,aAAa,oBAAoB,MAAM,WAAW,EAAE,EAAE,SAAS,SAAS;EACjF,EAAE,CACF,MAAM,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,QAAQ,CAAC;;AAGvD,SAAS,yBACP,SACmC;AACnC,MAAK,MAAM,CAAC,IAAI,WAAW,OAAO,QAAQ,QAAQ,CAChD,MAAK,MAAM,UAAU,OAAO,WAAW,EAAE,CACvC,KAAI,CAAC,QAAQ,QACX,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,GAAG,4BAA4B,OAAO;EAAI,QAAQ;EAAK;CAK9G,MAAM,2BAAW,IAAI,KAAa;CAClC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,SAAS,IAAY,UAAuD;AAChF,MAAI,SAAS,IAAI,GAAG,CAClB,QAAO;GAAE,IAAI;GAAO,OAAO,qCAAqC,CAAC,GAAG,OAAO,GAAG,CAAC,KAAK,OAAO;GAAI,QAAQ;GAAK;AAE9G,MAAI,QAAQ,IAAI,GAAG,CACjB,QAAO;GAAE,IAAI;GAAM,MAAM,KAAA;GAAW;AAEtC,WAAS,IAAI,GAAG;AAChB,OAAK,MAAM,UAAU,QAAQ,KAAK,WAAW,EAAE,EAAE;GAC/C,MAAM,MAAM,MAAM,QAAQ,CAAC,GAAG,OAAO,GAAG,CAAC;AACzC,OAAI,CAAC,IAAI,GAAI,QAAO;;AAEtB,WAAS,OAAO,GAAG;AACnB,UAAQ,IAAI,GAAG;AACf,SAAO;GAAE,IAAI;GAAM,MAAM,KAAA;GAAW;;AAGtC,MAAK,MAAM,MAAM,OAAO,KAAK,QAAQ,EAAE;EACrC,MAAM,MAAM,MAAM,IAAI,EAAE,CAAC;AACzB,MAAI,CAAC,IAAI,GAAI,QAAO;;AAEtB,QAAO;EAAE,IAAI;EAAM,MAAM,KAAA;EAAW;;AAGtC,SAAS,8BAA8B,KAAgD;CACrF,MAAM,UAAU,UAAU,IAAI;AAC9B,MAAK,MAAM,SAAS,IAAI,OAAO,KAC7B,MAAK,MAAM,YAAY,MAAM,WAAW,EAAE,CACxC,KAAI,CAAC,QAAQ,UACX,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,MAAM,GAAG,4BAA4B,SAAS;EAAI,QAAQ;EAAK;AAI1G,QAAO;EAAE,IAAI;EAAM,MAAM,KAAA;EAAW;;AAGtC,SAAS,sBACP,KACA,SACqD;CACrD,MAAM,OAAO,yBAAyB,QAAQ;AAC9C,KAAI,KAAK,OAAO,MAAO,QAAO;EAAE,IAAI;EAAO,OAAO,KAAK;EAAO,QAAQ,KAAK;EAAQ;CACnF,MAAM,aAAqB;EACzB,GAAG;EACH,QAAQ;GACN,GAAG,IAAI;GACP,mBAAmB;GACpB;EACF;CACD,MAAM,YAAY,8BAA8B,WAAW;AAC3D,KAAI,UAAU,OAAO,MAAO,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU;EAAO,QAAQ,UAAU;EAAQ;AAClG,QAAO;EAAE,IAAI;EAAM,MAAM,EAAE,YAAY;EAAE;;AAG3C,SAAgB,sBAAsB,KAA4C;CAChF,MAAM,UAAU,OAAO,OAAO,UAAU,IAAI,CAAC,CAC1C,KAAK,YAAY;EAAE,GAAG;EAAQ,OAAO,WAAW,KAAK,OAAO,GAAG;EAAE,EAAE,CACnE,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC;CAC3C,MAAM,SAAS,IAAI,OAAO,KACvB,KAAK,WAAW;EACf,IAAI,MAAM;EACV,GAAI,MAAM,SAAS,OAAO,EAAE,MAAM,MAAM,SAAS,MAAM,GAAG,EAAE;EAC5D,SAAS,CAAC,GAAI,MAAM,WAAW,EAAE,CAAE;EACpC,EAAE,CACF,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC;AAC3C,QAAO;EAAE,iBAAiB,IAAI,OAAO;EAAe;EAAS;EAAQ,gBAAgB,CAAC,GAAG,yBAAyB;EAAE;;AAGtH,SAAgB,8BACd,KACA,MACuE;CACvE,MAAM,KAAK,kBAAkB,KAAK,MAAM,GAAG;CAC3C,MAAM,OAAO,KAAK,MAAM,MAAM,IAAI;CAClC,MAAM,cAAc,KAAK,aAAa,MAAM;CAC5C,MAAM,EAAE,IAAI,KAAK,MAAM,OAAO,aAAa,cAAc,GAAG,WAAW;CACvE,MAAM,SAAS,uBAAuB,UAAU;EAC9C,GAAG;EACH;EACA;EACA,SAAS,KAAK,WAAW;EACzB,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;EACvC,CAAC;AACF,KAAI,CAAC,OAAO,QACV,QAAO;EAAE,IAAI;EAAO,OAAO,OAAO,MAAM,OAAO,IAAI,WAAW;EAAgC,QAAQ;EAAK;CAE7G,MAAM,UAAU,UAAU,IAAI;AAC9B,KAAI,QAAQ,IACV,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,GAAG;EAAmB,QAAQ;EAAK;CAEtF,MAAM,OAAO,sBAAsB,KAAK;EAAE,GAAG;GAAU,KAAK,OAAO;EAAM,CAAC;AAC1E,KAAI,KAAK,OAAO,MAAO,QAAO;EAAE,IAAI;EAAO,OAAO,KAAK;EAAO,QAAQ,KAAK;EAAQ;AACnF,QAAO;EAAE,IAAI;EAAM,MAAM;GAAE,GAAG,KAAK;GAAM,UAAU;GAAI;EAAE;;AAG3D,SAAgB,8BACd,KACA,aACA,MACqD;CACrD,MAAM,WAAW,kBAAkB,YAAY;CAC/C,MAAM,UAAU,UAAU,IAAI;CAC9B,MAAM,UAAU,QAAQ;AACxB,KAAI,CAAC,QACH,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,SAAS;EAAc,QAAQ;EAAK;CAEvF,MAAM,aAA+B,EAAE,GAAG,SAAS;AAEnD,KAAI,KAAK,SAAS,KAAA,GAAW;EAC3B,MAAM,OAAO,KAAK,KAAK,MAAM;AAC7B,MAAI,CAAC,KACH,QAAO;GAAE,IAAI;GAAO,OAAO;GAAmC,QAAQ;GAAK;AAE7E,aAAW,OAAO;;AAEpB,KAAI,KAAK,gBAAgB,KAAA,EACvB,KAAI,KAAK,gBAAgB,QAAQ,CAAC,KAAK,YAAY,MAAM,CACvD,QAAO,WAAW;KAElB,YAAW,cAAc,KAAK,YAAY,MAAM;AAGpD,YAAW,UAAU,KAAK,WAAW,QAAQ,UAAU;AACvD,MAAK,MAAM,OAAO;EAAC;EAAW;EAAU;EAAS;EAAU;EAAU;EAAa;EAAc;EAAW;EAAQ,EAAW;AAC5H,MAAI,KAAK,SAAS,KAAA,EAAW;AAC7B,MAAI,KAAK,SAAS,KAChB,QAAO,WAAW;MAEjB,YAAuC,OAAO,KAAK;;CAIxD,MAAM,SAAS,uBAAuB,UAAU,WAAW;AAC3D,KAAI,CAAC,OAAO,QACV,QAAO;EAAE,IAAI;EAAO,OAAO,OAAO,MAAM,OAAO,IAAI,WAAW;EAAgC,QAAQ;EAAK;AAE7G,QAAO,sBAAsB,KAAK;EAAE,GAAG;GAAU,WAAW,OAAO;EAAM,CAAC;;AAG5E,SAAgB,8BACd,KACA,aACqD;CACrD,MAAM,WAAW,kBAAkB,YAAY;CAC/C,MAAM,UAAU,UAAU,IAAI;AAC9B,KAAI,CAAC,QAAQ,UACX,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,SAAS;EAAc,QAAQ;EAAK;AAEvF,KAAI,aAAa,IAAI,OAAO,cAC1B,QAAO;EAAE,IAAI;EAAO,OAAO,sBAAsB,SAAS;EAAgD,QAAQ;EAAK;CAEzH,MAAM,QAAQ,WAAW,KAAK,SAAS;AACvC,KAAI,MAAM,SAAS,EACjB,QAAO;EACL,IAAI;EACJ,OAAO,sBAAsB,SAAS,uBAAuB,MAAM,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK;EACnG,QAAQ;EACT;CAEH,MAAM,cAAc,EAAE,GAAG,SAAS;AAClC,QAAO,YAAY;AACnB,QAAO,sBAAsB,KAAK,YAAY"}