@xopcai/xopc 0.0.117 → 0.0.118

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 (277) hide show
  1. package/README.md +24 -23
  2. package/README.zh-CN.md +45 -42
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BBLhJ2C6.js → agent-avatar-dicebear-dzUrQoeZ.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-BCxZbkOw.js +2 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CBpLONvG.js → app-management-settings-panel-bxYJVJpx.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-CGcc54yj.js → appearance-settings-BCIykXlI.js} +1 -1
  9. package/dist/gateway/static/root/assets/{apps-page-DdDx0V4z.js → apps-page--wMEL00f.js} +1 -1
  10. package/dist/gateway/static/root/assets/{archive-plugin-DZsVqMhU.js → archive-plugin-Cki4SfU2.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CIol6JWX.js → attachment-load-D-KnS2Do.js} +1 -1
  12. package/dist/gateway/static/root/assets/{binary-plugins-B3UauMfp.js → binary-plugins-D2sgvJja.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-2HJoFchH.js → block-editor-DIq-4Gvb.js} +27 -27
  14. package/dist/gateway/static/root/assets/browser-settings-page-DRhKdn81.js +8 -0
  15. package/dist/gateway/static/root/assets/{capability-presets-api-XQoMTI0A.js → capability-presets-api-CorhiHlg.js} +1 -1
  16. package/dist/gateway/static/root/assets/capability-presets-settings-panel-qFx9Jtce.js +1 -0
  17. package/dist/gateway/static/root/assets/channels-settings-BAwAkqIA.js +1 -0
  18. package/dist/gateway/static/root/assets/connectors-page-BQLcfitM.js +2 -0
  19. package/dist/gateway/static/root/assets/{cron-api-R41aQCX9.js → cron-api-qGwmL-24.js} +1 -1
  20. package/dist/gateway/static/root/assets/cron-page-BBWMAj8-.js +3 -0
  21. package/dist/gateway/static/root/assets/{dreaming-settings-Bceaw1PG.js → dreaming-settings-Dw89Obml.js} +1 -1
  22. package/dist/gateway/static/root/assets/{extension-debug-page-DX5WRE_t.js → extension-debug-page-DYu1KaFQ.js} +1 -1
  23. package/dist/gateway/static/root/assets/{extension-page-Buah7Bw-.js → extension-page-B3W6QwlZ.js} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-settings-page-DuroLVnf.js → extension-settings-page-D7VQ-cxE.js} +1 -1
  25. package/dist/gateway/static/root/assets/{fetch-B5zdl27C.js → fetch-B7xDPu-R.js} +1 -1
  26. package/dist/gateway/static/root/assets/{format-interval-label-Dn_QKIqb.js → format-interval-label-BzxafKxR.js} +1 -1
  27. package/dist/gateway/static/root/assets/{gateway-config-api-gT4ZhE7h.js → gateway-config-api-BTBLSeUu.js} +1 -1
  28. package/dist/gateway/static/root/assets/{gateway-settings-iISWAZRm.js → gateway-settings-Cmwy58rS.js} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-startup-retry-B3kL8KGY.js → gateway-startup-retry-CYj9FWCp.js} +1 -1
  30. package/dist/gateway/static/root/assets/goal-detail-page-l7mu5GWX.js +1 -0
  31. package/dist/gateway/static/root/assets/{goals-config-api-AR-fVh0P.js → goals-config-api-CQQgzPr3.js} +1 -1
  32. package/dist/gateway/static/root/assets/{goals-page-Cpb21ezg.js → goals-page-BHitvJAv.js} +2 -2
  33. package/dist/gateway/static/root/assets/{goals-settings-CPAuFwoD.js → goals-settings-CYVY4NGp.js} +1 -1
  34. package/dist/gateway/static/root/assets/{heartbeat-config-api-D7bKFEeO.js → heartbeat-config-api-DriO_CN9.js} +1 -1
  35. package/dist/gateway/static/root/assets/heartbeat-settings-CUQTKxg7.js +1 -0
  36. package/dist/gateway/static/root/assets/index-C17Tsd_b.css +1 -0
  37. package/dist/gateway/static/root/assets/{index-CwhK8qCh.js → index-Cc41r16l.js} +120 -51
  38. package/dist/gateway/static/root/assets/{interval-presets-0DtsnxE0.js → interval-presets-DFPCKW_W.js} +1 -1
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BPREtN8B.js → keyboard-shortcuts-settings-CcVcqstx.js} +1 -1
  40. package/dist/gateway/static/root/assets/{logs-page-DefCgF4o.js → logs-page-C2Uqvae9.js} +2 -2
  41. package/dist/gateway/static/root/assets/{markdown-split-ChbR9LGO.js → markdown-split-BwOFrJ2E.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-B82oP80n.js → media-plugins-Cnz6Q5G6.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-B1iSKjiw.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-BfLfcta8.js +2 -0
  45. package/dist/gateway/static/root/assets/note-detail-page-BjwOQBfT.js +2 -0
  46. package/dist/gateway/static/root/assets/{note-image-lightbox-CDknITM3.js → note-image-lightbox-B-Svpmza.js} +1 -1
  47. package/dist/gateway/static/root/assets/notes-page-DktdBiwB.js +1 -0
  48. package/dist/gateway/static/root/assets/{preview-open-alternatives-DV3xQsWB.js → preview-open-alternatives-Bss4TiwP.js} +1 -1
  49. package/dist/gateway/static/root/assets/remote-access-hub-rfVx96fq.js +1 -0
  50. package/dist/gateway/static/root/assets/{save-bar-controls-BODCXnvY.js → save-bar-controls-BRNjdgKq.js} +1 -1
  51. package/dist/gateway/static/root/assets/{schedule-field-CgJSCD3B.js → schedule-field-CUCKODde.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schema-form-qtfEI6QM.js → schema-form-Dub9sy8r.js} +1 -1
  53. package/dist/gateway/static/root/assets/sessions-page-DbtMFuuH.js +1 -0
  54. package/dist/gateway/static/root/assets/settings-advanced-gate-5re5gAnP.js +1 -0
  55. package/dist/gateway/static/root/assets/{settings-collapsible-section-BHZKjVcS.js → settings-collapsible-section-BeFLavGi.js} +1 -1
  56. package/dist/gateway/static/root/assets/{settings-form-section-5i35D-J1.js → settings-form-section-dmyaPOTG.js} +1 -1
  57. package/dist/gateway/static/root/assets/settings-page-D-WMyLlj.js +1 -0
  58. package/dist/gateway/static/root/assets/setup-status-panel-Cl-DEJAU.js +1 -0
  59. package/dist/gateway/static/root/assets/{share-preview-page-B0H63tDv.js → share-preview-page-C8glrdqe.js} +1 -1
  60. package/dist/gateway/static/root/assets/shares-settings-CyPVvbLt.js +1 -0
  61. package/dist/gateway/static/root/assets/skill-reload-api-CleTH1oz.js +1 -0
  62. package/dist/gateway/static/root/assets/skills-page-Ck8hdXqR.js +2 -0
  63. package/dist/gateway/static/root/assets/{system-settings-panel-DXs2VmBt.js → system-settings-panel-DI-LN_8G.js} +1 -1
  64. package/dist/gateway/static/root/assets/{typed-models-lib-4gPuydz_.js → typed-models-lib-kX9duch4.js} +1 -1
  65. package/dist/gateway/static/root/assets/{use-channel-catalog-BCjP_VHb.js → use-channel-catalog-DqlWTsDb.js} +1 -1
  66. package/dist/gateway/static/root/assets/user-profile-settings-panel-B-UFPMXy.js +1 -0
  67. package/dist/gateway/static/root/assets/utils-Dm0hKalR.js +1 -0
  68. package/dist/gateway/static/root/assets/voice-api-key-field-C73IVZeC.js +1 -0
  69. package/dist/gateway/static/root/assets/{voice-player-store-BlF9V05n.js → voice-player-store-C_ArLJPM.js} +1 -1
  70. package/dist/gateway/static/root/assets/workflows-page-BtFGp2y_.js +18 -0
  71. package/dist/gateway/static/root/index.html +5 -5
  72. package/dist/package.js +1 -1
  73. package/dist/src/agent/agent-manager.js +2 -1
  74. package/dist/src/agent/agent-manager.js.map +1 -1
  75. package/dist/src/agent/agent-scope.js +0 -2
  76. package/dist/src/agent/agent-scope.js.map +1 -1
  77. package/dist/src/agent/background-review/prompts.d.ts +2 -2
  78. package/dist/src/agent/background-review/prompts.js +2 -2
  79. package/dist/src/agent/background-review/prompts.js.map +1 -1
  80. package/dist/src/agent/background-review/settings.d.ts +1 -1
  81. package/dist/src/agent/background-review/settings.js +7 -8
  82. package/dist/src/agent/background-review/settings.js.map +1 -1
  83. package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +1 -0
  84. package/dist/src/agent/bootstrap/bootstrap-cache.js +4 -3
  85. package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
  86. package/dist/src/agent/bootstrap/bootstrap-files.d.ts +4 -0
  87. package/dist/src/agent/bootstrap/bootstrap-files.js +4 -3
  88. package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
  89. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +4 -2
  90. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  91. package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +2 -1
  92. package/dist/src/agent/bootstrap/load-bootstrap-files.js +17 -16
  93. package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
  94. package/dist/src/agent/bootstrap/types.d.ts +1 -1
  95. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  96. package/dist/src/agent/context/workspace.d.ts +4 -2
  97. package/dist/src/agent/context/workspace.js +5 -4
  98. package/dist/src/agent/context/workspace.js.map +1 -1
  99. package/dist/src/agent/embedded/run-for-session.js +4 -1
  100. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  101. package/dist/src/agent/fallback/candidates.js +10 -11
  102. package/dist/src/agent/fallback/candidates.js.map +1 -1
  103. package/dist/src/agent/memory/builtin-memory-store.d.ts +4 -2
  104. package/dist/src/agent/memory/builtin-memory-store.js +11 -6
  105. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  106. package/dist/src/agent/memory/builtin-provider.js +36 -7
  107. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  108. package/dist/src/agent/memory/candidate-extractor.d.ts +6 -0
  109. package/dist/src/agent/memory/candidate-extractor.js +60 -0
  110. package/dist/src/agent/memory/candidate-extractor.js.map +1 -0
  111. package/dist/src/agent/memory/manager.d.ts +2 -1
  112. package/dist/src/agent/memory/manager.js +27 -3
  113. package/dist/src/agent/memory/manager.js.map +1 -1
  114. package/dist/src/agent/memory/memory-config.js +3 -0
  115. package/dist/src/agent/memory/memory-config.js.map +1 -1
  116. package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -1
  117. package/dist/src/agent/memory/types.d.ts +27 -4
  118. package/dist/src/agent/prompt/memory/index.d.ts +4 -2
  119. package/dist/src/agent/prompt/memory/index.js +18 -12
  120. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  121. package/dist/src/agent/prompt/sections/memory-skills.js +1 -1
  122. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
  123. package/dist/src/agent/prompt/service-prompt-builder.js +4 -19
  124. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  125. package/dist/src/agent/reply/post-compaction-context.js +1 -3
  126. package/dist/src/agent/reply/post-compaction-context.js.map +1 -1
  127. package/dist/src/agent/reply/startup-context.js +1 -5
  128. package/dist/src/agent/reply/startup-context.js.map +1 -1
  129. package/dist/src/agent/service.d.ts +3 -3
  130. package/dist/src/agent/service.js +7 -8
  131. package/dist/src/agent/service.js.map +1 -1
  132. package/dist/src/agent/tools/curated-memory-tool.js +57 -2
  133. package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
  134. package/dist/src/agent/tools/web-extract.js +6 -8
  135. package/dist/src/agent/tools/web-extract.js.map +1 -1
  136. package/dist/src/agent-manifest/index.js +2 -2
  137. package/dist/src/agent-manifest/resolver.d.ts +3 -2
  138. package/dist/src/agent-manifest/resolver.js +5 -3
  139. package/dist/src/agent-manifest/resolver.js.map +1 -1
  140. package/dist/src/agent-manifest/schema.d.ts +232 -0
  141. package/dist/src/agent-manifest/schema.js +4 -2
  142. package/dist/src/agent-manifest/schema.js.map +1 -1
  143. package/dist/src/agent-manifest/validator.d.ts +3 -2
  144. package/dist/src/agent-manifest/validator.js +2 -1
  145. package/dist/src/agent-manifest/validator.js.map +1 -1
  146. package/dist/src/channels/pipeline.d.ts +1 -1
  147. package/dist/src/channels/pipeline.js.map +1 -1
  148. package/dist/src/chat-commands/agent-edit.js +0 -1
  149. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  150. package/dist/src/chat-commands/builtins/model.js +2 -2
  151. package/dist/src/chat-commands/builtins/model.js.map +1 -1
  152. package/dist/src/cli/command-manifest.d.ts +1 -1
  153. package/dist/src/cli/command-manifest.js +1 -1
  154. package/dist/src/cli/command-manifest.js.map +1 -1
  155. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  156. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -0
  157. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -1
  158. package/dist/src/cli/commands/doctor/checks/workspace-status.js +0 -1
  159. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
  160. package/dist/src/cli/commands/init.js +7 -6
  161. package/dist/src/cli/commands/init.js.map +1 -1
  162. package/dist/src/cli/commands/models.js +13 -31
  163. package/dist/src/cli/commands/models.js.map +1 -1
  164. package/dist/src/cli/commands/onboard/model.js +17 -10
  165. package/dist/src/cli/commands/onboard/model.js.map +1 -1
  166. package/dist/src/cli/commands/onboard.js +2 -2
  167. package/dist/src/cli/commands/onboard.js.map +1 -1
  168. package/dist/src/cli/commands/tui.js +2 -2
  169. package/dist/src/cli/commands/tui.js.map +1 -1
  170. package/dist/src/cli/index.d.ts +1 -0
  171. package/dist/src/cli/index.js +10 -5
  172. package/dist/src/cli/index.js.map +1 -1
  173. package/dist/src/cli/registry.js +1 -1
  174. package/dist/src/cli/registry.js.map +1 -1
  175. package/dist/src/cli/templates.d.ts +1 -1
  176. package/dist/src/cli/templates.js +1 -11
  177. package/dist/src/cli/templates.js.map +1 -1
  178. package/dist/src/commands/agents.config.js +2 -10
  179. package/dist/src/commands/agents.config.js.map +1 -1
  180. package/dist/src/config/agent-profile.js +3 -2
  181. package/dist/src/config/agent-profile.js.map +1 -1
  182. package/dist/src/config/agent-typed-models.js +3 -5
  183. package/dist/src/config/agent-typed-models.js.map +1 -1
  184. package/dist/src/config/index.js +2 -2
  185. package/dist/src/config/paths.d.ts +8 -1
  186. package/dist/src/config/paths.js +16 -3
  187. package/dist/src/config/paths.js.map +1 -1
  188. package/dist/src/config/schema.d.ts +32 -32
  189. package/dist/src/config/schema.js +46 -26
  190. package/dist/src/config/schema.js.map +1 -1
  191. package/dist/src/gateway/agents-admin.d.ts +7 -0
  192. package/dist/src/gateway/agents-admin.js +51 -20
  193. package/dist/src/gateway/agents-admin.js.map +1 -1
  194. package/dist/src/gateway/chat-stream/mapper.js +9 -2
  195. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  196. package/dist/src/gateway/global-defaults-admin.d.ts +36 -0
  197. package/dist/src/gateway/global-defaults-admin.js +146 -0
  198. package/dist/src/gateway/global-defaults-admin.js.map +1 -0
  199. package/dist/src/gateway/hono/routes/agents.js +33 -1
  200. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  201. package/dist/src/gateway/hono/routes/commands-skills.js +4 -5
  202. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  203. package/dist/src/gateway/hono/routes/global-defaults.d.ts +3 -0
  204. package/dist/src/gateway/hono/routes/global-defaults.js +61 -0
  205. package/dist/src/gateway/hono/routes/global-defaults.js.map +1 -0
  206. package/dist/src/gateway/hono/routes/lazy-bundles.js +11 -3
  207. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  208. package/dist/src/gateway/hono/routes/memory.js +75 -4
  209. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  210. package/dist/src/gateway/hono/routes/models.js +1 -1
  211. package/dist/src/gateway/service/marketplace-service.d.ts +4 -4
  212. package/dist/src/gateway/service/marketplace-service.js +11 -7
  213. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  214. package/dist/src/notes/service.js +3 -1
  215. package/dist/src/notes/service.js.map +1 -1
  216. package/dist/src/providers/index.d.ts +4 -5
  217. package/dist/src/providers/index.js +16 -16
  218. package/dist/src/providers/index.js.map +1 -1
  219. package/dist/src/session/session-workspace.js +2 -7
  220. package/dist/src/session/session-workspace.js.map +1 -1
  221. package/dist/src/session/store.js +0 -5
  222. package/dist/src/session/store.js.map +1 -1
  223. package/dist/src/storage/sqlite/index.d.ts +1 -1
  224. package/dist/src/storage/sqlite/index.js +2 -2
  225. package/dist/src/storage/sqlite/memory-index-repository.d.ts +1 -0
  226. package/dist/src/storage/sqlite/memory-index-repository.js +15 -14
  227. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
  228. package/dist/src/storage/sqlite/memory-records-repository.d.ts +44 -1
  229. package/dist/src/storage/sqlite/memory-records-repository.js +164 -20
  230. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  231. package/dist/src/storage/sqlite/migrations/014_memory_record_governance.sql +10 -0
  232. package/dist/src/storage/sqlite/migrations/015_memory_trace_feedback.sql +4 -0
  233. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  234. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  235. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  236. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  237. package/dist/src/storage/sqlite/schema.js +1 -1
  238. package/dist/src/tui/backends/embedded-backend.d.ts +3 -0
  239. package/dist/src/tui/backends/embedded-backend.js +37 -20
  240. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  241. package/dist/src/tui/backends/gateway-sse-backend.d.ts +1 -0
  242. package/dist/src/tui/backends/gateway-sse-backend.js +16 -3
  243. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  244. package/dist/src/tui/components/tui-header.d.ts +0 -1
  245. package/dist/src/tui/components/tui-header.js +3 -8
  246. package/dist/src/tui/components/tui-header.js.map +1 -1
  247. package/dist/src/tui/tui-startup-resources.js +2 -1
  248. package/dist/src/tui/tui-startup-resources.js.map +1 -1
  249. package/dist/src/tui/tui-startup-text.js +12 -12
  250. package/dist/src/tui/tui-startup-text.js.map +1 -1
  251. package/dist/src/tui/tui.js +4 -1
  252. package/dist/src/tui/tui.js.map +1 -1
  253. package/package.json +1 -1
  254. package/dist/gateway/static/root/assets/agents-C6xgagZd.js +0 -71
  255. package/dist/gateway/static/root/assets/browser-settings-page-CUOO-PQk.js +0 -8
  256. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CfqF-kGr.js +0 -1
  257. package/dist/gateway/static/root/assets/channels-settings-BYyRzsex.js +0 -1
  258. package/dist/gateway/static/root/assets/connectors-page-DIvEz4NX.js +0 -2
  259. package/dist/gateway/static/root/assets/cron-page-D97nFqQD.js +0 -3
  260. package/dist/gateway/static/root/assets/goal-detail-page-DCUROLMQ.js +0 -1
  261. package/dist/gateway/static/root/assets/heartbeat-settings-CC69vNx0.js +0 -1
  262. package/dist/gateway/static/root/assets/index-CyfhARL-.css +0 -1
  263. package/dist/gateway/static/root/assets/messages-Cf1O9JB0.js +0 -3
  264. package/dist/gateway/static/root/assets/models-hub-panel-DmKiIihT.js +0 -2
  265. package/dist/gateway/static/root/assets/note-detail-page-CxxfLRJZ.js +0 -2
  266. package/dist/gateway/static/root/assets/notes-page-X2slSx7V.js +0 -1
  267. package/dist/gateway/static/root/assets/remote-access-hub-BvmUWqgu.js +0 -1
  268. package/dist/gateway/static/root/assets/sessions-page-B0yESuuL.js +0 -1
  269. package/dist/gateway/static/root/assets/settings-advanced-gate-q2M8RKOo.js +0 -1
  270. package/dist/gateway/static/root/assets/settings-page-CGozabLD.js +0 -1
  271. package/dist/gateway/static/root/assets/setup-status-panel-FoOSe6nU.js +0 -1
  272. package/dist/gateway/static/root/assets/shares-settings-Cy5zWyYQ.js +0 -1
  273. package/dist/gateway/static/root/assets/skill-reload-api-DaZNFro9.js +0 -1
  274. package/dist/gateway/static/root/assets/skills-page-CnwtUzvw.js +0 -2
  275. package/dist/gateway/static/root/assets/voice-api-key-field-DvKiOglO.js +0 -1
  276. package/dist/gateway/static/root/assets/workflows-page-CAVZcONQ.js +0 -18
  277. package/dist/src/agent/context/workspace-templates/USER.md +0 -24
@@ -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 { join, resolve as pathResolve } from 'node:path';\n\nimport {\n DEFAULT_AGENT_ID,\n listAgentEntries,\n normalizeAgentId,\n resolveAgentDir,\n resolveAgentProfileDir,\n resolveAgentWorkspaceDir,\n resolveDefaultAgentId,\n resolveUserPath,\n validateAgentIdForNewAgent,\n} from '../agent/agent-scope.js';\nimport { AGENT_PROFILE_MARKDOWN_SYSTEM_FILES } 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 pruneAgentConfig,\n removeAgentDirsFromDisk,\n} from '../commands/agents.config.js';\nimport type { AgentModelsConfig, Config } from '../config/schema.js';\nimport { 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 /** 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; 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 ...(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 }),\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 /** 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\nfunction requireNonMain(id: string): AgentAdminResult<never> | null {\n if (normalizeAgentId(id) === DEFAULT_AGENT_ID) {\n return { ok: false, error: `\"${DEFAULT_AGENT_ID}\" is reserved`, status: 400 };\n }\n return null;\n}\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 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 ...(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; 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 models: { defaultRole: 'deep', roles: { deep: { model: 'openai/gpt-4.1' } } },\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 return { ok: false, error: 'models cannot be null; every agent manifest needs model roles', status: 400 };\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 = { ...entry.models, defaultRole: body.models.defaultRole.trim() };\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 description: row.description?.trim(),\n }))\n .filter((row) => row.id && row.model)\n .map((row) => [\n row.id,\n row.description ? { model: row.model, description: row.description } : { model: row.model },\n ]),\n );\n entry.models = { ...entry.models, roles };\n }\n }\n if (!entry.models.roles[entry.models.defaultRole]) {\n return { ok: false, error: 'models.defaultRole must reference models.roles', status: 400 };\n }\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 reserved = requireNonMain(agentId);\n if (reserved) {\n return reserved;\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 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\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 DEFAULT_AGENT_ID,\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 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 /** 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; 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 ...(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 /** 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\nfunction requireNonMain(id: string): AgentAdminResult<never> | null {\n if (normalizeAgentId(id) === DEFAULT_AGENT_ID) {\n return { ok: false, error: `\"${DEFAULT_AGENT_ID}\" is reserved`, status: 400 };\n }\n return null;\n}\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 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 ...(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; 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 description: row.description?.trim(),\n }))\n .filter((row) => row.id && row.model)\n .map((row) => [\n row.id,\n row.description ? { model: row.model, description: row.description } : { model: row.model },\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 reserved = requireNonMain(agentId);\n if (reserved) {\n return reserved;\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":";;;;;;;;;;;;;;;;kBAiBiC;YAiB4C;AAO7E,MAAM,kCAAkC,IAAI,IAAY,CAAC,GAAG,oCAAoC,CAAC;AA2CjG,SAAS,uBAAuB,KAAuB;CACrD,MAAM,UAAU,iBAAiB,IAAI,CAAC,QAAQ,MAAM,EAAE,YAAY,MAAM;CACxE,MAAM,YAAY,sBAAsB,IAAI;AAC5C,KAAI,QAAQ,WAAW,EACrB,QAAO,CAAC,UAAU;CAEpB,MAAM,sBAAM,IAAI,KAAa;AAC7B,MAAK,MAAM,KAAK,QACd,KAAI,IAAI,iBAAiB,EAAE,GAAG,CAAC;AAEjC,KAAI,IAAI,UAAU;AAClB,QAAO,CAAC,GAAG,IAAI;;AAGjB,SAAS,mBACP,OACmB;AACnB,QAAO,OAAO,QAAQ,SAAS,EAAE,CAAC,CAC/B,KAAK,CAAC,IAAI,WAAW;EAAE;EAAI,GAAG;EAAM,EAAE,CACtC,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC;;;AAI7C,SAAgB,kCAAkC,SAAqC;AACrF,QAAO,sBAAsB,QAAQ,CAAC,UAAU,KAAA;;AAGlD,SAAgB,sBAAsB,SAKpC;CACA,MAAM,MAAmF,EAAE;AAC3F,MAAK,MAAM,QAAQ,QAAQ,MAAM,KAAK,EAAE;EACtC,MAAM,QAAQ,KAAK,MAAM,8DAA8D;AACvF,MAAI,OAAO;GACT,MAAM,MAAM,MAAM,IAAI,aAAa;GACnC,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI;AAC9B,OAAI,CAAC,KAAK,aAAa,KAAK,EAAE,CAAE;AAChC,OAAI,QAAQ,OAAQ,KAAI,OAAO;AAC/B,OAAI,QAAQ,cAAe,KAAI,cAAc;AAC7C,OAAI,QAAQ,WAAY,KAAI,WAAW;AACvC,OAAI,QAAQ,SAAU,KAAI,SAAS;;;AAGvC,QAAO;;AAGT,eAAsB,kBACpB,KACA,WAAgC,EAAE,EACE;CACpC,MAAM,YAAY,sBAAsB,IAAI;CAC5C,MAAM,SAA4B,EAAE;CAEpC,MAAM,eAAyB,EAAE;CACjC,MAAM,oBAA2D,EAAE;AACnE,MAAK,MAAM,MAAM,uBAAuB,IAAI,EAAE;EAC5C,MAAM,UAAU,6BAA6B,KAAK,GAAG;EACrD,MAAM,QAAQ,iBAAiB,IAAI,CAAC,MAAM,MAAM,iBAAiB,EAAE,GAAG,KAAK,GAAG;EAC9E,MAAM,QACJ,QAAQ,mBAAmB,QAAQ,UAAU,SAAS,IAClD;GACE,GAAI,QAAQ,kBAAkB,EAAE,SAAS,QAAQ,iBAAiB,GAAG,EAAE;GACvE,GAAI,QAAQ,UAAU,SAAS,IAAI,EAAE,WAAW,QAAQ,WAAW,GAAG,EAAE;GACzE,GACD,KAAA;EACN,MAAM,cAAc,OAAO,OAAO,SAAS,cAAc,MAAM,OAAO,SAAS,EAAE,GAAG,KAAA;EACpF,MAAM,eAAe,OAAO,QAAQ,OAAO,MAAM,WAAW,EAAE,CAAC,CAC5D,QAAQ,GAAG,YAAY,OAAO,SAAS,OAAO,CAC9C,KAAK,CAAC,UAAU,KAAK;EACxB,MAAM,mBAAmB,mBAAmB,OAAO,QAAQ,MAAM;EACjE,MAAM,uBAAuB,CAAC,GAAG,4BAA4B,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,MACvF,EAAE,GAAG,cAAc,EAAE,GAAG,CACzB;EACD,IAAI,WAAqD,EAAE;AAC3D,MAAI;AAGF,cAAW,sBAAsB,MADX,SADD,KAAK,uBAAuB,KAAK,GAAG,EAAE,gBAAgB,SAChC,EAAE,QAAQ,CACZ;UACnC;AAGR,SAAO,KAAK;GACV;GACA,GAAI,SAAS,OAAO,EAAE,MAAM,SAAS,MAAM,GAAG,EAAE;GAChD,GAAI,SAAS,cAAc,EAAE,aAAa,SAAS,aAAa,GAAG,EAAE;GACrE,GAAI,SAAS,WAAW,EAAE,UAAU,SAAS,UAAU,GAAG,EAAE;GAC5D,GAAI,SAAS,SAAS,EAAE,QAAQ,SAAS,QAAQ,GAAG,EAAE;GACtD,WAAW,QAAQ;GACnB,YAAY,uBAAuB,KAAK,GAAG;GAC3C,GAAI,QAAQ,EAAE,OAAO,GAAG,EAAE;GAC1B,aAAa;IACX,aAAa,QAAQ,SAAS,OAAO;IACrC,QAAQ,CAAC,GAAG,kBAAkB;IAC9B,GAAI,iBAAiB,SAAS,IAAI,EAAE,OAAO,kBAAkB,GAAG,EAAE;IAClE,WAAW;IACZ;GACD,SAAS,CAAC,GAAI,OAAO,WAAW,EAAE,CAAE;GACpC,WAAW,OAAO;GAClB,QAAQ;IACN,QAA2C,EAAE;IAC7C,GAAI,gBAAgB,KAAA,IAAY,EAAE,OAAO,CAAC,GAAG,YAAY,EAAE,GAAG,EAAE;IAChE,GAAI,QAAQ,oBAAoB,KAAA,IAC5B,EAAE,oBAAoB,CAAC,GAAG,QAAQ,gBAAgB,EAAE,GACpD,EAAE;IACP;GACD,OAAO;IACL,cAAc,CAAC,GAAG,aAAa;IAC/B,cAAc,CAAC,GAAG,aAAa;IAC/B,kBAAkB,CAAC,GAAG,QAAQ,MAAM,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;IAC/E;GACF,CAAC;;AAEJ,QAAO,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC;AAC/C,QAAO;EAAE;EAAW;EAAQ,gBAAgB,CAAC,GAAG,yBAAyB;EAAE;;AAG7E,SAAgB,iCACd,KACA,YACyD;CACzD,MAAM,UAAU,iBAAiB,WAAW;CAC5C,MAAM,QAAQ,iBAAiB,IAAI,CAAC,MAAM,MAAM,iBAAiB,EAAE,GAAG,KAAK,QAAQ;AACnF,KAAI,CAAC,MACH,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,QAAQ;EAAc,QAAQ;EAAK;AAE1E,KAAI;AACF,SAAO;GACL,IAAI;GACJ,MAAM,8BAA8B;IAClC,OAAO;IACP,SAAS,IAAI,OAAO;IACpB,iBAAiB,IAAI,OAAO;IAC7B,CAAC;GACH;UACM,KAAK;AACZ,SAAO;GACL,IAAI;GACJ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACvD,QAAQ;GACT;;;AAyBL,SAAS,eAAe,IAA4C;AAClE,KAAI,iBAAiB,GAAG,KAAA,OACtB,QAAO;EAAE,IAAI;EAAO,OAAO,IAAI,iBAAiB;EAAgB,QAAQ;EAAK;AAE/E,QAAO;;AAGT,SAAgB,mBACd,KACA,MAC8E;CAC9E,MAAM,YAAY,KAAK,WAAW,MAAM,IAAI;AAC5C,KAAI,CAAC,UACH,QAAO;EAAE,IAAI;EAAO,OAAO;EAAyB,QAAQ;EAAK;AAEnE,KAAI,KAAK,iBAAiB,KAAA,GAAW;AACnC,MAAI,OAAO,KAAK,iBAAiB,YAAY,KAAK,iBAAiB,QAAQ,MAAM,QAAQ,KAAK,aAAa,CACzG,QAAO;GAAE,IAAI;GAAO,OAAO;GAAkC,QAAQ;GAAK;AAE5E,OAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,KAAK,aAAa,EAAE;GAC/D,MAAM,MAAM,kBAAkB,KAAK;AACnC,OAAI,IACF,QAAO;AAET,OAAI,OAAO,YAAY,SACrB,QAAO;IAAE,IAAI;IAAO,OAAO,iBAAiB,KAAK;IAAsB,QAAQ;IAAK;;;AAI1F,KAAI,CAAC,KAAK,aAAa,OAAO,KAAK,eAAe,gBAAgB,cAAc,SAC9E,QAAO;EAAE,IAAI;EAAO,OAAO,iBAAiB,gBAAgB,SAAS;EAAiB,QAAQ;EAAK;CAMrG,MAAM,YAHW,KAAK,eAAe,gBAAgB,YACjD,sBAAsB,KAAK,aAAa,gBAAgB,UAAU,GAClE,EAAE,EACoB,QAAQ,KAAK,MAAM;CAC7C,MAAM,QAAQ,2BAA2B,KAAK,IAAI,SAAS;AAC3D,KAAI,MAAM,OAAO,MACf,QAAO;EAAE,IAAI;EAAO,OAAO,MAAM;EAAO,QAAQ;EAAK;CAEvD,MAAM,UAAU,MAAM;AACtB,KAAI,oBAAoB,iBAAiB,IAAI,EAAE,QAAQ,IAAI,EACzD,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,QAAQ;EAAmB,QAAQ;EAAK;CAI/E,IAAI;AACJ,KAAI,KAAK,WAAW;EAClB,MAAM,QAAQ,iBAAiB,KAAK,UAAU;AAC9C,qBAAmB,iBAAiB,IAAI,CAAC,MAAM,MAAM,iBAAiB,EAAE,GAAG,KAAK,MAAM;AACtF,MAAI,CAAC,oBAAoB,UAAU,sBAAsB,IAAI,CAC3D,QAAO;GAAE,IAAI;GAAO,OAAO,iBAAiB,MAAM;GAAc,QAAQ;GAAK;;CAIjF,MAAM,QAAQ,gBAAgB,UAAU;CAExC,MAAM,YAAY,KAAK,UAAU,kBAAkB;CACnD,MAAM,kBACJ,WAAW,QACP;EACE,aAAa,UAAU,eAAe,OAAO,KAAK,UAAU,MAAM,CAAC,MAAM;EACzE,OAAO,UAAU;EAClB,GACD,KAAA;AACN,KAAI,mBAAmB,CAAC,gBAAgB,MAAM,gBAAgB,aAC5D,QAAO;EAAE,IAAI;EAAO,OAAO;EAAkD,QAAQ;EAAK;AAe5F,QAAO;EAAE,IAAI;EAAM,MAAM;GAAE,YAZhB,iBAAiB,KAAK;IAC/B;IACA,WAAW;IACX,GAAI,kBAAkB,EAAE,QAAQ,iBAAiB,GAAG,EAAE;IACtD,GAAI,KAAK,WAAW,KAAA,IAChB,EAAE,QAAQ,KAAK,OAAO,KAAK,MAAM,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,QAAQ,EAAE,GACpE,kBAAkB,OAAO,SAAS,eAAe,KAAK,YACpD,EAAE,QAAQ,CAAC,GAAI,iBAAiB,OAAO,SAAS,EAAE,CAAE,EAAE,GACtD,EAAE;IACR,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,OAAO,GAAG,kBAAkB,SAAS,KAAK,YAAY,EAAE,OAAO,iBAAiB,OAAO,GAAG,EAAE;IAC1I,CAE0C;GAAE;GAAS,WAAW;GAAO;EAAE;;AAG5E,eAAsB,wBACpB,KACA,SACA,MACiC;CACjC,MAAM,SAAS,yBAAyB,KAAK,QAAQ;CACrD,MAAM,cAAc,uBAAuB,KAAK,QAAQ;CACxD,MAAM,SAAS,gBAAgB,KAAK,QAAQ;AAC5C,OAAM,MAAM,QAAQ,EAAE,WAAW,MAAM,CAAC;AACxC,OAAM,MAAM,aAAa,EAAE,WAAW,MAAM,CAAC;AAC7C,OAAM,MAAM,QAAQ,EAAE,WAAW,MAAM,CAAC;CACxC,MAAM,KAAK,iBAAiB,QAAQ;CACpC,MAAM,cAAc,MAAM,eAAe,gBAAgB,YACpD,sBAAsB,KAAK,aAAa,gBAAgB,UAAU,CAAC,QAAQ,KAC5E;AAGJ,KAAI,MAAM,WAAW;EACnB,MAAM,iBAAiB,uBAAuB,KAAK,iBAAiB,KAAK,UAAU,CAAC;AACpF,MAAI;AAEF,QAAI,MADkB,KAAK,eAAe,EAC9B,aAAa,EAAE;IACzB,MAAM,WAAW,MAAM,QAAQ,eAAe;AAC9C,SAAK,MAAM,YAAY,UAAU;KAC/B,MAAM,UAAU,KAAK,gBAAgB,SAAS;KAC9C,MAAM,UAAU,KAAK,aAAa,SAAS;AAC3C,SAAI;AAEF,WAAI,MADmB,KAAK,QAAQ,EACvB,QAAQ,CACnB,OAAM,GAAG,SAAS,QAAQ;aAEtB;;;UAKN;AAEN,iCAA8B,aAAa,QAAQ,EAAE,aAAa,CAAC;;OAGrE,+BAA8B,aAAa,QAAQ,EAAE,aAAa,CAAC;CAGrE,MAAM,eAAe,MAAM;AAC3B,KAAI,gBAAgB,OAAO,KAAK,aAAa,CAAC,SAAS,EACrD,MAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,aAAa,EAAE;EAC1D,MAAM,UAAU,MAAM,sBAAsB,KAAK,SAAS,MAAM,QAAQ;AACxE,MAAI,QAAQ,OAAO,MACjB,QAAO;;AAKb,QAAO;EAAE,IAAI;EAAM,MAAM,KAAA;EAAW;;AAmBtC,SAAgB,mBACd,KACA,YACA,MAC0C;CAC1C,MAAM,UAAU,iBAAiB,WAAW;CAC5C,IAAI,OAAO,CAAC,GAAG,iBAAiB,IAAI,CAAC;CACrC,IAAI,MAAM,oBAAoB,MAAM,QAAQ;AAC5C,KAAI,MAAM,KAAK,YAAY,sBAAsB,IAAI,EAAE;AACrD,SAAO,CAAC,GAAG,MAAM;GACf,IAAI;GACJ,SAAS;GACT,UAAU;IAAE,MAAM;IAAS,MAAM;IAAS,UAAU;IAAM,MAAM;IAAU;GAC1E,kBAAkB,EAAE,SAAS,CAAC,+BAA+B,EAAE;GAC/D,WAAW,EAAE,MAAM,qBAAqB,WAAW;GACnD,OAAO,EAAE,SAAS,EAAE,EAAE;GACtB,QAAQ,EAAE,MAAM,OAAO;GACvB,QAAQ;IAAE,MAAM;IAAgB,SAAS,CAAC,WAAW,UAAU;IAAE,aAAa,EAAE,SAAS,WAAW;IAAE;GACtG,WAAW,EAAE;GACb,YAAY;IAAE,sBAAsB,EAAE;IAAE,WAAW,EAAE;IAAE,YAAY,EAAE;IAAE;GACxE,CAAC;AACF,QAAM,KAAK,SAAS;;AAEtB,KAAI,MAAM,EACR,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,QAAQ;EAAc,QAAQ;EAAK;CAI1E,MAAM,QAAe,EAAE,GAAG,KAAK,MAAM;AAErC,KAAI,KAAK,cAAc,KAAA,GAAW;EAChC,MAAM,IAAI,KAAK,UAAU,MAAM;AAC/B,MAAI,EACF,OAAM,YAAY,EAAE,MAAM,gBAAgB,EAAE,EAAE;;AAGlD,KAAI,KAAK,YAAY,KAAA,GAAW;EAC9B,MAAM,cAAc,MAAM,KAAK,IAAI,IAAI,KAAK,QAAQ,KAAK,OAAO,iBAAiB,GAAG,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC;AACvG,OAAK,MAAM,YAAY,YACrB,KAAI,CAAC,IAAI,OAAO,kBAAkB,UAChC,QAAO;GAAE,IAAI;GAAO,OAAO,sBAAsB,SAAS;GAAc,QAAQ;GAAK;AAGzF,MAAI,YAAY,SAAS,EACvB,OAAM,UAAU;MAEhB,QAAO,MAAM;;AAGjB,KAAI,KAAK,WAAW,KAAA,EAClB,KAAI,KAAK,WAAW,KAClB,QAAO,MAAM;MACR;AACL,MAAI,KAAK,OAAO,gBAAgB,KAAA,GAAW;AACzC,OAAI,KAAK,OAAO,gBAAgB,QAAQ,CAAC,KAAK,OAAO,YAAY,MAAM,CACrE,QAAO;IAAE,IAAI;IAAO,OAAO;IAAiD,QAAQ;IAAK;AAE3F,SAAM,SAAS;IACb,aAAa,KAAK,OAAO,YAAY,MAAM;IAC3C,OAAO,MAAM,QAAQ,SAAS,EAAE;IAChC,GAAI,MAAM,QAAQ,SAAS,EAAE,QAAQ,MAAM,OAAO,QAAQ,GAAG,EAAE;IAChE;;AAEH,MAAI,OAAO,OAAO,KAAK,QAAQ,QAAQ;OACjC,KAAK,OAAO,UAAU,KAAA,GAAW;IACnC,MAAM,QAAQ,OAAO,YACnB,OAAO,QAAQ,KAAK,OAAO,MAAM,CAC9B,KAAK,CAAC,IAAI,UAAU;KACnB,IAAI,GAAG,MAAM;KACb,OAAO,IAAI,MAAM,MAAM;KACvB,aAAa,IAAI,aAAa,MAAM;KACrC,EAAE,CACF,QAAQ,QAAQ,IAAI,MAAM,IAAI,MAAM,CACpC,KAAK,QAAQ,CACZ,IAAI,IACJ,IAAI,cAAc;KAAE,OAAO,IAAI;KAAO,aAAa,IAAI;KAAa,GAAG,EAAE,OAAO,IAAI,OAAO,CAC5F,CAAC,CACL;AACD,UAAM,SAAS;KACb,aAAa,MAAM,QAAQ,eAAe,OAAO,KAAK,MAAM,CAAC,MAAM;KACnE;KACA,GAAI,MAAM,QAAQ,SAAS,EAAE,QAAQ,MAAM,OAAO,QAAQ,GAAG,EAAE;KAChE;;;EAGL,MAAM,QAAQ,MAAM,QAAQ,SAAS,EAAE;EACvC,MAAM,cAAc,MAAM,QAAQ,eAAe,OAAO,KAAK,MAAM,CAAC;AACpE,MAAI,CAAC,eAAe,CAAC,MAAM,aACzB,QAAO;GAAE,IAAI;GAAO,OAAO;GAAkD,QAAQ;GAAK;AAE5F,QAAM,SAAS;GAAE,GAAG,MAAM;GAAQ;GAAa;;AAInD,KAAI,KAAK,WAAW,KAAA,EAClB,KAAI,KAAK,WAAW,KAClB,OAAM,SAAS,EAAE,MAAM,OAAO;MACzB;EACL,MAAM,OAAO,KAAK,OAAO,KAAK,MAAM,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,QAAQ;AACrE,MAAI,KAAK,WAAW,EAClB,OAAM,SAAS;GAAE,MAAM;GAAa,OAAO,EAAE;GAAE;MAE/C,OAAM,SAAS;GAAE,MAAM;GAAa,OAAO;GAAM;;AAKvD,KAAI,KAAK,UAAU,KAAA,EACjB,KAAI,KAAK,UAAU,KACjB,OAAM,QAAQ,EAAE,SAAS,EAAE,EAAE;KAE7B,OAAM,QAAQ,KAAK;AAIvB,KAAI,KAAK,WAAW,KAAA,EAClB,OAAM,SAAS,KAAK;AAGtB,MAAK,OAAO;CACZ,IAAI,OAAe;EACjB,GAAG;EACH,QAAQ;GACN,GAAG,IAAI;GACP;GACD;EACF;AAED,KAAI,KAAK,eAAe,KACtB,QAAO;EACL,GAAG;EACH,QAAQ;GACN,GAAG,KAAK;GACR,SAAS;GACV;EACF;AAEH,QAAO;EAAE,IAAI;EAAM,MAAM,EAAE,YAAY,MAAM;EAAE;;AAGjD,SAAgB,mBACd,KACA,YAC2D;CAC3D,MAAM,UAAU,iBAAiB,WAAW;CAC5C,MAAM,WAAW,eAAe,QAAQ;AACxC,KAAI,SACF,QAAO;AAET,KAAI,oBAAoB,iBAAiB,IAAI,EAAE,QAAQ,GAAG,EACxD,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,QAAQ;EAAc,QAAQ;EAAK;CAE1E,MAAM,EAAE,QAAQ,WAAW,iBAAiB,KAAK,QAAQ;AACzD,QAAO;EAAE,IAAI;EAAM,MAAM;GAAE,YAAY;GAAQ;GAAS;EAAE;;AAG5D,eAAsB,oBAAoB,KAAa,SAAgC;AACrF,OAAM,wBAAwB,KAAK,QAAQ;;AAU7C,eAAe,wBAAwB,KAAa,SAAkC;CACpF,MAAM,MAAM,uBAAuB,KAAK,QAAQ;AAChD,OAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AACrC,KAAI;AACF,SAAO,MAAM,SAAS,IAAI;SACpB;AACN,SAAOA,QAAY,IAAI;;;AAI3B,SAAS,kBAAkB,MAA8C;AACvE,KAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,KAAK,IAAI,CAAC,gCAAgC,IAAI,KAAK,CAClG,QAAO;EAAE,IAAI;EAAO,OAAO,qBAAqB,KAAK;EAAI,QAAQ;EAAK;AAExE,QAAO;;AAGT,eAAsB,sBACpB,KACA,SAC6F;CAC7F,MAAM,KAAK,iBAAiB,QAAQ;AACpC,KAAI,uBAAuB,IAAI,CAAC,OAAO,MAAM,MAAM,GAAG,CACpD,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,GAAG;EAAc,QAAQ;EAAK;CAErE,MAAM,OAAO,MAAM,wBAAwB,KAAK,GAAG;CACnD,MAAM,QAAQ,CAAC,GAAG,gCAAgC;CAClD,MAAM,QAA0B,EAAE;AAClC,MAAK,MAAM,QAAQ,MAAM,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE;EAC3D,MAAM,MAAM,yBAAyB,MAAM,KAAK;AAChD,MAAI,CAAC,IACH;AAEF,MAAI;GACF,MAAM,KAAK,MAAM,KAAK,IAAI;AAC1B,OAAI,CAAC,GAAG,QAAQ,CACd;AAEF,SAAM,KAAK;IACT;IACA,SAAS;IACT,MAAM,GAAG;IACT,aAAa,GAAG;IACjB,CAAC;UACI;AACN,OAAI,CAAC,yCAAyC,IAAI,KAAK,CACrD;AAEF,SAAM,KAAK;IAAE;IAAM,SAAS;IAAM,CAAC;;;AAGvC,OAAM,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;AAClD,QAAO;EAAE,IAAI;EAAM,MAAM;GAAE,SAAS;GAAI,YAAY;GAAM;GAAO;EAAE;;AAGrE,eAAsB,qBACpB,KACA,SACA,MAC+E;CAC/E,MAAM,MAAM,kBAAkB,KAAK;AACnC,KAAI,IACF,QAAO;CAET,MAAM,KAAK,iBAAiB,QAAQ;AACpC,KAAI,uBAAuB,IAAI,CAAC,OAAO,MAAM,MAAM,GAAG,CACpD,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,GAAG;EAAc,QAAQ;EAAK;CAGrE,MAAM,MAAM,yBAAyB,MADlB,wBAAwB,KAAK,GAAG,EACR,KAAK;AAChD,KAAI,CAAC,IACH,QAAO;EAAE,IAAI;EAAO,OAAO;EAAgB,QAAQ;EAAK;AAE1D,KAAI;AAEF,SAAO;GAAE,IAAI;GAAM,MAAM;IAAE,SAAS;IAAI,SAAA,MADlB,SAAS,KAAK,QAAQ;IACK,MAAM;IAAK;GAAE;SACxD;AACN,SAAO;GAAE,IAAI;GAAO,OAAO;GAAkB,QAAQ;GAAK;;;AAI9D,eAAsB,sBACpB,KACA,SACA,MACA,SAC8D;CAC9D,MAAM,MAAM,kBAAkB,KAAK;AACnC,KAAI,IACF,QAAO;CAET,MAAM,KAAK,iBAAiB,QAAQ;AACpC,KAAI,uBAAuB,IAAI,CAAC,OAAO,MAAM,MAAM,GAAG,CACpD,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,GAAG;EAAc,QAAQ;EAAK;CAGrE,MAAM,MAAM,yBAAyB,MADlB,wBAAwB,KAAK,GAAG,EACR,KAAK;AAChD,KAAI,CAAC,IACH,QAAO;EAAE,IAAI;EAAO,OAAO;EAAgB,QAAQ;EAAK;AAG1D,KAAI,CAAC,qBAAqB,MADH,wBAAwB,KAAK,GAAG,EACnB,IAAI,CACtC,QAAO;EAAE,IAAI;EAAO,OAAO;EAAsC,QAAQ;EAAK;AAEhF,OAAM,UAAU,KAAK,SAAS,QAAQ;AACtC,QAAO;EAAE,IAAI;EAAM,MAAM;GAAE,SAAS;GAAI,MAAM;GAAK;EAAE;;AAGvD,eAAsB,sBAAoF;CACxG,MAAM,OAAO,wBAAwB;AACrC,KAAI;AAEF,SAAO;GAAE,IAAI;GAAM,MAAM;IAAE,SAAA,MADL,SAAS,MAAM,QAAQ;IACT;IAAM;GAAE;SACtC;AACN,SAAO;GAAE,IAAI;GAAM,MAAM;IAAE,SAAS;IAAI;IAAM;GAAE;;;AAIpD,eAAsB,qBACpB,SAC6C;CAC7C,MAAM,OAAO,wBAAwB;AACrC,OAAM,MAAM,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,OAAM,UAAU,MAAM,SAAS,QAAQ;AACvC,QAAO;EAAE,IAAI;EAAM,MAAM,EAAE,MAAM;EAAE;;AAOrC,MAAM,yBAAyB,MAAM;AACrC,MAAM,wBAAwB;AAE9B,MAAM,0BAA0B;CAAC;CAAQ;CAAQ;CAAS;CAAQ;AAElE,SAAS,uBAAiC;AACxC,QAAO,wBAAwB,KAAK,QAAQ,GAAG,wBAAwB,MAAM;;AAG/E,SAAS,UAAU,MAA0D;CAC3E,MAAM,IAAI,KAAK,aAAa,CAAC,MAAM;AACnC,KAAI,MAAM,YAAa,QAAO;AAC9B,KAAI,MAAM,gBAAgB,MAAM,YAAa,QAAO;AACpD,KAAI,MAAM,aAAc,QAAO;AAC/B,QAAO;;AAGT,SAAS,yBAAyB,KAAmE;AACnG,KAAI,IAAI,UAAU,KAAK,IAAI,OAAO,OAAQ,IAAI,OAAO,MAAQ,IAAI,OAAO,MAAQ,IAAI,OAAO,GACzF,QAAO;AAET,KAAI,IAAI,UAAU,KAAK,IAAI,OAAO,OAAQ,IAAI,OAAO,OAAQ,IAAI,OAAO,IACtE,QAAO;AAET,KACE,IAAI,UAAU,MACd,IAAI,OAAO,MACX,IAAI,OAAO,MACX,IAAI,OAAO,MACX,IAAI,OAAO,MACX,IAAI,OAAO,MACX,IAAI,OAAO,MACX,IAAI,QAAQ,MACZ,IAAI,QAAQ,GAEZ,QAAO;AAET,QAAO;;AAGT,SAAS,2BAA2B,KAAa,IAA4C;AAC3F,KAAI,uBAAuB,IAAI,CAAC,OAAO,MAAM,MAAM,GAAG,CACpD,QAAO;EAAE,IAAI;EAAO,OAAO,UAAU,GAAG;EAAc,QAAQ;EAAK;AAErE,QAAO;;AAGT,eAAsB,oBACpB,KACA,SACmG;CACnG,MAAM,eAAe,2BAA2B,KAAK,QAAQ;AAC7D,KAAI,aACF,QAAO;CAET,MAAM,KAAK,iBAAiB,QAAQ;CACpC,MAAM,OAAO,MAAM,wBAAwB,KAAK,GAAG;AACnD,MAAK,MAAM,QAAQ,sBAAsB,EAAE;EACzC,MAAM,MAAM,yBAAyB,MAAM,KAAK;AAChD,MAAI,CAAC,IACH;AAEF,MAAI;GACF,MAAM,KAAK,MAAM,KAAK,IAAI;AAC1B,OAAI,CAAC,GAAG,QAAQ,IAAI,GAAG,QAAQ,KAAK,GAAG,OAAO,uBAC5C;GAEF,MAAM,SAAS,MAAM,SAAS,IAAI;GAClC,MAAM,WAAW,yBAAyB,OAAO;AACjD,OAAI,CAAC,SACH;AAEF,UAAO;IAAE,IAAI;IAAM,MAAM;KAAE,SAAS;KAAI;KAAQ,aAAa;KAAU,MAAM;KAAK;IAAE;UAC9E;;AAIV,QAAO;EAAE,IAAI;EAAO,OAAO;EAAoB,QAAQ;EAAK;;AAG9D,eAAsB,2BACpB,KACA,SACA,QACA,UAC8D;CAC9D,MAAM,eAAe,2BAA2B,KAAK,QAAQ;AAC7D,KAAI,aACF,QAAO;CAET,MAAM,KAAK,iBAAiB,QAAQ;CACpC,MAAM,MAAM,UAAU,SAAS;AAC/B,KAAI,CAAC,IACH,QAAO;EAAE,IAAI;EAAO,OAAO;EAAmE,QAAQ;EAAK;CAE7G,IAAI;AACJ,KAAI;AACF,QAAM,OAAO,KAAK,QAAQ,SAAS;SAC7B;AACN,SAAO;GAAE,IAAI;GAAO,OAAO;GAAkB,QAAQ;GAAK;;AAE5D,KAAI,IAAI,WAAW,KAAK,IAAI,SAAS,uBACnC,QAAO;EAAE,IAAI;EAAO,OAAO,wCAAwC,uBAAuB;EAAS,QAAQ;EAAK;CAElH,MAAM,WAAW,yBAAyB,IAAI;AAC9C,KAAI,CAAC,YAAY,CAAC,uBAAuB,KAAK,SAAS,CACrD,QAAO;EAAE,IAAI;EAAO,OAAO;EAAmD,QAAQ;EAAK;CAG7F,MAAM,OAAO,MAAM,wBAAwB,KAAK,GAAG;CACnD,MAAM,WAAW,MAAM,wBAAwB,KAAK,GAAG;CACvD,MAAM,aAAa,GAAG,wBAAwB;CAC9C,MAAM,MAAM,yBAAyB,MAAM,WAAW;AACtD,KAAI,CAAC,OAAO,CAAC,qBAAqB,UAAU,IAAI,CAC9C,QAAO;EAAE,IAAI;EAAO,OAAO;EAAgB,QAAQ;EAAK;AAE1D,MAAK,MAAM,QAAQ,sBAAsB,EAAE;AACzC,MAAI,SAAS,WACX;EAEF,MAAM,QAAQ,yBAAyB,MAAM,KAAK;AAClD,MAAI,SAAS,qBAAqB,UAAU,MAAM,CAChD,KAAI;AACF,SAAM,OAAO,MAAM;UACb;;AAKZ,OAAM,UAAU,KAAK,IAAI;AACzB,QAAO;EAAE,IAAI;EAAM,MAAM;GAAE,SAAS;GAAI,MAAM;GAAK;EAAE;;AAGvD,SAAS,gBAAgB,KAAqF;AAC5G,KAAI,QAAQ,OAAQ,QAAO;AAC3B,KAAI,QAAQ,QAAS,QAAO;AAC5B,QAAO;;AAGT,SAAS,uBACP,KACA,UACS;AACT,QAAO,aAAa,gBAAgB,IAAI;;;AAI1C,eAAsB,sBAAsB,KAAa,SAAiE;CACxH,MAAM,eAAe,2BAA2B,KAAK,QAAQ;AAC7D,KAAI,aACF,QAAO;CAET,MAAM,KAAK,iBAAiB,QAAQ;CACpC,MAAM,OAAO,MAAM,wBAAwB,KAAK,GAAG;CACnD,MAAM,WAAW,MAAM,wBAAwB,KAAK,GAAG;AACvD,MAAK,MAAM,QAAQ,sBAAsB,EAAE;EACzC,MAAM,MAAM,yBAAyB,MAAM,KAAK;AAChD,MAAI,CAAC,OAAO,CAAC,qBAAqB,UAAU,IAAI,CAC9C;AAEF,MAAI;AACF,SAAM,OAAO,IAAI;UACX;;AAIV,QAAO;EAAE,IAAI;EAAM,MAAM,EAAE,SAAS,IAAI;EAAE"}
@@ -88,10 +88,17 @@ var ChatStreamMapper = class {
88
88
  const messageId = this.ensureAssistantMessageId();
89
89
  const delta = event.assistantMessageEvent;
90
90
  if (delta?.type === "text_delta" && typeof delta.delta === "string" && delta.delta) {
91
- this.currentAssistantText += delta.delta;
91
+ const text = extractTextFromMessage(message);
92
+ const suffixFromMessage = appendSuffix(this.currentAssistantText, text);
93
+ const suffix = text ? suffixFromMessage : appendSuffix(this.currentAssistantText, delta.delta);
94
+ if (!suffix) {
95
+ if (text) this.currentAssistantText = text;
96
+ return [];
97
+ }
98
+ this.currentAssistantText = text || `${this.currentAssistantText}${suffix}`;
92
99
  return [this.make("assistant_delta", {
93
100
  messageId,
94
- delta: delta.delta
101
+ delta: suffix
95
102
  })];
96
103
  }
97
104
  if (delta?.type === "thinking_delta" && typeof delta.delta === "string" && delta.delta) return [this.make("thinking_delta", {
@@ -1 +1 @@
1
- {"version":3,"file":"mapper.js","names":["message"],"sources":["../../../../src/gateway/chat-stream/mapper.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { EmbeddedStreamEvent } from '../../agent/embedded/types.js';\nimport type { ChatStreamEvent, ChatStreamStatus } from './protocol.js';\n\nexport type RuntimeStreamEvent = EmbeddedStreamEvent | { type: string; [key: string]: unknown };\n\nexport interface ChatStreamMapperOptions {\n runId: string;\n sessionKey: string;\n channel: string;\n}\n\ntype ToolResultEnvelope = { content?: unknown[]; details?: unknown; text?: string };\n\nexport class ChatStreamMapper {\n private assistantIndex = 0;\n private currentAssistantMessageId: string | undefined;\n private lastAssistantMessageId: string | undefined;\n private currentAssistantText = '';\n private toolCallToMessageId = new Map<string, string>();\n private started = false;\n private ended = false;\n\n constructor(private readonly opts: ChatStreamMapperOptions) {}\n\n map(input: RuntimeStreamEvent): ChatStreamEvent[] {\n const event = input as { type: string; [key: string]: unknown };\n switch (event.type) {\n case 'agent_start':\n return this.start();\n case 'agent_end':\n return [];\n case 'message_start':\n return this.mapMessageStart(event.message);\n case 'message_update':\n return this.mapMessageUpdate(event);\n case 'message_end':\n return this.mapMessageEnd(event.message);\n case 'tool_execution_start':\n return this.mapToolStart(event);\n case 'tool_execution_update':\n return this.mapToolUpdate(event);\n case 'tool_execution_end':\n return this.mapToolEnd(event);\n case 'user_message':\n return [this.make('user_message', { message: userMessageFromLegacyEvent(event) })];\n case 'user_transcript':\n return [this.make('user_transcript', { text: String(event.text ?? ''), media: event.media })];\n case 'progress':\n return [this.make('progress', { stage: String(event.stage ?? ''), message: String(event.message ?? '') })];\n case 'compaction':\n return [\n this.make('compaction', {\n status: event.status === 'completed' || event.status === 'skipped' ? event.status : 'started',\n tokensBefore: typeof event.tokensBefore === 'number' ? event.tokensBefore : undefined,\n tokensAfter: typeof event.tokensAfter === 'number' ? event.tokensAfter : undefined,\n summary: typeof event.summary === 'string' ? event.summary : undefined,\n }),\n ];\n case 'tts_audio':\n return [\n this.make('tts_audio', {\n uri: String(event.uri ?? ''),\n mimeType: String(event.mimeType ?? 'audio/mpeg'),\n name: String(event.name ?? 'voice.mp3'),\n attachTo: 'last_assistant',\n ...(this.lastAssistantMessageId ? { messageId: this.lastAssistantMessageId } : {}),\n }),\n ];\n case 'clarify_request':\n return [\n this.make('clarify_request', {\n requestId: String(event.requestId ?? ''),\n question: String(event.question ?? ''),\n choices: Array.isArray(event.choices) ? event.choices.filter((x): x is string => typeof x === 'string') : undefined,\n default: typeof event.default === 'string' ? event.default : undefined,\n }),\n ];\n case 'error':\n return [this.make('error', { code: 'AGENT_RUN_ERROR', message: String(event.content ?? event.message ?? 'Unknown error') })];\n default:\n return [];\n }\n }\n\n start(): ChatStreamEvent[] {\n if (this.started) return [];\n this.started = true;\n return [this.make('run_start', { channel: this.opts.channel })];\n }\n\n end(status: ChatStreamStatus, summary?: string): ChatStreamEvent[] {\n if (this.ended) return [];\n this.ended = true;\n return [this.make('run_end', { status, summary })];\n }\n\n error(message: string, code = 'AGENT_RUN_ERROR'): ChatStreamEvent[] {\n return [this.make('error', { code, message })];\n }\n\n private mapMessageStart(raw: unknown): ChatStreamEvent[] {\n const message = raw as AgentMessage | undefined;\n if (message?.role !== 'assistant') return [];\n const messageId = this.nextAssistantMessageId();\n this.currentAssistantMessageId = messageId;\n this.currentAssistantText = '';\n return [this.make('assistant_message_start', { messageId })];\n }\n\n private mapMessageUpdate(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const message = event.message as AgentMessage | undefined;\n if (message?.role !== 'assistant') return [];\n const messageId = this.ensureAssistantMessageId();\n const delta = event.assistantMessageEvent as { type?: unknown; delta?: unknown } | undefined;\n if (delta?.type === 'text_delta' && typeof delta.delta === 'string' && delta.delta) {\n this.currentAssistantText += delta.delta;\n return [this.make('assistant_delta', { messageId, delta: delta.delta })];\n }\n if (delta?.type === 'thinking_delta' && typeof delta.delta === 'string' && delta.delta) {\n return [this.make('thinking_delta', { messageId, delta: delta.delta })];\n }\n\n const text = extractTextFromMessage(message);\n const suffix = appendSuffix(this.currentAssistantText, text);\n this.currentAssistantText = text || this.currentAssistantText;\n return suffix ? [this.make('assistant_delta', { messageId, delta: suffix })] : [];\n }\n\n private mapMessageEnd(raw: unknown): ChatStreamEvent[] {\n const message = raw as AgentMessage | undefined;\n if (message?.role !== 'assistant') return [];\n const messageId = this.ensureAssistantMessageId();\n const events: ChatStreamEvent[] = [];\n const text = extractTextFromMessage(message);\n const suffix = appendSuffix(this.currentAssistantText, text);\n if (suffix) events.push(this.make('assistant_delta', { messageId, delta: suffix }));\n this.currentAssistantText = text || this.currentAssistantText;\n events.push(this.make('thinking_end', { messageId }));\n events.push(this.make('assistant_message_end', { messageId, usage: extractUsage(message) }));\n this.lastAssistantMessageId = messageId;\n this.currentAssistantMessageId = undefined;\n this.currentAssistantText = '';\n return events;\n }\n\n private mapToolStart(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const toolCallId = String(event.toolCallId ?? '');\n if (!toolCallId) return [];\n const messageId = this.ensureAssistantMessageId();\n this.toolCallToMessageId.set(toolCallId, messageId);\n return [\n this.make('tool_start', {\n messageId,\n toolCallId,\n toolName: String(event.toolName ?? 'unknown'),\n args: event.args,\n }),\n ];\n }\n\n private mapToolUpdate(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const toolCallId = String(event.toolCallId ?? '');\n if (!toolCallId) return [];\n const messageId = this.toolCallToMessageId.get(toolCallId) ?? this.ensureAssistantMessageId();\n const partial = event.partialResult;\n const details = extractDetails(partial);\n const textDelta = extractText(partial);\n return [\n this.make('tool_update', {\n messageId,\n toolCallId,\n toolName: String(event.toolName ?? 'unknown'),\n details,\n textDelta,\n }),\n ];\n }\n\n private mapToolEnd(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const toolCallId = String(event.toolCallId ?? '');\n if (!toolCallId) return [];\n const messageId = this.toolCallToMessageId.get(toolCallId) ?? this.ensureAssistantMessageId();\n const result = normalizeToolResult(event.result);\n return [\n this.make('tool_end', {\n messageId,\n toolCallId,\n toolName: String(event.toolName ?? 'unknown'),\n status: event.isError ? 'error' : 'success',\n result,\n errorMessage: event.isError ? result?.text : undefined,\n }),\n ];\n }\n\n private ensureAssistantMessageId(): string {\n if (!this.currentAssistantMessageId) {\n this.currentAssistantMessageId = this.nextAssistantMessageId();\n }\n return this.currentAssistantMessageId;\n }\n\n private nextAssistantMessageId(): string {\n this.assistantIndex += 1;\n return `msg_${this.opts.runId.replace(/[^a-zA-Z0-9_-]/g, '_')}_${this.assistantIndex}`;\n }\n\n private make<T extends ChatStreamEvent['type']>(\n type: T,\n payload: Extract<ChatStreamEvent, { type: T }>['payload'],\n ): Extract<ChatStreamEvent, { type: T }> {\n return {\n type,\n runId: this.opts.runId,\n sessionKey: this.opts.sessionKey,\n timestamp: Date.now(),\n payload,\n } as Extract<ChatStreamEvent, { type: T }>;\n }\n}\n\nfunction userMessageFromLegacyEvent(event: { [key: string]: unknown }): unknown {\n return {\n role: 'user',\n content: event.content ?? [],\n attachments: event.media,\n timestamp: typeof event.timestamp === 'number' ? event.timestamp : Date.now(),\n };\n}\n\nfunction extractTextFromMessage(message: AgentMessage): string {\n return extractText((message as { content?: unknown }).content);\n}\n\nfunction extractText(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (!Array.isArray(value)) return undefined;\n const text = value\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n return text || undefined;\n}\n\nfunction appendSuffix(base: string, next: string | undefined): string {\n if (!next) return '';\n if (!base) return next;\n if (next === base || base.endsWith(next)) return '';\n if (next.startsWith(base)) return next.slice(base.length);\n const max = Math.min(base.length, next.length, 512);\n for (let overlap = max; overlap > 0; overlap--) {\n if (base.slice(-overlap) === next.slice(0, overlap)) return next.slice(overlap);\n }\n return next;\n}\n\nfunction extractUsage(message: AgentMessage): AssistantMessageEndEventPayload['usage'] {\n const usage = (message as { usage?: unknown }).usage;\n if (!usage || typeof usage !== 'object') return undefined;\n const rec = usage as Record<string, unknown>;\n return {\n inputTokens: typeof rec.inputTokens === 'number' ? rec.inputTokens : undefined,\n outputTokens: typeof rec.outputTokens === 'number' ? rec.outputTokens : undefined,\n totalTokens: typeof rec.totalTokens === 'number' ? rec.totalTokens : undefined,\n cost: typeof rec.cost === 'number' ? rec.cost : undefined,\n };\n}\n\ntype AssistantMessageEndEventPayload = Extract<ChatStreamEvent, { type: 'assistant_message_end' }>['payload'];\n\nfunction extractDetails(value: unknown): unknown {\n if (!value || typeof value !== 'object') return undefined;\n return (value as Record<string, unknown>).details;\n}\n\nfunction normalizeToolResult(value: unknown): ToolResultEnvelope | undefined {\n if (value == null) return undefined;\n if (typeof value === 'string') return { text: value };\n if (typeof value !== 'object') return { text: String(value) };\n const rec = value as Record<string, unknown>;\n return {\n content: Array.isArray(rec.content) ? rec.content : undefined,\n details: rec.details,\n text: extractText(rec.content) ?? (typeof rec.text === 'string' ? rec.text : undefined),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mapper.js","names":["message"],"sources":["../../../../src/gateway/chat-stream/mapper.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { EmbeddedStreamEvent } from '../../agent/embedded/types.js';\nimport type { ChatStreamEvent, ChatStreamStatus } from './protocol.js';\n\nexport type RuntimeStreamEvent = EmbeddedStreamEvent | { type: string; [key: string]: unknown };\n\nexport interface ChatStreamMapperOptions {\n runId: string;\n sessionKey: string;\n channel: string;\n}\n\ntype ToolResultEnvelope = { content?: unknown[]; details?: unknown; text?: string };\n\nexport class ChatStreamMapper {\n private assistantIndex = 0;\n private currentAssistantMessageId: string | undefined;\n private lastAssistantMessageId: string | undefined;\n private currentAssistantText = '';\n private toolCallToMessageId = new Map<string, string>();\n private started = false;\n private ended = false;\n\n constructor(private readonly opts: ChatStreamMapperOptions) {}\n\n map(input: RuntimeStreamEvent): ChatStreamEvent[] {\n const event = input as { type: string; [key: string]: unknown };\n switch (event.type) {\n case 'agent_start':\n return this.start();\n case 'agent_end':\n return [];\n case 'message_start':\n return this.mapMessageStart(event.message);\n case 'message_update':\n return this.mapMessageUpdate(event);\n case 'message_end':\n return this.mapMessageEnd(event.message);\n case 'tool_execution_start':\n return this.mapToolStart(event);\n case 'tool_execution_update':\n return this.mapToolUpdate(event);\n case 'tool_execution_end':\n return this.mapToolEnd(event);\n case 'user_message':\n return [this.make('user_message', { message: userMessageFromLegacyEvent(event) })];\n case 'user_transcript':\n return [this.make('user_transcript', { text: String(event.text ?? ''), media: event.media })];\n case 'progress':\n return [this.make('progress', { stage: String(event.stage ?? ''), message: String(event.message ?? '') })];\n case 'compaction':\n return [\n this.make('compaction', {\n status: event.status === 'completed' || event.status === 'skipped' ? event.status : 'started',\n tokensBefore: typeof event.tokensBefore === 'number' ? event.tokensBefore : undefined,\n tokensAfter: typeof event.tokensAfter === 'number' ? event.tokensAfter : undefined,\n summary: typeof event.summary === 'string' ? event.summary : undefined,\n }),\n ];\n case 'tts_audio':\n return [\n this.make('tts_audio', {\n uri: String(event.uri ?? ''),\n mimeType: String(event.mimeType ?? 'audio/mpeg'),\n name: String(event.name ?? 'voice.mp3'),\n attachTo: 'last_assistant',\n ...(this.lastAssistantMessageId ? { messageId: this.lastAssistantMessageId } : {}),\n }),\n ];\n case 'clarify_request':\n return [\n this.make('clarify_request', {\n requestId: String(event.requestId ?? ''),\n question: String(event.question ?? ''),\n choices: Array.isArray(event.choices) ? event.choices.filter((x): x is string => typeof x === 'string') : undefined,\n default: typeof event.default === 'string' ? event.default : undefined,\n }),\n ];\n case 'error':\n return [this.make('error', { code: 'AGENT_RUN_ERROR', message: String(event.content ?? event.message ?? 'Unknown error') })];\n default:\n return [];\n }\n }\n\n start(): ChatStreamEvent[] {\n if (this.started) return [];\n this.started = true;\n return [this.make('run_start', { channel: this.opts.channel })];\n }\n\n end(status: ChatStreamStatus, summary?: string): ChatStreamEvent[] {\n if (this.ended) return [];\n this.ended = true;\n return [this.make('run_end', { status, summary })];\n }\n\n error(message: string, code = 'AGENT_RUN_ERROR'): ChatStreamEvent[] {\n return [this.make('error', { code, message })];\n }\n\n private mapMessageStart(raw: unknown): ChatStreamEvent[] {\n const message = raw as AgentMessage | undefined;\n if (message?.role !== 'assistant') return [];\n const messageId = this.nextAssistantMessageId();\n this.currentAssistantMessageId = messageId;\n this.currentAssistantText = '';\n return [this.make('assistant_message_start', { messageId })];\n }\n\n private mapMessageUpdate(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const message = event.message as AgentMessage | undefined;\n if (message?.role !== 'assistant') return [];\n const messageId = this.ensureAssistantMessageId();\n const delta = event.assistantMessageEvent as { type?: unknown; delta?: unknown } | undefined;\n if (delta?.type === 'text_delta' && typeof delta.delta === 'string' && delta.delta) {\n const text = extractTextFromMessage(message);\n const suffixFromMessage = appendSuffix(this.currentAssistantText, text);\n const suffix = text ? suffixFromMessage : appendSuffix(this.currentAssistantText, delta.delta);\n if (!suffix) {\n if (text) this.currentAssistantText = text;\n return [];\n }\n this.currentAssistantText = text || `${this.currentAssistantText}${suffix}`;\n return [this.make('assistant_delta', { messageId, delta: suffix })];\n }\n if (delta?.type === 'thinking_delta' && typeof delta.delta === 'string' && delta.delta) {\n return [this.make('thinking_delta', { messageId, delta: delta.delta })];\n }\n\n const text = extractTextFromMessage(message);\n const suffix = appendSuffix(this.currentAssistantText, text);\n this.currentAssistantText = text || this.currentAssistantText;\n return suffix ? [this.make('assistant_delta', { messageId, delta: suffix })] : [];\n }\n\n private mapMessageEnd(raw: unknown): ChatStreamEvent[] {\n const message = raw as AgentMessage | undefined;\n if (message?.role !== 'assistant') return [];\n const messageId = this.ensureAssistantMessageId();\n const events: ChatStreamEvent[] = [];\n const text = extractTextFromMessage(message);\n const suffix = appendSuffix(this.currentAssistantText, text);\n if (suffix) events.push(this.make('assistant_delta', { messageId, delta: suffix }));\n this.currentAssistantText = text || this.currentAssistantText;\n events.push(this.make('thinking_end', { messageId }));\n events.push(this.make('assistant_message_end', { messageId, usage: extractUsage(message) }));\n this.lastAssistantMessageId = messageId;\n this.currentAssistantMessageId = undefined;\n this.currentAssistantText = '';\n return events;\n }\n\n private mapToolStart(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const toolCallId = String(event.toolCallId ?? '');\n if (!toolCallId) return [];\n const messageId = this.ensureAssistantMessageId();\n this.toolCallToMessageId.set(toolCallId, messageId);\n return [\n this.make('tool_start', {\n messageId,\n toolCallId,\n toolName: String(event.toolName ?? 'unknown'),\n args: event.args,\n }),\n ];\n }\n\n private mapToolUpdate(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const toolCallId = String(event.toolCallId ?? '');\n if (!toolCallId) return [];\n const messageId = this.toolCallToMessageId.get(toolCallId) ?? this.ensureAssistantMessageId();\n const partial = event.partialResult;\n const details = extractDetails(partial);\n const textDelta = extractText(partial);\n return [\n this.make('tool_update', {\n messageId,\n toolCallId,\n toolName: String(event.toolName ?? 'unknown'),\n details,\n textDelta,\n }),\n ];\n }\n\n private mapToolEnd(event: { [key: string]: unknown }): ChatStreamEvent[] {\n const toolCallId = String(event.toolCallId ?? '');\n if (!toolCallId) return [];\n const messageId = this.toolCallToMessageId.get(toolCallId) ?? this.ensureAssistantMessageId();\n const result = normalizeToolResult(event.result);\n return [\n this.make('tool_end', {\n messageId,\n toolCallId,\n toolName: String(event.toolName ?? 'unknown'),\n status: event.isError ? 'error' : 'success',\n result,\n errorMessage: event.isError ? result?.text : undefined,\n }),\n ];\n }\n\n private ensureAssistantMessageId(): string {\n if (!this.currentAssistantMessageId) {\n this.currentAssistantMessageId = this.nextAssistantMessageId();\n }\n return this.currentAssistantMessageId;\n }\n\n private nextAssistantMessageId(): string {\n this.assistantIndex += 1;\n return `msg_${this.opts.runId.replace(/[^a-zA-Z0-9_-]/g, '_')}_${this.assistantIndex}`;\n }\n\n private make<T extends ChatStreamEvent['type']>(\n type: T,\n payload: Extract<ChatStreamEvent, { type: T }>['payload'],\n ): Extract<ChatStreamEvent, { type: T }> {\n return {\n type,\n runId: this.opts.runId,\n sessionKey: this.opts.sessionKey,\n timestamp: Date.now(),\n payload,\n } as Extract<ChatStreamEvent, { type: T }>;\n }\n}\n\nfunction userMessageFromLegacyEvent(event: { [key: string]: unknown }): unknown {\n return {\n role: 'user',\n content: event.content ?? [],\n attachments: event.media,\n timestamp: typeof event.timestamp === 'number' ? event.timestamp : Date.now(),\n };\n}\n\nfunction extractTextFromMessage(message: AgentMessage): string {\n return extractText((message as { content?: unknown }).content);\n}\n\nfunction extractText(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (!Array.isArray(value)) return undefined;\n const text = value\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n return text || undefined;\n}\n\nfunction appendSuffix(base: string, next: string | undefined): string {\n if (!next) return '';\n if (!base) return next;\n if (next === base || base.endsWith(next)) return '';\n if (next.startsWith(base)) return next.slice(base.length);\n const max = Math.min(base.length, next.length, 512);\n for (let overlap = max; overlap > 0; overlap--) {\n if (base.slice(-overlap) === next.slice(0, overlap)) return next.slice(overlap);\n }\n return next;\n}\n\nfunction extractUsage(message: AgentMessage): AssistantMessageEndEventPayload['usage'] {\n const usage = (message as { usage?: unknown }).usage;\n if (!usage || typeof usage !== 'object') return undefined;\n const rec = usage as Record<string, unknown>;\n return {\n inputTokens: typeof rec.inputTokens === 'number' ? rec.inputTokens : undefined,\n outputTokens: typeof rec.outputTokens === 'number' ? rec.outputTokens : undefined,\n totalTokens: typeof rec.totalTokens === 'number' ? rec.totalTokens : undefined,\n cost: typeof rec.cost === 'number' ? rec.cost : undefined,\n };\n}\n\ntype AssistantMessageEndEventPayload = Extract<ChatStreamEvent, { type: 'assistant_message_end' }>['payload'];\n\nfunction extractDetails(value: unknown): unknown {\n if (!value || typeof value !== 'object') return undefined;\n return (value as Record<string, unknown>).details;\n}\n\nfunction normalizeToolResult(value: unknown): ToolResultEnvelope | undefined {\n if (value == null) return undefined;\n if (typeof value === 'string') return { text: value };\n if (typeof value !== 'object') return { text: String(value) };\n const rec = value as Record<string, unknown>;\n return {\n content: Array.isArray(rec.content) ? rec.content : undefined,\n details: rec.details,\n text: extractText(rec.content) ?? (typeof rec.text === 'string' ? rec.text : undefined),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { type CapabilityPreset } from '../agent-manifest/schema.js';
2
+ import type { Config } from '../config/schema.js';
3
+ export type GlobalDefaultsProviderSource = 'config' | 'env' | 'oauth' | 'extension' | 'models_json' | 'agent' | null;
4
+ export type GlobalDefaultsProviderRow = {
5
+ id: string;
6
+ name: string;
7
+ configured: boolean;
8
+ source: GlobalDefaultsProviderSource;
9
+ };
10
+ export type GlobalDefaultsRecommendation = {
11
+ provider: string;
12
+ model: string;
13
+ reason: 'configured-provider' | 'recommended';
14
+ };
15
+ export type GlobalDefaultsPayload = {
16
+ presetId: string;
17
+ models: NonNullable<CapabilityPreset['models']>;
18
+ providers: GlobalDefaultsProviderRow[];
19
+ recommendations: GlobalDefaultsRecommendation[];
20
+ };
21
+ export type UpdateGlobalDefaultsBody = {
22
+ models?: CapabilityPreset['models'];
23
+ };
24
+ export type GlobalDefaultsAdminResult<T> = {
25
+ ok: true;
26
+ data: T;
27
+ } | {
28
+ ok: false;
29
+ error: string;
30
+ status?: 400 | 404 | 500;
31
+ };
32
+ export declare function ensureGlobalDefaultsConfig(cfg: Config): Config;
33
+ export declare function listGlobalDefaults(cfg: Config): Promise<GlobalDefaultsPayload>;
34
+ export declare function prepareUpdateGlobalDefaults(cfg: Config, body: UpdateGlobalDefaultsBody): GlobalDefaultsAdminResult<{
35
+ nextConfig: Config;
36
+ }>;
@@ -0,0 +1,146 @@
1
+ import { CapabilityPresetSchema, init_schema } from "../agent-manifest/schema.js";
2
+ import { getAllProviders, getModelsByProvider, getProviderAuthState, getProviderDisplayName, init_providers } from "../providers/index.js";
3
+ import { getRecommendedModelsForProvider } from "../providers/presentation.js";
4
+ //#region src/gateway/global-defaults-admin.ts
5
+ init_schema();
6
+ init_providers();
7
+ function defaultPresetId(cfg) {
8
+ return cfg.agents.defaultPreset || "default";
9
+ }
10
+ function emptyGlobalDefaultsPreset(id) {
11
+ return {
12
+ id,
13
+ name: "Global defaults",
14
+ description: "Default capabilities inherited by every agent.",
15
+ version: 1,
16
+ models: {
17
+ defaultRole: "deep",
18
+ roles: {}
19
+ }
20
+ };
21
+ }
22
+ function ensureGlobalDefaultsConfig(cfg) {
23
+ const presetId = defaultPresetId(cfg);
24
+ const current = cfg.agents.capabilityPresets[presetId];
25
+ const preset = current ? {
26
+ ...current,
27
+ id: presetId,
28
+ name: current.name || "Global defaults",
29
+ version: current.version || 1,
30
+ models: current.models ?? {
31
+ defaultRole: "deep",
32
+ roles: {}
33
+ }
34
+ } : emptyGlobalDefaultsPreset(presetId);
35
+ return {
36
+ ...cfg,
37
+ agents: {
38
+ ...cfg.agents,
39
+ defaultPreset: presetId,
40
+ capabilityPresets: {
41
+ ...cfg.agents.capabilityPresets,
42
+ [presetId]: preset
43
+ }
44
+ }
45
+ };
46
+ }
47
+ function sourceFromAuthMode(authMode) {
48
+ if (authMode === "gateway") return "config";
49
+ if (authMode === "env") return "env";
50
+ if (authMode === "oauth") return "oauth";
51
+ if (authMode === "extension") return "extension";
52
+ if (authMode === "models_json") return "models_json";
53
+ if (authMode === "agent") return "agent";
54
+ return null;
55
+ }
56
+ function recommendedModelForProvider(provider) {
57
+ const recommended = getRecommendedModelsForProvider(provider, 1)[0];
58
+ if (recommended) return recommended.ref;
59
+ const model = getModelsByProvider(provider)[0];
60
+ return model ? `${provider}/${model.id}` : null;
61
+ }
62
+ async function listGlobalDefaults(cfg) {
63
+ const normalized = ensureGlobalDefaultsConfig(cfg);
64
+ const presetId = defaultPresetId(normalized);
65
+ const preset = normalized.agents.capabilityPresets[presetId] ?? emptyGlobalDefaultsPreset(presetId);
66
+ const providers = [];
67
+ const recommendations = [];
68
+ for (const provider of getAllProviders()) {
69
+ const auth = await getProviderAuthState(provider);
70
+ const configured = auth.authStatus === "connected";
71
+ const source = configured ? sourceFromAuthMode(auth.authMode) : null;
72
+ providers.push({
73
+ id: provider,
74
+ name: getProviderDisplayName(provider),
75
+ configured,
76
+ source
77
+ });
78
+ if (configured) {
79
+ const model = recommendedModelForProvider(provider);
80
+ if (model) recommendations.push({
81
+ provider,
82
+ model,
83
+ reason: "configured-provider"
84
+ });
85
+ }
86
+ }
87
+ providers.sort((a, b) => Number(b.configured) - Number(a.configured) || a.name.localeCompare(b.name));
88
+ return {
89
+ presetId,
90
+ models: preset.models ?? {
91
+ defaultRole: "deep",
92
+ roles: {}
93
+ },
94
+ providers,
95
+ recommendations
96
+ };
97
+ }
98
+ function prepareUpdateGlobalDefaults(cfg, body) {
99
+ const normalized = ensureGlobalDefaultsConfig(cfg);
100
+ const presetId = defaultPresetId(normalized);
101
+ let nextPreset = normalized.agents.capabilityPresets[presetId] ?? emptyGlobalDefaultsPreset(presetId);
102
+ if (body.models !== void 0) {
103
+ const parsed = CapabilityPresetSchema.pick({ models: true }).safeParse({ models: body.models });
104
+ if (!parsed.success) return {
105
+ ok: false,
106
+ error: `models ${parsed.error.issues[0]?.message ?? "is invalid"}`,
107
+ status: 400
108
+ };
109
+ const models = parsed.data.models ?? {
110
+ defaultRole: "deep",
111
+ roles: {}
112
+ };
113
+ const roles = models.roles ?? {};
114
+ const defaultRole = models.defaultRole ?? Object.keys(roles)[0] ?? "deep";
115
+ if (Object.keys(roles).length > 0 && !roles[defaultRole]) return {
116
+ ok: false,
117
+ error: "models.defaultRole must reference models.roles",
118
+ status: 400
119
+ };
120
+ nextPreset = {
121
+ ...nextPreset,
122
+ models: {
123
+ ...models,
124
+ defaultRole,
125
+ roles
126
+ }
127
+ };
128
+ }
129
+ return {
130
+ ok: true,
131
+ data: { nextConfig: {
132
+ ...normalized,
133
+ agents: {
134
+ ...normalized.agents,
135
+ capabilityPresets: {
136
+ ...normalized.agents.capabilityPresets,
137
+ [presetId]: nextPreset
138
+ }
139
+ }
140
+ } }
141
+ };
142
+ }
143
+ //#endregion
144
+ export { ensureGlobalDefaultsConfig, listGlobalDefaults, prepareUpdateGlobalDefaults };
145
+
146
+ //# sourceMappingURL=global-defaults-admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-defaults-admin.js","names":[],"sources":["../../../src/gateway/global-defaults-admin.ts"],"sourcesContent":["import {\n CapabilityPresetSchema,\n DEFAULT_CAPABILITY_PRESET_ID,\n type CapabilityPreset,\n} from '../agent-manifest/schema.js';\nimport type { Config } from '../config/schema.js';\nimport {\n getAllProviders,\n getModelsByProvider,\n getProviderAuthState,\n getProviderDisplayName,\n} from '../providers/index.js';\nimport { getRecommendedModelsForProvider } from '../providers/presentation.js';\n\nexport type GlobalDefaultsProviderSource = 'config' | 'env' | 'oauth' | 'extension' | 'models_json' | 'agent' | null;\n\nexport type GlobalDefaultsProviderRow = {\n id: string;\n name: string;\n configured: boolean;\n source: GlobalDefaultsProviderSource;\n};\n\nexport type GlobalDefaultsRecommendation = {\n provider: string;\n model: string;\n reason: 'configured-provider' | 'recommended';\n};\n\nexport type GlobalDefaultsPayload = {\n presetId: string;\n models: NonNullable<CapabilityPreset['models']>;\n providers: GlobalDefaultsProviderRow[];\n recommendations: GlobalDefaultsRecommendation[];\n};\n\nexport type UpdateGlobalDefaultsBody = {\n models?: CapabilityPreset['models'];\n};\n\nexport type GlobalDefaultsAdminResult<T> =\n | { ok: true; data: T }\n | { ok: false; error: string; status?: 400 | 404 | 500 };\n\nfunction defaultPresetId(cfg: Config): string {\n return cfg.agents.defaultPreset || DEFAULT_CAPABILITY_PRESET_ID;\n}\n\nfunction emptyGlobalDefaultsPreset(id: string): CapabilityPreset {\n return {\n id,\n name: 'Global defaults',\n description: 'Default capabilities inherited by every agent.',\n version: 1,\n models: {\n defaultRole: 'deep',\n roles: {},\n },\n };\n}\n\nexport function ensureGlobalDefaultsConfig(cfg: Config): Config {\n const presetId = defaultPresetId(cfg);\n const current = cfg.agents.capabilityPresets[presetId];\n const preset = current\n ? {\n ...current,\n id: presetId,\n name: current.name || 'Global defaults',\n version: current.version || 1,\n models: current.models ?? { defaultRole: 'deep', roles: {} },\n }\n : emptyGlobalDefaultsPreset(presetId);\n\n return {\n ...cfg,\n agents: {\n ...cfg.agents,\n defaultPreset: presetId,\n capabilityPresets: {\n ...cfg.agents.capabilityPresets,\n [presetId]: preset,\n },\n },\n };\n}\n\nfunction sourceFromAuthMode(authMode: string): GlobalDefaultsProviderSource {\n if (authMode === 'gateway') return 'config';\n if (authMode === 'env') return 'env';\n if (authMode === 'oauth') return 'oauth';\n if (authMode === 'extension') return 'extension';\n if (authMode === 'models_json') return 'models_json';\n if (authMode === 'agent') return 'agent';\n return null;\n}\n\nfunction recommendedModelForProvider(provider: string): string | null {\n const recommended = getRecommendedModelsForProvider(provider, 1)[0];\n if (recommended) return recommended.ref;\n const model = getModelsByProvider(provider)[0];\n return model ? `${provider}/${model.id}` : null;\n}\n\nexport async function listGlobalDefaults(cfg: Config): Promise<GlobalDefaultsPayload> {\n const normalized = ensureGlobalDefaultsConfig(cfg);\n const presetId = defaultPresetId(normalized);\n const preset = normalized.agents.capabilityPresets[presetId] ?? emptyGlobalDefaultsPreset(presetId);\n const providers: GlobalDefaultsProviderRow[] = [];\n const recommendations: GlobalDefaultsRecommendation[] = [];\n\n for (const provider of getAllProviders()) {\n const auth = await getProviderAuthState(provider);\n const configured = auth.authStatus === 'connected';\n const source = configured ? sourceFromAuthMode(auth.authMode) : null;\n providers.push({\n id: provider,\n name: getProviderDisplayName(provider),\n configured,\n source,\n });\n if (configured) {\n const model = recommendedModelForProvider(provider);\n if (model) {\n recommendations.push({ provider, model, reason: 'configured-provider' });\n }\n }\n }\n\n providers.sort((a, b) => Number(b.configured) - Number(a.configured) || a.name.localeCompare(b.name));\n\n return {\n presetId,\n models: preset.models ?? { defaultRole: 'deep', roles: {} },\n providers,\n recommendations,\n };\n}\n\nexport function prepareUpdateGlobalDefaults(\n cfg: Config,\n body: UpdateGlobalDefaultsBody,\n): GlobalDefaultsAdminResult<{ nextConfig: Config }> {\n const normalized = ensureGlobalDefaultsConfig(cfg);\n const presetId = defaultPresetId(normalized);\n const current = normalized.agents.capabilityPresets[presetId] ?? emptyGlobalDefaultsPreset(presetId);\n let nextPreset: CapabilityPreset = current;\n\n if (body.models !== undefined) {\n const parsed = CapabilityPresetSchema.pick({ models: true }).safeParse({ models: body.models });\n if (!parsed.success) {\n return { ok: false, error: `models ${parsed.error.issues[0]?.message ?? 'is invalid'}`, status: 400 };\n }\n const models = parsed.data.models ?? { defaultRole: 'deep', roles: {} };\n const roles = models.roles ?? {};\n const defaultRole = models.defaultRole ?? Object.keys(roles)[0] ?? 'deep';\n if (Object.keys(roles).length > 0 && !roles[defaultRole]) {\n return { ok: false, error: 'models.defaultRole must reference models.roles', status: 400 };\n }\n nextPreset = {\n ...nextPreset,\n models: { ...models, defaultRole, roles },\n };\n }\n\n return {\n ok: true,\n data: {\n nextConfig: {\n ...normalized,\n agents: {\n ...normalized.agents,\n capabilityPresets: {\n ...normalized.agents.capabilityPresets,\n [presetId]: nextPreset,\n },\n },\n },\n },\n };\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { AgentModelsSchema, init_schema as init_schema$1, parseModelRef } from "
3
3
  import { init_agent_scope, normalizeAgentId } from "../../../agent/agent-scope.js";
4
4
  import { init_providers, isProviderConfigured, resolveModel } from "../../../providers/index.js";
5
5
  import { getVoiceModelsConfig } from "../../../config/voice.js";
6
- import { deleteAgentAvatarFile, finalizeCreateAgentDirs, getGatewayAgentEffectiveManifest, listAgentProfileFiles, listGatewayAgents, prepareCreateAgent, prepareDeleteAgent, prepareUpdateAgent, readAgentAvatarFile, readAgentProfileFile, runAfterDeletePurge, writeAgentAvatarFromBase64, writeAgentProfileFile } from "../../agents-admin.js";
6
+ import { deleteAgentAvatarFile, finalizeCreateAgentDirs, getGatewayAgentEffectiveManifest, listAgentProfileFiles, listGatewayAgents, prepareCreateAgent, prepareDeleteAgent, prepareUpdateAgent, readAgentAvatarFile, readAgentProfileFile, readUserProfileFile, runAfterDeletePurge, writeAgentAvatarFromBase64, writeAgentProfileFile, writeUserProfileFile } from "../../agents-admin.js";
7
7
  import { resolveImageGenerationCapabilities, resolveImageUnderstandingCapabilities } from "../../image-capabilities.js";
8
8
  //#region src/gateway/hono/routes/agents.ts
9
9
  init_schema$1();
@@ -89,6 +89,38 @@ function registerAgentsRoutes(authenticated, deps) {
89
89
  payload
90
90
  });
91
91
  });
92
+ authenticated.get("/api/user-profile", async (c) => {
93
+ const res = await readUserProfileFile();
94
+ if (res.ok === false) return c.json({
95
+ ok: false,
96
+ error: { message: res.error }
97
+ }, res.status ?? 400);
98
+ return c.json({
99
+ ok: true,
100
+ payload: res.data
101
+ });
102
+ });
103
+ authenticated.put("/api/user-profile", strictRateLimitMiddleware, async (c) => {
104
+ let content = "";
105
+ try {
106
+ const body = await c.req.json();
107
+ content = typeof body.content === "string" ? body.content : "";
108
+ } catch {
109
+ return c.json({
110
+ ok: false,
111
+ error: { message: "Invalid JSON" }
112
+ }, 400);
113
+ }
114
+ const res = await writeUserProfileFile(content);
115
+ if (res.ok === false) return c.json({
116
+ ok: false,
117
+ error: { message: res.error }
118
+ }, res.status ?? 400);
119
+ return c.json({
120
+ ok: true,
121
+ payload: res.data
122
+ });
123
+ });
92
124
  authenticated.post("/api/agents", strictRateLimitMiddleware, async (c) => {
93
125
  let body = {};
94
126
  try {