@xopcai/xopc 0.0.87 → 0.0.89

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 (282) hide show
  1. package/README.md +8 -1
  2. package/README.zh-CN.md +8 -1
  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/agents-B6PJB07W.js +222 -0
  6. package/dist/gateway/static/root/assets/apps-page-BOr0B1wv.js +1 -0
  7. package/dist/gateway/static/root/assets/channels-settings-BelUKggl.js +1 -0
  8. package/dist/gateway/static/root/assets/{channels-status-swr-BSHqqCF1.js → channels-status-swr-DaHGkRF1.js} +1 -1
  9. package/dist/gateway/static/root/assets/cron-api-CjOg-BIj.js +1 -0
  10. package/dist/gateway/static/root/assets/cron-page-DhoZmZXb.js +1 -0
  11. package/dist/gateway/static/root/assets/{dist-Cmjp2APP.js → dist-6LecgDx5.js} +1 -1
  12. package/dist/gateway/static/root/assets/{extension-debug-page-CFa9z_1N.js → extension-debug-page-CtuKJ9tE.js} +1 -1
  13. package/dist/gateway/static/root/assets/{extension-page-BI8eaTPq.js → extension-page-ykzjOkR5.js} +1 -1
  14. package/dist/gateway/static/root/assets/extension-settings-page-Ce2qrdpO.js +1 -0
  15. package/dist/gateway/static/root/assets/{fetch-DRqwef_Q.js → fetch-C9FFJjuH.js} +1 -1
  16. package/dist/gateway/static/root/assets/{field-primitives-BiNHBo2Y.js → field-primitives-BFcrNeTU.js} +1 -1
  17. package/dist/gateway/static/root/assets/{heartbeat-config-api-ZRb8qhuz.js → heartbeat-config-api-CEg4Vr9R.js} +1 -1
  18. package/dist/gateway/static/root/assets/{index-Cu7bKuUi.js → index-CZfy9oxs.js} +85 -85
  19. package/dist/gateway/static/root/assets/index-CiN1cQiQ.css +1 -0
  20. package/dist/gateway/static/root/assets/logs-page-BwWLfqvd.js +1 -0
  21. package/dist/gateway/static/root/assets/sessions-page-DV5WN8uk.js +1 -0
  22. package/dist/gateway/static/root/assets/{settings-form-section-DiqqVs6m.js → settings-form-section-BqdzA28u.js} +1 -1
  23. package/dist/gateway/static/root/assets/settings-page-CfOBRbPX.js +3 -0
  24. package/dist/gateway/static/root/assets/{share-preview-page-n1Gprylk.js → share-preview-page-Di5Bzh4g.js} +1 -1
  25. package/dist/gateway/static/root/assets/skills-page-D0H5Kaxg.js +2 -0
  26. package/dist/gateway/static/root/assets/{theme-store-CZOh1nT3.js → theme-store-CNqbmTNV.js} +1 -1
  27. package/dist/gateway/static/root/assets/url-aYn-Rj1C.js +7 -0
  28. package/dist/gateway/static/root/assets/{utils-CkWBfxs4.js → utils-BWm2tG2w.js} +1 -1
  29. package/dist/gateway/static/root/assets/voice-api-key-field-X2UfnHeq.js +1 -0
  30. package/dist/gateway/static/root/assets/workflows-page-BOPpO3NG.js +27 -0
  31. package/dist/gateway/static/root/index.html +5 -5
  32. package/dist/package.js +1 -1
  33. package/dist/src/agent/agent-manager.d.ts +2 -0
  34. package/dist/src/agent/agent-manager.js +1 -0
  35. package/dist/src/agent/agent-manager.js.map +1 -1
  36. package/dist/src/agent/child-agent-factory.d.ts +15 -0
  37. package/dist/src/agent/child-agent-factory.js +35 -2
  38. package/dist/src/agent/child-agent-factory.js.map +1 -1
  39. package/dist/src/agent/client-error-format.d.ts +20 -0
  40. package/dist/src/agent/client-error-format.js +97 -0
  41. package/dist/src/agent/client-error-format.js.map +1 -0
  42. package/dist/src/agent/embedded/run-turn.js +23 -4
  43. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  44. package/dist/src/agent/goals/goal-locale.d.ts +1 -1
  45. package/dist/src/agent/inbound/turn-dispatcher.js +1 -1
  46. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  47. package/dist/src/agent/orchestration/llm-turn-retry.d.ts +2 -0
  48. package/dist/src/agent/orchestration/llm-turn-retry.js +9 -1
  49. package/dist/src/agent/orchestration/llm-turn-retry.js.map +1 -1
  50. package/dist/src/agent/service/process-direct-streaming.js +19 -3
  51. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  52. package/dist/src/agent/service/webchat-tts.d.ts +1 -2
  53. package/dist/src/agent/service/webchat-tts.js +1 -1
  54. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  55. package/dist/src/agent/service.js +2 -1
  56. package/dist/src/agent/service.js.map +1 -1
  57. package/dist/src/agent/service.types.d.ts +3 -1
  58. package/dist/src/agent/tools/cronjob-tool.js +2 -1
  59. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  60. package/dist/src/agent/tools/factory.d.ts +3 -0
  61. package/dist/src/agent/tools/factory.js +2 -23
  62. package/dist/src/agent/tools/factory.js.map +1 -1
  63. package/dist/src/agent/tools/workflow-tool.d.ts +6 -28
  64. package/dist/src/agent/tools/workflow-tool.js +61 -213
  65. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  66. package/dist/src/agent/workflow/agent-progress.d.ts +5 -0
  67. package/dist/src/agent/workflow/agent-progress.js +65 -0
  68. package/dist/src/agent/workflow/agent-progress.js.map +1 -0
  69. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
  70. package/dist/src/agent/workflow/builtins/audit-repo.js +14 -0
  71. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  72. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  73. package/dist/src/agent/workflow/builtins/debug-incident.js +14 -0
  74. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  75. package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +12 -0
  76. package/dist/src/agent/workflow/builtins/implementation-plan.js +175 -0
  77. package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -0
  78. package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
  79. package/dist/src/agent/workflow/builtins/index.js +11 -1
  80. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  81. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
  82. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +14 -0
  83. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  84. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  85. package/dist/src/agent/workflow/builtins/pr-review.js +14 -0
  86. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  87. package/dist/src/agent/workflow/builtins/release-check.d.ts +11 -0
  88. package/dist/src/agent/workflow/builtins/release-check.js +165 -0
  89. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -0
  90. package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
  91. package/dist/src/agent/workflow/builtins/research.js +14 -0
  92. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  93. package/dist/src/agent/workflow/index.d.ts +2 -1
  94. package/dist/src/agent/workflow/index.js +3 -2
  95. package/dist/src/agent/workflow/meta-locale.d.ts +12 -0
  96. package/dist/src/agent/workflow/meta-locale.js +62 -0
  97. package/dist/src/agent/workflow/meta-locale.js.map +1 -0
  98. package/dist/src/agent/workflow/parser.js +3 -0
  99. package/dist/src/agent/workflow/parser.js.map +1 -1
  100. package/dist/src/agent/workflow/runtime.d.ts +2 -2
  101. package/dist/src/agent/workflow/runtime.js +21 -14
  102. package/dist/src/agent/workflow/runtime.js.map +1 -1
  103. package/dist/src/agent/workflow/snapshot.js +2 -12
  104. package/dist/src/agent/workflow/snapshot.js.map +1 -1
  105. package/dist/src/agent/workflow/step-labels.d.ts +8 -0
  106. package/dist/src/agent/workflow/step-labels.js +48 -0
  107. package/dist/src/agent/workflow/step-labels.js.map +1 -0
  108. package/dist/src/agent/workflow/subagent-runner.js +46 -1
  109. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  110. package/dist/src/agent/workflow/types.d.ts +74 -1
  111. package/dist/src/agent/workflow/workflow-child-tools.d.ts +4 -0
  112. package/dist/src/agent/workflow/workflow-child-tools.js +21 -0
  113. package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -0
  114. package/dist/src/auth/credentials.d.ts +19 -2
  115. package/dist/src/auth/credentials.js +47 -13
  116. package/dist/src/auth/credentials.js.map +1 -1
  117. package/dist/src/auth/oauth/types.d.ts +16 -0
  118. package/dist/src/cli/commands/auth.js +6 -0
  119. package/dist/src/cli/commands/auth.js.map +1 -1
  120. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  121. package/dist/src/cli/commands/onboard/model.js +6 -0
  122. package/dist/src/cli/commands/onboard/model.js.map +1 -1
  123. package/dist/src/config/agent-typed-models.d.ts +18 -0
  124. package/dist/src/config/agent-typed-models.js +53 -0
  125. package/dist/src/config/agent-typed-models.js.map +1 -0
  126. package/dist/src/config/index.js +2 -2
  127. package/dist/src/config/schema.d.ts +52 -0
  128. package/dist/src/config/schema.js +39 -3
  129. package/dist/src/config/schema.js.map +1 -1
  130. package/dist/src/config/voice.d.ts +3 -28
  131. package/dist/src/config/voice.js +27 -261
  132. package/dist/src/config/voice.js.map +1 -1
  133. package/dist/src/cron/executor.d.ts +2 -0
  134. package/dist/src/cron/executor.js +59 -5
  135. package/dist/src/cron/executor.js.map +1 -1
  136. package/dist/src/cron/job-content.js +2 -1
  137. package/dist/src/cron/job-content.js.map +1 -1
  138. package/dist/src/cron/types.d.ts +21 -1
  139. package/dist/src/cron/validation.d.ts +76 -0
  140. package/dist/src/cron/validation.js +26 -1
  141. package/dist/src/cron/validation.js.map +1 -1
  142. package/dist/src/gateway/agents-admin.d.ts +9 -0
  143. package/dist/src/gateway/agents-admin.js +16 -0
  144. package/dist/src/gateway/agents-admin.js.map +1 -1
  145. package/dist/src/gateway/config-tools-web.js +3 -2
  146. package/dist/src/gateway/config-tools-web.js.map +1 -1
  147. package/dist/src/gateway/gateway-workflow-host.types.d.ts +17 -0
  148. package/dist/src/gateway/gateway-workflow-host.types.js +1 -0
  149. package/dist/src/gateway/hono/lib/agent-model.d.ts +7 -0
  150. package/dist/src/gateway/hono/lib/agent-model.js +36 -1
  151. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  152. package/dist/src/gateway/hono/lib/config-payload.js +28 -5
  153. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  154. package/dist/src/gateway/hono/lib/mask-secret-length.d.ts +6 -0
  155. package/dist/src/gateway/hono/lib/mask-secret-length.js +16 -0
  156. package/dist/src/gateway/hono/lib/mask-secret-length.js.map +1 -0
  157. package/dist/src/gateway/hono/lib/safe-providers-config.d.ts +1 -1
  158. package/dist/src/gateway/hono/lib/safe-providers-config.js +2 -1
  159. package/dist/src/gateway/hono/lib/safe-providers-config.js.map +1 -1
  160. package/dist/src/gateway/hono/lib/safe-voice-config.js +2 -1
  161. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  162. package/dist/src/gateway/hono/oauth-async.js +40 -15
  163. package/dist/src/gateway/hono/oauth-async.js.map +1 -1
  164. package/dist/src/gateway/hono/oauth.js +31 -6
  165. package/dist/src/gateway/hono/oauth.js.map +1 -1
  166. package/dist/src/gateway/hono/routes/agents.js +1 -1
  167. package/dist/src/gateway/hono/routes/config-patch/agents.js +8 -2
  168. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  169. package/dist/src/gateway/hono/routes/config-patch/gateway.js +3 -2
  170. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  171. package/dist/src/gateway/hono/routes/config-patch/misc.js +7 -2
  172. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
  173. package/dist/src/gateway/hono/routes/config.js +59 -0
  174. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  175. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  176. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  177. package/dist/src/gateway/hono/routes/models.js +84 -15
  178. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  179. package/dist/src/gateway/hono/routes/voice.js +75 -0
  180. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  181. package/dist/src/gateway/hono/routes/workflows.d.ts +3 -0
  182. package/dist/src/gateway/hono/routes/workflows.js +226 -0
  183. package/dist/src/gateway/hono/routes/workflows.js.map +1 -0
  184. package/dist/src/gateway/service/run-gateway-agent.js +2 -20
  185. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  186. package/dist/src/gateway/service.d.ts +8 -0
  187. package/dist/src/gateway/service.js +28 -2
  188. package/dist/src/gateway/service.js.map +1 -1
  189. package/dist/src/mcp/channel-bridge.js +1 -1
  190. package/dist/src/providers/index.d.ts +8 -0
  191. package/dist/src/providers/index.js +51 -12
  192. package/dist/src/providers/index.js.map +1 -1
  193. package/dist/src/share/site-share-config.d.ts +3 -2
  194. package/dist/src/share/site-share-config.js.map +1 -1
  195. package/dist/src/tui/tui-agent-events.js +2 -1
  196. package/dist/src/tui/tui-agent-events.js.map +1 -1
  197. package/dist/src/voice/metadata/builtin.d.ts +2 -0
  198. package/dist/src/voice/metadata/builtin.js +420 -0
  199. package/dist/src/voice/metadata/builtin.js.map +1 -0
  200. package/dist/src/voice/metadata/index.d.ts +4 -0
  201. package/dist/src/voice/metadata/index.js +3 -0
  202. package/dist/src/voice/metadata/registry.d.ts +5 -0
  203. package/dist/src/voice/metadata/registry.js +34 -0
  204. package/dist/src/voice/metadata/registry.js.map +1 -0
  205. package/dist/src/voice/metadata/types.d.ts +41 -0
  206. package/dist/src/voice/metadata/types.js +1 -0
  207. package/dist/src/voice/stt/list-providers.d.ts +3 -3
  208. package/dist/src/voice/stt/list-providers.js +41 -6
  209. package/dist/src/voice/stt/list-providers.js.map +1 -1
  210. package/dist/src/voice/tts/list-providers.d.ts +3 -3
  211. package/dist/src/voice/tts/list-providers.js +41 -6
  212. package/dist/src/voice/tts/list-providers.js.map +1 -1
  213. package/dist/src/workflows/domain/command.d.ts +19 -0
  214. package/dist/src/workflows/domain/command.js +1 -0
  215. package/dist/src/workflows/domain/definition-utils.d.ts +14 -0
  216. package/dist/src/workflows/domain/definition-utils.js +50 -0
  217. package/dist/src/workflows/domain/definition-utils.js.map +1 -0
  218. package/dist/src/workflows/domain/definition.d.ts +62 -0
  219. package/dist/src/workflows/domain/definition.js +1 -0
  220. package/dist/src/workflows/domain/event.d.ts +67 -0
  221. package/dist/src/workflows/domain/event.js +1 -0
  222. package/dist/src/workflows/domain/index.d.ts +7 -0
  223. package/dist/src/workflows/domain/index.js +4 -0
  224. package/dist/src/workflows/domain/result.d.ts +65 -0
  225. package/dist/src/workflows/domain/result.js +1 -0
  226. package/dist/src/workflows/domain/run.d.ts +177 -0
  227. package/dist/src/workflows/domain/run.js +14 -0
  228. package/dist/src/workflows/domain/run.js.map +1 -0
  229. package/dist/src/workflows/domain/validation.d.ts +19 -0
  230. package/dist/src/workflows/domain/validation.js +66 -0
  231. package/dist/src/workflows/domain/validation.js.map +1 -0
  232. package/dist/src/workflows/engine/index.d.ts +2 -0
  233. package/dist/src/workflows/engine/index.js +3 -0
  234. package/dist/src/workflows/engine/projector.d.ts +3 -0
  235. package/dist/src/workflows/engine/projector.js +205 -0
  236. package/dist/src/workflows/engine/projector.js.map +1 -0
  237. package/dist/src/workflows/engine/workflow-engine.d.ts +32 -0
  238. package/dist/src/workflows/engine/workflow-engine.js +189 -0
  239. package/dist/src/workflows/engine/workflow-engine.js.map +1 -0
  240. package/dist/src/workflows/index.d.ts +10 -0
  241. package/dist/src/workflows/index.js +18 -0
  242. package/dist/src/workflows/runtime/index.d.ts +1 -0
  243. package/dist/src/workflows/runtime/index.js +4 -0
  244. package/dist/src/workflows/runtime/script-runtime.d.ts +3 -0
  245. package/dist/src/workflows/runtime/script-runtime.js +3 -0
  246. package/dist/src/workflows/service/run-view-to-snapshot.d.ts +4 -0
  247. package/dist/src/workflows/service/run-view-to-snapshot.js +61 -0
  248. package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -0
  249. package/dist/src/workflows/service/workflow-run-service.d.ts +36 -0
  250. package/dist/src/workflows/service/workflow-run-service.js +279 -0
  251. package/dist/src/workflows/service/workflow-run-service.js.map +1 -0
  252. package/dist/src/workflows/service/workflow-run-service.types.d.ts +47 -0
  253. package/dist/src/workflows/service/workflow-run-service.types.js +1 -0
  254. package/dist/src/workflows/service/workflow-session-bridge.d.ts +29 -0
  255. package/dist/src/workflows/service/workflow-session-bridge.js +177 -0
  256. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -0
  257. package/dist/src/workflows/service/workflow-session-key.d.ts +3 -0
  258. package/dist/src/workflows/service/workflow-session-key.js +21 -0
  259. package/dist/src/workflows/service/workflow-session-key.js.map +1 -0
  260. package/dist/src/workflows/store/event-store.d.ts +17 -0
  261. package/dist/src/workflows/store/event-store.js +83 -0
  262. package/dist/src/workflows/store/event-store.js.map +1 -0
  263. package/dist/src/workflows/store/paths.d.ts +7 -0
  264. package/dist/src/workflows/store/paths.js +26 -0
  265. package/dist/src/workflows/store/paths.js.map +1 -0
  266. package/dist/src/workflows/store/run-store.d.ts +13 -0
  267. package/dist/src/workflows/store/run-store.js +69 -0
  268. package/dist/src/workflows/store/run-store.js.map +1 -0
  269. package/package.json +5 -5
  270. package/dist/gateway/static/root/assets/agents-BEAbXpuP.js +0 -222
  271. package/dist/gateway/static/root/assets/apps-page-Dg8R-Szf.js +0 -1
  272. package/dist/gateway/static/root/assets/channels-settings-yohw9YSu.js +0 -1
  273. package/dist/gateway/static/root/assets/cron-api-0h_QT8U3.js +0 -1
  274. package/dist/gateway/static/root/assets/cron-page-BkfKFfFk.js +0 -1
  275. package/dist/gateway/static/root/assets/extension-settings-page-x4BB7q1X.js +0 -1
  276. package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +0 -1
  277. package/dist/gateway/static/root/assets/logs-page-BFZ8GgCv.js +0 -1
  278. package/dist/gateway/static/root/assets/sessions-page-CD7AfB-2.js +0 -1
  279. package/dist/gateway/static/root/assets/settings-page-BBOjEQW3.js +0 -3
  280. package/dist/gateway/static/root/assets/skills-page-CcN_gj--.js +0 -2
  281. package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +0 -3
  282. package/dist/gateway/static/root/assets/voice-api-key-field-O6awz9hi.js +0 -1
@@ -1,4 +1,6 @@
1
1
  import type { SessionStore } from '../session/store.js';
2
+ import type { WorkflowRunInputEnvelope, WorkflowRunSource } from '../workflows/domain/index.js';
3
+ import type { StartWorkflowRunServiceParams, WorkflowRunServiceResult } from '../workflows/service/workflow-run-service.types.js';
2
4
  export type CronDeliveryMode = 'none' | 'announce' | 'direct';
3
5
  export interface CronDelivery {
4
6
  mode: CronDeliveryMode;
@@ -14,7 +16,19 @@ export type CronPayload = {
14
16
  message: string;
15
17
  model?: string;
16
18
  timeoutSeconds?: number;
17
- };
19
+ } | CronWorkflowRunPayload;
20
+ export interface CronWorkflowRunPayload {
21
+ kind: 'workflowRun';
22
+ definitionId: string;
23
+ input?: unknown;
24
+ inputEnvelope?: WorkflowRunInputEnvelope;
25
+ goal?: string;
26
+ agentId?: string;
27
+ sessionKey?: string;
28
+ source?: Partial<Extract<WorkflowRunSource, {
29
+ kind: 'cron';
30
+ }>>;
31
+ }
18
32
  export type CronSessionTarget = 'main' | 'isolated';
19
33
  export interface JobData {
20
34
  id: string;
@@ -67,6 +81,7 @@ export interface JobExecution {
67
81
  model?: string;
68
82
  provider?: string;
69
83
  usage?: CronUsageSummary;
84
+ workflowRunId?: string;
70
85
  }
71
86
  export interface CronUsageSummary {
72
87
  input_tokens?: number;
@@ -88,6 +103,10 @@ export interface CronRunOutcome {
88
103
  model?: string;
89
104
  provider?: string;
90
105
  usage?: CronUsageSummary;
106
+ workflowRunId?: string;
107
+ }
108
+ export interface CronWorkflowRunStarter {
109
+ startWorkflowRun(params: StartWorkflowRunServiceParams): Promise<WorkflowRunServiceResult>;
91
110
  }
92
111
  /** Optional hook after a successful cron run (e.g. wake gateway heartbeat). */
93
112
  export interface HeartbeatWakeSink {
@@ -106,6 +125,7 @@ export interface JobExecutorDeps {
106
125
  * (same as {@link JobData.agentId} set to the default agent). Typically `getDefaultAgentId(config)`.
107
126
  */
108
127
  getDefaultCronAgentId?: () => string;
128
+ workflowRunService?: CronWorkflowRunStarter;
109
129
  }
110
130
  export interface JobExecutor {
111
131
  execute(job: JobData, signal: AbortSignal, deps?: JobExecutorDeps): Promise<void>;
@@ -8,6 +8,25 @@ declare const CronPayloadSchema: z.ZodUnion<readonly [z.ZodObject<{
8
8
  message: z.ZodString;
9
9
  model: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
10
10
  timeoutSeconds: z.ZodOptional<z.ZodNumber>;
11
+ }, z.core.$strip>, z.ZodObject<{
12
+ kind: z.ZodLiteral<"workflowRun">;
13
+ definitionId: z.ZodString;
14
+ input: z.ZodOptional<z.ZodUnknown>;
15
+ inputEnvelope: z.ZodOptional<z.ZodObject<{
16
+ payload: z.ZodUnknown;
17
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
18
+ variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
19
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
20
+ }, z.core.$strip>>;
21
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
22
+ agentId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
23
+ sessionKey: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
24
+ source: z.ZodOptional<z.ZodObject<{
25
+ kind: z.ZodOptional<z.ZodLiteral<"cron">>;
26
+ scheduleId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
27
+ fireId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
28
+ scheduledAtMs: z.ZodOptional<z.ZodNumber>;
29
+ }, z.core.$strip>>;
11
30
  }, z.core.$strip>]>;
12
31
  declare const CronDeliverySchema: z.ZodObject<{
13
32
  mode: z.ZodDefault<z.ZodEnum<{
@@ -43,6 +62,25 @@ export declare const JobDataSchema: z.ZodObject<{
43
62
  message: z.ZodString;
44
63
  model: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
45
64
  timeoutSeconds: z.ZodOptional<z.ZodNumber>;
65
+ }, z.core.$strip>, z.ZodObject<{
66
+ kind: z.ZodLiteral<"workflowRun">;
67
+ definitionId: z.ZodString;
68
+ input: z.ZodOptional<z.ZodUnknown>;
69
+ inputEnvelope: z.ZodOptional<z.ZodObject<{
70
+ payload: z.ZodUnknown;
71
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
72
+ variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
73
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
74
+ }, z.core.$strip>>;
75
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
76
+ agentId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
77
+ sessionKey: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
78
+ source: z.ZodOptional<z.ZodObject<{
79
+ kind: z.ZodOptional<z.ZodLiteral<"cron">>;
80
+ scheduleId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
81
+ fireId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
82
+ scheduledAtMs: z.ZodOptional<z.ZodNumber>;
83
+ }, z.core.$strip>>;
46
84
  }, z.core.$strip>]>;
47
85
  delivery: z.ZodOptional<z.ZodObject<{
48
86
  mode: z.ZodDefault<z.ZodEnum<{
@@ -77,6 +115,25 @@ export declare const AddJobRequestSchema: z.ZodObject<{
77
115
  message: z.ZodString;
78
116
  model: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
79
117
  timeoutSeconds: z.ZodOptional<z.ZodNumber>;
118
+ }, z.core.$strip>, z.ZodObject<{
119
+ kind: z.ZodLiteral<"workflowRun">;
120
+ definitionId: z.ZodString;
121
+ input: z.ZodOptional<z.ZodUnknown>;
122
+ inputEnvelope: z.ZodOptional<z.ZodObject<{
123
+ payload: z.ZodUnknown;
124
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
125
+ variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
126
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
127
+ }, z.core.$strip>>;
128
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
129
+ agentId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
130
+ sessionKey: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
131
+ source: z.ZodOptional<z.ZodObject<{
132
+ kind: z.ZodOptional<z.ZodLiteral<"cron">>;
133
+ scheduleId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
134
+ fireId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
135
+ scheduledAtMs: z.ZodOptional<z.ZodNumber>;
136
+ }, z.core.$strip>>;
80
137
  }, z.core.$strip>]>;
81
138
  delivery: z.ZodOptional<z.ZodObject<{
82
139
  mode: z.ZodDefault<z.ZodEnum<{
@@ -111,6 +168,25 @@ export declare const UpdateJobRequestSchema: z.ZodObject<{
111
168
  message: z.ZodString;
112
169
  model: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
113
170
  timeoutSeconds: z.ZodOptional<z.ZodNumber>;
171
+ }, z.core.$strip>, z.ZodObject<{
172
+ kind: z.ZodLiteral<"workflowRun">;
173
+ definitionId: z.ZodString;
174
+ input: z.ZodOptional<z.ZodUnknown>;
175
+ inputEnvelope: z.ZodOptional<z.ZodObject<{
176
+ payload: z.ZodUnknown;
177
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
178
+ variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
179
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
180
+ }, z.core.$strip>>;
181
+ goal: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
182
+ agentId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
183
+ sessionKey: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
184
+ source: z.ZodOptional<z.ZodObject<{
185
+ kind: z.ZodOptional<z.ZodLiteral<"cron">>;
186
+ scheduleId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
187
+ fireId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
188
+ scheduledAtMs: z.ZodOptional<z.ZodNumber>;
189
+ }, z.core.$strip>>;
114
190
  }, z.core.$strip>]>>;
115
191
  delivery: z.ZodOptional<z.ZodObject<{
116
192
  mode: z.ZodDefault<z.ZodEnum<{
@@ -76,7 +76,32 @@ const CronAgentTurnPayloadSchema = z.object({
76
76
  model: optionalTrimmedString(100, 1),
77
77
  timeoutSeconds: z.number().int().min(10).max(3600).optional()
78
78
  });
79
- const CronPayloadSchema = z.union([CronSystemEventPayloadSchema, CronAgentTurnPayloadSchema]);
79
+ const WorkflowRunInputEnvelopeSchema = z.object({
80
+ payload: z.unknown(),
81
+ goal: optionalTrimmedString(5e3, 1),
82
+ variables: z.record(z.string(), z.unknown()).optional(),
83
+ context: z.record(z.string(), z.unknown()).optional()
84
+ });
85
+ const CronWorkflowRunPayloadSchema = z.object({
86
+ kind: z.literal("workflowRun"),
87
+ definitionId: z.string().min(1).max(200),
88
+ input: z.unknown().optional(),
89
+ inputEnvelope: WorkflowRunInputEnvelopeSchema.optional(),
90
+ goal: optionalTrimmedString(5e3, 1),
91
+ agentId: optionalTrimmedString(64, 1),
92
+ sessionKey: optionalTrimmedString(300, 1),
93
+ source: z.object({
94
+ kind: z.literal("cron").optional(),
95
+ scheduleId: optionalTrimmedString(200, 1),
96
+ fireId: optionalTrimmedString(200, 1),
97
+ scheduledAtMs: z.number().int().nonnegative().optional()
98
+ }).optional()
99
+ });
100
+ const CronPayloadSchema = z.union([
101
+ CronSystemEventPayloadSchema,
102
+ CronAgentTurnPayloadSchema,
103
+ CronWorkflowRunPayloadSchema
104
+ ]);
80
105
  const CronDeliverySchema = z.object({
81
106
  mode: CronDeliveryMode.default("none"),
82
107
  channel: optionalTrimmedString(32, 1),
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","names":[],"sources":["../../../src/cron/validation.ts"],"sourcesContent":["// Cron input validation using Zod\nimport { z } from 'zod';\nimport nodeCron from 'node-cron';\n\n// Custom cron validation\nconst cronExpression = z.string().superRefine((val, ctx) => {\n if (!nodeCron.validate(val)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid cron expression: ${val}`,\n });\n }\n});\n\n// Valid timezones (subset of IANA timezones)\nconst validTimezones = [\n 'UTC',\n 'America/New_York',\n 'America/Chicago',\n 'America/Denver',\n 'America/Los_Angeles',\n 'America/Toronto',\n 'Europe/London',\n 'Europe/Paris',\n 'Europe/Berlin',\n 'Europe/Moscow',\n 'Asia/Tokyo',\n 'Asia/Shanghai',\n 'Asia/Hong_Kong',\n 'Asia/Singapore',\n 'Asia/Seoul',\n 'Asia/Dubai',\n 'Asia/Mumbai',\n 'Australia/Sydney',\n 'Pacific/Auckland',\n];\n\nconst CronDeliveryMode = z.enum(['none', 'announce', 'direct']);\n\n/** Treat `''` / `null` / whitespace-only as absent so optional fields do not fail `.min(1)` */\nfunction optionalTrimmedString(max: number, min = 1) {\n return z.preprocess((v) => {\n if (v === null || v === undefined) return undefined;\n if (typeof v !== 'string') return v;\n const t = v.trim();\n return t.length === 0 ? undefined : t;\n }, z.string().min(min).max(max).optional());\n}\n\n/** Optional agent id on create; empty string → undefined. */\nconst optionalJobAgentId = optionalTrimmedString(64, 1);\n\n/** Optional absolute workspace path on create; empty string → undefined. */\nconst optionalJobWorkingDirectory = optionalTrimmedString(4096, 1);\n\n/**\n * PATCH: set string, or `null` / empty to clear `workingDirectory` on the job.\n */\nconst patchJobWorkingDirectory = z.preprocess((v) => {\n if (v === null) return null;\n if (v === undefined) return undefined;\n if (typeof v !== 'string') return v;\n const t = v.trim();\n return t.length === 0 ? null : t;\n}, z.union([z.string().min(1).max(4096), z.null()]).optional());\n\n/**\n * PATCH: set string, or `null` / empty to clear `agentId` on the job.\n */\nconst patchJobAgentId = z.preprocess((v) => {\n if (v === null) return null;\n if (v === undefined) return undefined;\n if (typeof v !== 'string') return v;\n const t = v.trim();\n return t.length === 0 ? null : t;\n}, z.union([z.string().min(1).max(64), z.null()]).optional());\n\n// CronPayload validation\nconst CronSystemEventPayloadSchema = z.object({\n kind: z.literal('systemEvent'),\n text: z.string().min(1).max(50000),\n});\n\nconst CronAgentTurnPayloadSchema = z.object({\n kind: z.literal('agentTurn'),\n message: z.string().min(1).max(50000),\n model: optionalTrimmedString(100, 1),\n timeoutSeconds: z.number().int().min(10).max(3600).optional(),\n});\n\nconst CronPayloadSchema = z.union([\n CronSystemEventPayloadSchema,\n CronAgentTurnPayloadSchema,\n]);\n\n// CronDelivery validation — channel must match gateway UI / message bus ids (telegram, weixin, cli, local, …)\nconst CronDeliverySchema = z.object({\n mode: CronDeliveryMode.default('none'),\n channel: optionalTrimmedString(32, 1),\n to: z.preprocess(\n (v) => (v === '' || v === null || v === undefined ? undefined : v),\n z.string().max(100).optional(),\n ),\n bestEffort: z.boolean().optional(),\n});\n\nexport const JobDataSchema = z\n .object({\n id: z.string().min(1).max(32),\n name: z.string().max(100).optional(),\n schedule: cronExpression,\n enabled: z.boolean(),\n timezone: z.string().superRefine((val, ctx) => {\n if (val && !validTimezones.includes(val)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid timezone: ${val}. Use IANA timezone names.`,\n });\n }\n }).optional(),\n maxRetries: z.number().int().min(0).max(10).default(3),\n timeout: z.number().int().min(1000).max(300000).default(180000),\n created_at: z.string().datetime(),\n updated_at: z.string().datetime(),\n sessionTarget: z.enum(['main', 'isolated']).optional(),\n agentId: optionalTrimmedString(64, 1),\n workingDirectory: optionalJobWorkingDirectory,\n payload: CronPayloadSchema,\n delivery: CronDeliverySchema.optional(),\n model: optionalTrimmedString(100, 1),\n state: z.any().optional(),\n })\n .strict();\n\nexport const AddJobRequestSchema = z.object({\n schedule: cronExpression,\n name: z.string().max(100).optional(),\n timezone: z.string().optional(),\n maxRetries: z.number().int().min(0).max(10).optional(),\n timeout: z.number().int().min(1000).max(300000).optional(),\n sessionTarget: z.enum(['main', 'isolated']).optional(),\n agentId: optionalJobAgentId,\n workingDirectory: optionalJobWorkingDirectory,\n payload: CronPayloadSchema,\n delivery: CronDeliverySchema.optional(),\n model: optionalTrimmedString(100, 1),\n});\n\nexport const UpdateJobRequestSchema = z.object({\n name: z.string().max(100).optional(),\n schedule: cronExpression.optional(),\n timezone: z.string().optional(),\n maxRetries: z.number().int().min(0).max(10).optional(),\n timeout: z.number().int().min(1000).max(300000).optional(),\n enabled: z.boolean().optional(),\n sessionTarget: z.enum(['main', 'isolated']).optional(),\n agentId: patchJobAgentId,\n workingDirectory: patchJobWorkingDirectory,\n payload: CronPayloadSchema.optional(),\n delivery: CronDeliverySchema.optional(),\n model: optionalTrimmedString(100, 1),\n}).refine(\n (data) => Object.keys(data).length > 0,\n { message: 'At least one field must be provided for update' }\n);\n\nexport type ValidatedJobData = z.infer<typeof JobDataSchema>;\nexport type ValidatedAddJobRequest = z.infer<typeof AddJobRequestSchema>;\nexport type ValidatedUpdateJobRequest = z.infer<typeof UpdateJobRequestSchema>;\nexport type ValidatedCronPayload = z.infer<typeof CronPayloadSchema>;\nexport type ValidatedCronDelivery = z.infer<typeof CronDeliverySchema>;\n\n// Re-export common validation helpers\nexport { cronExpression };\n"],"mappings":"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"}
1
+ {"version":3,"file":"validation.js","names":[],"sources":["../../../src/cron/validation.ts"],"sourcesContent":["// Cron input validation using Zod\nimport { z } from 'zod';\nimport nodeCron from 'node-cron';\n\n// Custom cron validation\nconst cronExpression = z.string().superRefine((val, ctx) => {\n if (!nodeCron.validate(val)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid cron expression: ${val}`,\n });\n }\n});\n\n// Valid timezones (subset of IANA timezones)\nconst validTimezones = [\n 'UTC',\n 'America/New_York',\n 'America/Chicago',\n 'America/Denver',\n 'America/Los_Angeles',\n 'America/Toronto',\n 'Europe/London',\n 'Europe/Paris',\n 'Europe/Berlin',\n 'Europe/Moscow',\n 'Asia/Tokyo',\n 'Asia/Shanghai',\n 'Asia/Hong_Kong',\n 'Asia/Singapore',\n 'Asia/Seoul',\n 'Asia/Dubai',\n 'Asia/Mumbai',\n 'Australia/Sydney',\n 'Pacific/Auckland',\n];\n\nconst CronDeliveryMode = z.enum(['none', 'announce', 'direct']);\n\n/** Treat `''` / `null` / whitespace-only as absent so optional fields do not fail `.min(1)` */\nfunction optionalTrimmedString(max: number, min = 1) {\n return z.preprocess((v) => {\n if (v === null || v === undefined) return undefined;\n if (typeof v !== 'string') return v;\n const t = v.trim();\n return t.length === 0 ? undefined : t;\n }, z.string().min(min).max(max).optional());\n}\n\n/** Optional agent id on create; empty string → undefined. */\nconst optionalJobAgentId = optionalTrimmedString(64, 1);\n\n/** Optional absolute workspace path on create; empty string → undefined. */\nconst optionalJobWorkingDirectory = optionalTrimmedString(4096, 1);\n\n/**\n * PATCH: set string, or `null` / empty to clear `workingDirectory` on the job.\n */\nconst patchJobWorkingDirectory = z.preprocess((v) => {\n if (v === null) return null;\n if (v === undefined) return undefined;\n if (typeof v !== 'string') return v;\n const t = v.trim();\n return t.length === 0 ? null : t;\n}, z.union([z.string().min(1).max(4096), z.null()]).optional());\n\n/**\n * PATCH: set string, or `null` / empty to clear `agentId` on the job.\n */\nconst patchJobAgentId = z.preprocess((v) => {\n if (v === null) return null;\n if (v === undefined) return undefined;\n if (typeof v !== 'string') return v;\n const t = v.trim();\n return t.length === 0 ? null : t;\n}, z.union([z.string().min(1).max(64), z.null()]).optional());\n\n// CronPayload validation\nconst CronSystemEventPayloadSchema = z.object({\n kind: z.literal('systemEvent'),\n text: z.string().min(1).max(50000),\n});\n\nconst CronAgentTurnPayloadSchema = z.object({\n kind: z.literal('agentTurn'),\n message: z.string().min(1).max(50000),\n model: optionalTrimmedString(100, 1),\n timeoutSeconds: z.number().int().min(10).max(3600).optional(),\n});\n\nconst WorkflowRunInputEnvelopeSchema = z.object({\n payload: z.unknown(),\n goal: optionalTrimmedString(5000, 1),\n variables: z.record(z.string(), z.unknown()).optional(),\n context: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst CronWorkflowRunPayloadSchema = z.object({\n kind: z.literal('workflowRun'),\n definitionId: z.string().min(1).max(200),\n input: z.unknown().optional(),\n inputEnvelope: WorkflowRunInputEnvelopeSchema.optional(),\n goal: optionalTrimmedString(5000, 1),\n agentId: optionalTrimmedString(64, 1),\n sessionKey: optionalTrimmedString(300, 1),\n source: z.object({\n kind: z.literal('cron').optional(),\n scheduleId: optionalTrimmedString(200, 1),\n fireId: optionalTrimmedString(200, 1),\n scheduledAtMs: z.number().int().nonnegative().optional(),\n }).optional(),\n});\n\nconst CronPayloadSchema = z.union([\n CronSystemEventPayloadSchema,\n CronAgentTurnPayloadSchema,\n CronWorkflowRunPayloadSchema,\n]);\n\n// CronDelivery validation — channel must match gateway UI / message bus ids (telegram, weixin, cli, local, …)\nconst CronDeliverySchema = z.object({\n mode: CronDeliveryMode.default('none'),\n channel: optionalTrimmedString(32, 1),\n to: z.preprocess(\n (v) => (v === '' || v === null || v === undefined ? undefined : v),\n z.string().max(100).optional(),\n ),\n bestEffort: z.boolean().optional(),\n});\n\nexport const JobDataSchema = z\n .object({\n id: z.string().min(1).max(32),\n name: z.string().max(100).optional(),\n schedule: cronExpression,\n enabled: z.boolean(),\n timezone: z.string().superRefine((val, ctx) => {\n if (val && !validTimezones.includes(val)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid timezone: ${val}. Use IANA timezone names.`,\n });\n }\n }).optional(),\n maxRetries: z.number().int().min(0).max(10).default(3),\n timeout: z.number().int().min(1000).max(300000).default(180000),\n created_at: z.string().datetime(),\n updated_at: z.string().datetime(),\n sessionTarget: z.enum(['main', 'isolated']).optional(),\n agentId: optionalTrimmedString(64, 1),\n workingDirectory: optionalJobWorkingDirectory,\n payload: CronPayloadSchema,\n delivery: CronDeliverySchema.optional(),\n model: optionalTrimmedString(100, 1),\n state: z.any().optional(),\n })\n .strict();\n\nexport const AddJobRequestSchema = z.object({\n schedule: cronExpression,\n name: z.string().max(100).optional(),\n timezone: z.string().optional(),\n maxRetries: z.number().int().min(0).max(10).optional(),\n timeout: z.number().int().min(1000).max(300000).optional(),\n sessionTarget: z.enum(['main', 'isolated']).optional(),\n agentId: optionalJobAgentId,\n workingDirectory: optionalJobWorkingDirectory,\n payload: CronPayloadSchema,\n delivery: CronDeliverySchema.optional(),\n model: optionalTrimmedString(100, 1),\n});\n\nexport const UpdateJobRequestSchema = z.object({\n name: z.string().max(100).optional(),\n schedule: cronExpression.optional(),\n timezone: z.string().optional(),\n maxRetries: z.number().int().min(0).max(10).optional(),\n timeout: z.number().int().min(1000).max(300000).optional(),\n enabled: z.boolean().optional(),\n sessionTarget: z.enum(['main', 'isolated']).optional(),\n agentId: patchJobAgentId,\n workingDirectory: patchJobWorkingDirectory,\n payload: CronPayloadSchema.optional(),\n delivery: CronDeliverySchema.optional(),\n model: optionalTrimmedString(100, 1),\n}).refine(\n (data) => Object.keys(data).length > 0,\n { message: 'At least one field must be provided for update' }\n);\n\nexport type ValidatedJobData = z.infer<typeof JobDataSchema>;\nexport type ValidatedAddJobRequest = z.infer<typeof AddJobRequestSchema>;\nexport type ValidatedUpdateJobRequest = z.infer<typeof UpdateJobRequestSchema>;\nexport type ValidatedCronPayload = z.infer<typeof CronPayloadSchema>;\nexport type ValidatedCronDelivery = z.infer<typeof CronDeliverySchema>;\n\n// Re-export common validation helpers\nexport { cronExpression };\n"],"mappings":"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"}
@@ -2,6 +2,12 @@
2
2
  * Gateway REST helpers for multi-agent management.
3
3
  */
4
4
  import type { Config } from '../config/schema.js';
5
+ import type { AgentTypedModel } from '../config/schema.js';
6
+ export type GatewayAgentTypedModelsInfo = {
7
+ defaults: AgentTypedModel[];
8
+ entry?: AgentTypedModel[];
9
+ effective: AgentTypedModel[];
10
+ };
5
11
  export type GatewayAgentRow = {
6
12
  id: string;
7
13
  name?: string;
@@ -15,6 +21,7 @@ export type GatewayAgentRow = {
15
21
  primary?: string;
16
22
  fallbacks?: string[];
17
23
  };
24
+ typedModels: GatewayAgentTypedModelsInfo;
18
25
  isDefault: boolean;
19
26
  skills: {
20
27
  defaults: string[];
@@ -85,6 +92,8 @@ export type UpdateAgentBody = {
85
92
  skills?: string[] | null;
86
93
  /** Replace `agents.list[].tools.disable`; `null` clears entry-level disables. */
87
94
  toolsDisable?: string[] | null;
95
+ /** Replace `agents.list[].models`; `null` removes entry overrides (inherit defaults). */
96
+ models?: AgentTypedModel[] | null;
88
97
  };
89
98
  export declare function prepareUpdateAgent(cfg: Config, agentIdRaw: string, body: UpdateAgentBody): AgentAdminResult<{
90
99
  nextConfig: Config;
@@ -4,8 +4,10 @@ import { WORKSPACE_FILES, init_paths } from "../config/paths.js";
4
4
  import { AGENT_PROFILE_MARKDOWN_SYSTEM_FILES } from "../agent/context/workspace.js";
5
5
  import { isPathUnderWorkspace, resolveWorkspaceSafePath } from "./workspace-editor-path.js";
6
6
  import { applyAgentConfig, findAgentEntryIndex, pruneAgentConfig, removeAgentDirsFromDisk } from "../commands/agents.config.js";
7
+ import { resolveEffectiveTypedModels } from "../config/agent-typed-models.js";
7
8
  import { GATEWAY_BUILTIN_TOOL_IDS } from "./agent-builtin-tools.js";
8
9
  import { seedAgentProfileMarkdownFiles } from "../agent/context/workspace-seed.js";
10
+ import { normalizePatchTypedModels } from "./hono/lib/agent-model.js";
9
11
  import { join, resolve } from "node:path";
10
12
  import { mkdir, readFile, realpath, stat, unlink, writeFile } from "node:fs/promises";
11
13
  //#region src/gateway/agents-admin.ts
@@ -39,6 +41,7 @@ async function listGatewayAgents(cfg) {
39
41
  const agents = [];
40
42
  const defaultsSkills = cfg.agents?.defaults?.skills;
41
43
  const defaultsDisable = cfg.agents?.defaults?.tools?.disable ?? [];
44
+ const defaultsTypedModels = cfg.agents?.defaults?.models ?? [];
42
45
  for (const id of collectAgentIdsForList(cfg)) {
43
46
  const profile = resolveEffectiveAgentProfile(cfg, id);
44
47
  const entry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id);
@@ -48,6 +51,8 @@ async function listGatewayAgents(cfg) {
48
51
  } : void 0;
49
52
  const entrySkills = entry?.skills;
50
53
  const entryDisable = entry?.tools?.disable ?? [];
54
+ const entryTypedModels = entry?.models;
55
+ const effectiveTypedModels = [...resolveEffectiveTypedModels(cfg, id).values()];
51
56
  let avatar;
52
57
  try {
53
58
  avatar = extractAvatarFromIdentityMarkdown(await readFile(join(resolveAgentProfileDir(cfg, id), WORKSPACE_FILES.IDENTITY), "utf-8"));
@@ -60,6 +65,11 @@ async function listGatewayAgents(cfg) {
60
65
  workspace: profile.resolvedWorkspacePath,
61
66
  profileDir: resolveAgentProfileDir(cfg, id),
62
67
  ...model ? { model } : {},
68
+ typedModels: {
69
+ defaults: [...defaultsTypedModels],
70
+ ...entryTypedModels !== void 0 ? { entry: [...entryTypedModels] } : {},
71
+ effective: effectiveTypedModels
72
+ },
63
73
  isDefault: id === defaultId,
64
74
  skills: {
65
75
  defaults: defaultsSkills ? [...defaultsSkills] : [],
@@ -268,6 +278,12 @@ function prepareUpdateAgent(cfg, agentIdRaw, body) {
268
278
  disable: next
269
279
  };
270
280
  }
281
+ if (body.models !== void 0) if (body.models === null) delete entry.models;
282
+ else {
283
+ const normalized = normalizePatchTypedModels(body.models);
284
+ if (normalized === null || normalized === void 0) delete entry.models;
285
+ else entry.models = normalized;
286
+ }
271
287
  list[idx] = entry;
272
288
  let next = {
273
289
  ...cfg,
@@ -1 +1 @@
1
- {"version":3,"file":"agents-admin.js","names":["pathResolve"],"sources":["../../../src/gateway/agents-admin.ts"],"sourcesContent":["/**\n * Gateway REST helpers for multi-agent management.\n */\n\nimport { mkdir, 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 applyAgentConfig,\n findAgentEntryIndex,\n pruneAgentConfig,\n removeAgentDirsFromDisk,\n} from '../commands/agents.config.js';\nimport type { Config } from '../config/schema.js';\nimport { WORKSPACE_FILES } from '../config/paths.js';\nimport { resolveEffectiveAgentProfile } from '../config/agent-profile.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>([\n ...AGENT_PROFILE_MARKDOWN_SYSTEM_FILES,\n WORKSPACE_FILES.BOOTSTRAP,\n]);\n\nexport type GatewayAgentRow = {\n id: string;\n name?: string;\n description?: 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 isDefault: boolean;\n skills: {\n defaults: string[];\n entry?: string[];\n effectiveAllowlist?: string[];\n };\n tools: {\n defaultsDisable: string[];\n entryDisable: string[];\n effectiveDisable: string[];\n };\n};\n\nexport type GatewayAgentsListResponse = {\n defaultId: string;\n agents: GatewayAgentRow[];\n builtinToolIds: string[];\n};\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\n/** Extract `**Avatar:**` value from profile IDENTITY.md (same line shape as the gateway console parser). */\nexport function extractAvatarFromIdentityMarkdown(content: string): string | undefined {\n for (const line of content.split('\\n')) {\n const match = line.match(/^[-*]\\s+\\*\\*Avatar:\\*\\*\\s*(.*)/i);\n if (match) {\n const v = match[1]?.trim() ?? '';\n return v.length > 0 ? v : undefined;\n }\n }\n return undefined;\n}\n\nexport async function listGatewayAgents(cfg: Config): Promise<GatewayAgentsListResponse> {\n const defaultId = resolveDefaultAgentId(cfg);\n const agents: GatewayAgentRow[] = [];\n const defaultsSkills = cfg.agents?.defaults?.skills;\n const defaultsDisable = cfg.agents?.defaults?.tools?.disable ?? [];\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;\n const entryDisable = entry?.tools?.disable ?? [];\n let avatar: string | undefined;\n try {\n const identityPath = join(resolveAgentProfileDir(cfg, id), WORKSPACE_FILES.IDENTITY);\n const content = await readFile(identityPath, 'utf-8');\n avatar = extractAvatarFromIdentityMarkdown(content);\n } catch {\n /* missing IDENTITY.md or unreadable */\n }\n agents.push({\n id,\n ...(entry?.name?.trim() ? { name: entry.name.trim() } : {}),\n ...(entry?.description?.trim() ? { description: entry.description.trim() } : {}),\n ...(avatar ? { avatar } : {}),\n workspace: profile.resolvedWorkspacePath,\n profileDir: resolveAgentProfileDir(cfg, id),\n ...(model ? { model } : {}),\n isDefault: id === defaultId,\n skills: {\n defaults: defaultsSkills ? [...defaultsSkills] : [],\n ...(entrySkills !== undefined ? { entry: [...entrySkills] } : {}),\n ...(profile.skillsAllowlist !== undefined\n ? { effectiveAllowlist: [...profile.skillsAllowlist] }\n : {}),\n },\n tools: {\n defaultsDisable: [...defaultsDisable],\n entryDisable: [...entryDisable],\n effectiveDisable: [...profile.tools.disable].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 type CreateAgentBody = {\n /** Display name stored on the agent entry. */\n name: string;\n /** Optional id seed; normalized agent id defaults from `name` when omitted. */\n id?: string;\n workspace: string;\n model?: string;\n agentDir?: string;\n description?: string;\n /** Initial `agents.list[].tools.disable` for the new entry. */\n toolsDisable?: string[];\n /** Profile markdown files to write after seeding (e.g. `IDENTITY.md`, `SOUL.md`). */\n profileFiles?: Record<string, 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 name = body.name?.trim() ?? '';\n if (!name) {\n return { ok: false, error: 'name is required', status: 400 };\n }\n const workspace = body.workspace?.trim() ?? '';\n if (!workspace) {\n return { ok: false, error: 'workspace is required', status: 400 };\n }\n const idRes = validateAgentIdForNewAgent(body.id, name);\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 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\n const wsAbs = resolveUserPath(workspace);\n let next = applyAgentConfig(cfg, {\n agentId,\n name,\n workspace: wsAbs,\n ...(body.model?.trim() ? { model: body.model.trim() } : {}),\n ...(body.agentDir?.trim() ? { agentDir: body.agentDir.trim() } : {}),\n ...(body.description?.trim() ? { description: body.description.trim() } : {}),\n });\n\n if (body.toolsDisable !== undefined) {\n const list = [...listAgentEntries(next)];\n const idx = findAgentEntryIndex(list, agentId);\n if (idx >= 0) {\n type Entry = (typeof list)[number];\n const entry: Entry = { ...list[idx] };\n const disable = body.toolsDisable.map((s) => String(s).trim()).filter(Boolean);\n entry.tools = { ...entry.tools, disable };\n list[idx] = entry;\n next = {\n ...next,\n agents: {\n ...next.agents,\n list,\n },\n };\n }\n }\n\n return { ok: true, data: { nextConfig: next, agentId, workspace: wsAbs } };\n}\n\nexport type PreparedBatchCreateAgent = {\n agentId: string;\n profileFiles?: Record<string, string>;\n};\n\nexport function prepareCreateAgentsBatch(\n cfg: Config,\n bodies: CreateAgentBody[],\n): AgentAdminResult<{ nextConfig: Config; created: PreparedBatchCreateAgent[] }> {\n if (!Array.isArray(bodies) || bodies.length === 0) {\n return { ok: false, error: 'agents must be a non-empty array', status: 400 };\n }\n let next = cfg;\n const created: PreparedBatchCreateAgent[] = [];\n for (const body of bodies) {\n const prep = prepareCreateAgent(next, body);\n if (prep.ok === false) {\n return prep;\n }\n next = prep.data.nextConfig;\n created.push({\n agentId: prep.data.agentId,\n ...(body.profileFiles !== undefined ? { profileFiles: body.profileFiles } : {}),\n });\n }\n return { ok: true, data: { nextConfig: next, created } };\n}\n\nexport async function finalizeCreateAgentDirs(\n cfg: Config,\n agentId: string,\n opts?: { profileFiles?: Record<string, 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 entry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id);\n const displayName = entry?.name?.trim() || id;\n seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });\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 name?: string;\n description?: string | null;\n workspace?: string;\n model?: string | null;\n agentDir?: string | null;\n setDefault?: boolean;\n /** Replace `agents.list[].skills`; `null` removes the key (inherit defaults). */\n skills?: string[] | null;\n /** Replace `agents.list[].tools.disable`; `null` clears entry-level disables. */\n toolsDisable?: string[] | null;\n};\n\nexport function prepareUpdateAgent(\n cfg: Config,\n agentIdRaw: string,\n body: UpdateAgentBody,\n): AgentAdminResult<{ nextConfig: Config }> {\n const agentId = normalizeAgentId(agentIdRaw);\n let list = [...listAgentEntries(cfg)];\n let idx = findAgentEntryIndex(list, agentId);\n if (idx < 0 && agentId === resolveDefaultAgentId(cfg)) {\n list = [...list, { id: agentId, enabled: true as const }];\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.name !== undefined) {\n const n = body.name.trim();\n if (n) {\n entry.name = n;\n }\n }\n if (body.description !== undefined) {\n if (body.description === null || String(body.description).trim() === '') {\n delete entry.description;\n } else {\n entry.description = String(body.description).trim();\n }\n }\n if (body.workspace !== undefined) {\n const w = body.workspace.trim();\n if (w) {\n entry.workspace = resolveUserPath(w);\n }\n }\n if (body.model !== undefined) {\n if (body.model === null) {\n delete entry.model;\n } else {\n const trimmed = String(body.model).trim();\n if (!trimmed) {\n return { ok: false, error: 'model must be a non-empty string or null', status: 400 };\n }\n entry.model = { primary: trimmed };\n }\n }\n if (body.agentDir !== undefined) {\n if (body.agentDir === null || String(body.agentDir).trim() === '') {\n delete entry.agentDir;\n } else {\n entry.agentDir = String(body.agentDir).trim();\n }\n }\n\n if (body.skills !== undefined) {\n if (body.skills === null) {\n delete entry.skills;\n } else {\n const next = body.skills.map((s) => String(s).trim()).filter(Boolean);\n if (next.length === 0) {\n entry.skills = [];\n } else {\n entry.skills = next;\n }\n }\n }\n\n if (body.toolsDisable !== undefined) {\n if (body.toolsDisable === null) {\n if (entry.tools) {\n delete entry.tools.disable;\n if (Object.keys(entry.tools).length === 0) {\n delete entry.tools;\n }\n }\n } else {\n const next = body.toolsDisable.map((s) => String(s).trim()).filter(Boolean);\n entry.tools = { ...entry.tools, disable: next };\n }\n }\n\n list[idx] = entry;\n let next: Config = {\n ...cfg,\n agents: {\n ...cfg.agents,\n list,\n },\n };\n\n if (body.setDefault === true) {\n next = {\n ...next,\n agents: {\n ...next.agents,\n default: agentId,\n },\n };\n }\n return { ok: true, data: { nextConfig: next } };\n}\n\nexport function prepareDeleteAgent(\n cfg: Config,\n agentIdRaw: string,\n): AgentAdminResult<{ nextConfig: Config; agentId: string }> {\n const agentId = normalizeAgentId(agentIdRaw);\n const 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 { mkdir, 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 applyAgentConfig,\n findAgentEntryIndex,\n pruneAgentConfig,\n removeAgentDirsFromDisk,\n} from '../commands/agents.config.js';\nimport type { Config } from '../config/schema.js';\nimport { WORKSPACE_FILES } from '../config/paths.js';\nimport { resolveEffectiveAgentProfile } from '../config/agent-profile.js';\nimport { resolveEffectiveTypedModels } from '../config/agent-typed-models.js';\nimport type { AgentTypedModel } from '../config/schema.js';\nimport { normalizePatchTypedModels } from './hono/lib/agent-model.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>([\n ...AGENT_PROFILE_MARKDOWN_SYSTEM_FILES,\n WORKSPACE_FILES.BOOTSTRAP,\n]);\n\nexport type GatewayAgentTypedModelsInfo = {\n defaults: AgentTypedModel[];\n entry?: AgentTypedModel[];\n effective: AgentTypedModel[];\n};\n\nexport type GatewayAgentRow = {\n id: string;\n name?: string;\n description?: 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 isDefault: boolean;\n skills: {\n defaults: string[];\n entry?: string[];\n effectiveAllowlist?: string[];\n };\n tools: {\n defaultsDisable: string[];\n entryDisable: string[];\n effectiveDisable: string[];\n };\n};\n\nexport type GatewayAgentsListResponse = {\n defaultId: string;\n agents: GatewayAgentRow[];\n builtinToolIds: string[];\n};\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\n/** Extract `**Avatar:**` value from profile IDENTITY.md (same line shape as the gateway console parser). */\nexport function extractAvatarFromIdentityMarkdown(content: string): string | undefined {\n for (const line of content.split('\\n')) {\n const match = line.match(/^[-*]\\s+\\*\\*Avatar:\\*\\*\\s*(.*)/i);\n if (match) {\n const v = match[1]?.trim() ?? '';\n return v.length > 0 ? v : undefined;\n }\n }\n return undefined;\n}\n\nexport async function listGatewayAgents(cfg: Config): Promise<GatewayAgentsListResponse> {\n const defaultId = resolveDefaultAgentId(cfg);\n const agents: GatewayAgentRow[] = [];\n const defaultsSkills = cfg.agents?.defaults?.skills;\n const defaultsDisable = cfg.agents?.defaults?.tools?.disable ?? [];\n const defaultsTypedModels = cfg.agents?.defaults?.models ?? [];\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;\n const entryDisable = entry?.tools?.disable ?? [];\n const entryTypedModels = entry?.models;\n const effectiveTypedMap = resolveEffectiveTypedModels(cfg, id);\n const effectiveTypedModels = [...effectiveTypedMap.values()];\n let avatar: string | undefined;\n try {\n const identityPath = join(resolveAgentProfileDir(cfg, id), WORKSPACE_FILES.IDENTITY);\n const content = await readFile(identityPath, 'utf-8');\n avatar = extractAvatarFromIdentityMarkdown(content);\n } catch {\n /* missing IDENTITY.md or unreadable */\n }\n agents.push({\n id,\n ...(entry?.name?.trim() ? { name: entry.name.trim() } : {}),\n ...(entry?.description?.trim() ? { description: entry.description.trim() } : {}),\n ...(avatar ? { avatar } : {}),\n workspace: profile.resolvedWorkspacePath,\n profileDir: resolveAgentProfileDir(cfg, id),\n ...(model ? { model } : {}),\n typedModels: {\n defaults: [...defaultsTypedModels],\n ...(entryTypedModels !== undefined ? { entry: [...entryTypedModels] } : {}),\n effective: effectiveTypedModels,\n },\n isDefault: id === defaultId,\n skills: {\n defaults: defaultsSkills ? [...defaultsSkills] : [],\n ...(entrySkills !== undefined ? { entry: [...entrySkills] } : {}),\n ...(profile.skillsAllowlist !== undefined\n ? { effectiveAllowlist: [...profile.skillsAllowlist] }\n : {}),\n },\n tools: {\n defaultsDisable: [...defaultsDisable],\n entryDisable: [...entryDisable],\n effectiveDisable: [...profile.tools.disable].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 type CreateAgentBody = {\n /** Display name stored on the agent entry. */\n name: string;\n /** Optional id seed; normalized agent id defaults from `name` when omitted. */\n id?: string;\n workspace: string;\n model?: string;\n agentDir?: string;\n description?: string;\n /** Initial `agents.list[].tools.disable` for the new entry. */\n toolsDisable?: string[];\n /** Profile markdown files to write after seeding (e.g. `IDENTITY.md`, `SOUL.md`). */\n profileFiles?: Record<string, 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 name = body.name?.trim() ?? '';\n if (!name) {\n return { ok: false, error: 'name is required', status: 400 };\n }\n const workspace = body.workspace?.trim() ?? '';\n if (!workspace) {\n return { ok: false, error: 'workspace is required', status: 400 };\n }\n const idRes = validateAgentIdForNewAgent(body.id, name);\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 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\n const wsAbs = resolveUserPath(workspace);\n let next = applyAgentConfig(cfg, {\n agentId,\n name,\n workspace: wsAbs,\n ...(body.model?.trim() ? { model: body.model.trim() } : {}),\n ...(body.agentDir?.trim() ? { agentDir: body.agentDir.trim() } : {}),\n ...(body.description?.trim() ? { description: body.description.trim() } : {}),\n });\n\n if (body.toolsDisable !== undefined) {\n const list = [...listAgentEntries(next)];\n const idx = findAgentEntryIndex(list, agentId);\n if (idx >= 0) {\n type Entry = (typeof list)[number];\n const entry: Entry = { ...list[idx] };\n const disable = body.toolsDisable.map((s) => String(s).trim()).filter(Boolean);\n entry.tools = { ...entry.tools, disable };\n list[idx] = entry;\n next = {\n ...next,\n agents: {\n ...next.agents,\n list,\n },\n };\n }\n }\n\n return { ok: true, data: { nextConfig: next, agentId, workspace: wsAbs } };\n}\n\nexport type PreparedBatchCreateAgent = {\n agentId: string;\n profileFiles?: Record<string, string>;\n};\n\nexport function prepareCreateAgentsBatch(\n cfg: Config,\n bodies: CreateAgentBody[],\n): AgentAdminResult<{ nextConfig: Config; created: PreparedBatchCreateAgent[] }> {\n if (!Array.isArray(bodies) || bodies.length === 0) {\n return { ok: false, error: 'agents must be a non-empty array', status: 400 };\n }\n let next = cfg;\n const created: PreparedBatchCreateAgent[] = [];\n for (const body of bodies) {\n const prep = prepareCreateAgent(next, body);\n if (prep.ok === false) {\n return prep;\n }\n next = prep.data.nextConfig;\n created.push({\n agentId: prep.data.agentId,\n ...(body.profileFiles !== undefined ? { profileFiles: body.profileFiles } : {}),\n });\n }\n return { ok: true, data: { nextConfig: next, created } };\n}\n\nexport async function finalizeCreateAgentDirs(\n cfg: Config,\n agentId: string,\n opts?: { profileFiles?: Record<string, 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 entry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id);\n const displayName = entry?.name?.trim() || id;\n seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });\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 name?: string;\n description?: string | null;\n workspace?: string;\n model?: string | null;\n agentDir?: string | null;\n setDefault?: boolean;\n /** Replace `agents.list[].skills`; `null` removes the key (inherit defaults). */\n skills?: string[] | null;\n /** Replace `agents.list[].tools.disable`; `null` clears entry-level disables. */\n toolsDisable?: string[] | null;\n /** Replace `agents.list[].models`; `null` removes entry overrides (inherit defaults). */\n models?: AgentTypedModel[] | null;\n};\n\nexport function prepareUpdateAgent(\n cfg: Config,\n agentIdRaw: string,\n body: UpdateAgentBody,\n): AgentAdminResult<{ nextConfig: Config }> {\n const agentId = normalizeAgentId(agentIdRaw);\n let list = [...listAgentEntries(cfg)];\n let idx = findAgentEntryIndex(list, agentId);\n if (idx < 0 && agentId === resolveDefaultAgentId(cfg)) {\n list = [...list, { id: agentId, enabled: true as const }];\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.name !== undefined) {\n const n = body.name.trim();\n if (n) {\n entry.name = n;\n }\n }\n if (body.description !== undefined) {\n if (body.description === null || String(body.description).trim() === '') {\n delete entry.description;\n } else {\n entry.description = String(body.description).trim();\n }\n }\n if (body.workspace !== undefined) {\n const w = body.workspace.trim();\n if (w) {\n entry.workspace = resolveUserPath(w);\n }\n }\n if (body.model !== undefined) {\n if (body.model === null) {\n delete entry.model;\n } else {\n const trimmed = String(body.model).trim();\n if (!trimmed) {\n return { ok: false, error: 'model must be a non-empty string or null', status: 400 };\n }\n entry.model = { primary: trimmed };\n }\n }\n if (body.agentDir !== undefined) {\n if (body.agentDir === null || String(body.agentDir).trim() === '') {\n delete entry.agentDir;\n } else {\n entry.agentDir = String(body.agentDir).trim();\n }\n }\n\n if (body.skills !== undefined) {\n if (body.skills === null) {\n delete entry.skills;\n } else {\n const next = body.skills.map((s) => String(s).trim()).filter(Boolean);\n if (next.length === 0) {\n entry.skills = [];\n } else {\n entry.skills = next;\n }\n }\n }\n\n if (body.toolsDisable !== undefined) {\n if (body.toolsDisable === null) {\n if (entry.tools) {\n delete entry.tools.disable;\n if (Object.keys(entry.tools).length === 0) {\n delete entry.tools;\n }\n }\n } else {\n const next = body.toolsDisable.map((s) => String(s).trim()).filter(Boolean);\n entry.tools = { ...entry.tools, disable: next };\n }\n }\n\n if (body.models !== undefined) {\n if (body.models === null) {\n delete entry.models;\n } else {\n const normalized = normalizePatchTypedModels(body.models);\n if (normalized === null || normalized === undefined) {\n delete entry.models;\n } else {\n entry.models = normalized;\n }\n }\n }\n\n list[idx] = entry;\n let next: Config = {\n ...cfg,\n agents: {\n ...cfg.agents,\n list,\n },\n };\n\n if (body.setDefault === true) {\n next = {\n ...next,\n agents: {\n ...next.agents,\n default: agentId,\n },\n };\n }\n return { ok: true, data: { nextConfig: next } };\n}\n\nexport function prepareDeleteAgent(\n cfg: Config,\n agentIdRaw: string,\n): AgentAdminResult<{ nextConfig: Config; agentId: string }> {\n const agentId = normalizeAgentId(agentIdRaw);\n const 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,13 +1,14 @@
1
+ import { isMaskedSecretPatchValue, maskSecretLength } from "./hono/lib/mask-secret-length.js";
1
2
  //#region src/gateway/config-tools-web.ts
2
3
  function isMaskedApiKey(v) {
3
- return v === "***" || v === "••••••••••••";
4
+ return typeof v === "string" && isMaskedSecretPatchValue(v);
4
5
  }
5
6
  function safeToolsWebForGet(config) {
6
7
  const web = config.tools?.web;
7
8
  const search = web?.search;
8
9
  const providersOut = (search?.providers ?? []).map((p) => ({
9
10
  type: p.type,
10
- apiKey: p.apiKey && p.apiKey.trim().length > 0 ? "***" : "",
11
+ apiKey: p.apiKey && p.apiKey.trim().length > 0 ? maskSecretLength(p.apiKey) : "",
11
12
  url: typeof p.url === "string" ? p.url : "",
12
13
  disabled: Boolean(p.disabled)
13
14
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"config-tools-web.js","names":[],"sources":["../../../src/gateway/config-tools-web.ts"],"sourcesContent":["import type { Config, SearchProviderEntry } from '../config/schema.js';\n\nexport function isMaskedApiKey(v: unknown): boolean {\n return v === '***' || v === '••••••••••••';\n}\n\nexport function safeToolsWebForGet(config: Config): {\n web: {\n region: 'cn' | 'global' | null;\n search: {\n maxResults: number;\n providers: Array<{ type: string; apiKey: string; url: string; disabled: boolean }>;\n };\n blocklist: {\n enabled: boolean;\n domains: string[];\n };\n };\n} {\n const web = config.tools?.web;\n const search = web?.search;\n const providers = search?.providers ?? [];\n const providersOut = providers.map((p) => ({\n type: p.type,\n apiKey: p.apiKey && p.apiKey.trim().length > 0 ? '***' : '',\n url: typeof p.url === 'string' ? p.url : '',\n disabled: Boolean(p.disabled),\n }));\n const blocklist = web?.blocklist;\n return {\n web: {\n region: web?.region ?? null,\n search: {\n maxResults: search?.maxResults ?? 5,\n providers: providersOut,\n },\n blocklist: {\n enabled: blocklist?.enabled === true,\n domains: Array.isArray(blocklist?.domains)\n ? blocklist.domains.filter((d): d is string => typeof d === 'string' && d.trim().length > 0)\n : [],\n },\n },\n };\n}\n\n/**\n * Merge `body.tools.web` into config. Returns an error message on validation failure.\n */\nexport function applyToolsWebPatch(config: Config, body: Record<string, unknown>): string | undefined {\n const tools = body.tools;\n if (!tools || typeof tools !== 'object') return undefined;\n const tw = (tools as { web?: unknown }).web;\n if (!tw || typeof tw !== 'object') return undefined;\n\n const incoming = tw as Record<string, unknown>;\n\n if (!config.tools) {\n config.tools = { web: {} };\n }\n if (!config.tools.web) {\n config.tools.web = {};\n }\n if (!config.tools.web.search) {\n config.tools.web.search = { maxResults: 5, providers: [] };\n }\n\n const prevSearch = config.tools.web.search;\n const prevProviders = prevSearch.providers;\n\n if (incoming.region !== undefined) {\n if (incoming.region === null || incoming.region === '' || incoming.region === 'auto') {\n delete config.tools.web.region;\n } else if (incoming.region === 'cn' || incoming.region === 'global') {\n config.tools.web.region = incoming.region;\n } else {\n return 'Invalid tools.web.region';\n }\n }\n\n if (incoming.blocklist !== undefined) {\n if (incoming.blocklist === null) {\n delete config.tools.web.blocklist;\n } else if (typeof incoming.blocklist !== 'object') {\n return 'Invalid tools.web.blocklist';\n } else {\n const bl = incoming.blocklist as Record<string, unknown>;\n const next = {\n ...(config.tools.web.blocklist ?? { enabled: false, domains: [] }),\n };\n if (bl.enabled !== undefined) {\n next.enabled = Boolean(bl.enabled);\n }\n if (bl.domains !== undefined) {\n if (!Array.isArray(bl.domains)) {\n return 'tools.web.blocklist.domains must be an array';\n }\n next.domains = bl.domains\n .filter((d): d is string => typeof d === 'string' && d.trim().length > 0)\n .map((d) => d.trim());\n }\n config.tools.web.blocklist = next;\n }\n }\n\n const sr = incoming.search;\n if (sr === undefined) return undefined;\n if (typeof sr !== 'object' || sr === null) {\n return 'Invalid tools.web.search';\n }\n const s = sr as Record<string, unknown>;\n\n if (s.maxResults !== undefined) {\n const n = typeof s.maxResults === 'number' ? s.maxResults : Number(s.maxResults);\n if (!Number.isFinite(n) || n < 1 || n > 50) {\n return 'tools.web.search.maxResults must be between 1 and 50';\n }\n config.tools.web.search.maxResults = Math.floor(n);\n }\n\n if (!('providers' in s)) {\n return undefined;\n }\n\n if (!Array.isArray(s.providers)) {\n return 'tools.web.search.providers must be an array';\n }\n\n const merged: SearchProviderEntry[] = [];\n for (let i = 0; i < s.providers.length; i++) {\n const rawRow = s.providers[i];\n if (!rawRow || typeof rawRow !== 'object') {\n return 'Invalid search provider entry';\n }\n const row = rawRow as Record<string, unknown>;\n const type = row.type;\n if (type !== 'brave' && type !== 'tavily' && type !== 'bing' && type !== 'searxng') {\n return `Invalid search provider type: ${String(type)}`;\n }\n const prev = prevProviders?.[i];\n let apiKey = typeof row.apiKey === 'string' ? row.apiKey : '';\n if (isMaskedApiKey(apiKey) && prev?.apiKey) {\n apiKey = prev.apiKey;\n }\n const urlRaw = typeof row.url === 'string' ? row.url.trim().replace(/\\/+$/, '') : '';\n const entry: SearchProviderEntry = { type };\n if (type === 'searxng') {\n if (urlRaw) entry.url = urlRaw;\n if (apiKey) entry.apiKey = apiKey;\n } else {\n if (apiKey) entry.apiKey = apiKey;\n }\n if (row.disabled === true) {\n entry.disabled = true;\n }\n merged.push(entry);\n }\n config.tools.web.search.providers = merged;\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config-tools-web.js","names":[],"sources":["../../../src/gateway/config-tools-web.ts"],"sourcesContent":["import type { Config, SearchProviderEntry } from '../config/schema.js';\nimport {\n isMaskedSecretPatchValue,\n maskSecretLength,\n} from './hono/lib/mask-secret-length.js';\n\nexport function isMaskedApiKey(v: unknown): boolean {\n return typeof v === 'string' && isMaskedSecretPatchValue(v);\n}\n\nexport function safeToolsWebForGet(config: Config): {\n web: {\n region: 'cn' | 'global' | null;\n search: {\n maxResults: number;\n providers: Array<{ type: string; apiKey: string; url: string; disabled: boolean }>;\n };\n blocklist: {\n enabled: boolean;\n domains: string[];\n };\n };\n} {\n const web = config.tools?.web;\n const search = web?.search;\n const providers = search?.providers ?? [];\n const providersOut = providers.map((p) => ({\n type: p.type,\n apiKey: p.apiKey && p.apiKey.trim().length > 0 ? maskSecretLength(p.apiKey) : '',\n url: typeof p.url === 'string' ? p.url : '',\n disabled: Boolean(p.disabled),\n }));\n const blocklist = web?.blocklist;\n return {\n web: {\n region: web?.region ?? null,\n search: {\n maxResults: search?.maxResults ?? 5,\n providers: providersOut,\n },\n blocklist: {\n enabled: blocklist?.enabled === true,\n domains: Array.isArray(blocklist?.domains)\n ? blocklist.domains.filter((d): d is string => typeof d === 'string' && d.trim().length > 0)\n : [],\n },\n },\n };\n}\n\n/**\n * Merge `body.tools.web` into config. Returns an error message on validation failure.\n */\nexport function applyToolsWebPatch(config: Config, body: Record<string, unknown>): string | undefined {\n const tools = body.tools;\n if (!tools || typeof tools !== 'object') return undefined;\n const tw = (tools as { web?: unknown }).web;\n if (!tw || typeof tw !== 'object') return undefined;\n\n const incoming = tw as Record<string, unknown>;\n\n if (!config.tools) {\n config.tools = { web: {} };\n }\n if (!config.tools.web) {\n config.tools.web = {};\n }\n if (!config.tools.web.search) {\n config.tools.web.search = { maxResults: 5, providers: [] };\n }\n\n const prevSearch = config.tools.web.search;\n const prevProviders = prevSearch.providers;\n\n if (incoming.region !== undefined) {\n if (incoming.region === null || incoming.region === '' || incoming.region === 'auto') {\n delete config.tools.web.region;\n } else if (incoming.region === 'cn' || incoming.region === 'global') {\n config.tools.web.region = incoming.region;\n } else {\n return 'Invalid tools.web.region';\n }\n }\n\n if (incoming.blocklist !== undefined) {\n if (incoming.blocklist === null) {\n delete config.tools.web.blocklist;\n } else if (typeof incoming.blocklist !== 'object') {\n return 'Invalid tools.web.blocklist';\n } else {\n const bl = incoming.blocklist as Record<string, unknown>;\n const next = {\n ...(config.tools.web.blocklist ?? { enabled: false, domains: [] }),\n };\n if (bl.enabled !== undefined) {\n next.enabled = Boolean(bl.enabled);\n }\n if (bl.domains !== undefined) {\n if (!Array.isArray(bl.domains)) {\n return 'tools.web.blocklist.domains must be an array';\n }\n next.domains = bl.domains\n .filter((d): d is string => typeof d === 'string' && d.trim().length > 0)\n .map((d) => d.trim());\n }\n config.tools.web.blocklist = next;\n }\n }\n\n const sr = incoming.search;\n if (sr === undefined) return undefined;\n if (typeof sr !== 'object' || sr === null) {\n return 'Invalid tools.web.search';\n }\n const s = sr as Record<string, unknown>;\n\n if (s.maxResults !== undefined) {\n const n = typeof s.maxResults === 'number' ? s.maxResults : Number(s.maxResults);\n if (!Number.isFinite(n) || n < 1 || n > 50) {\n return 'tools.web.search.maxResults must be between 1 and 50';\n }\n config.tools.web.search.maxResults = Math.floor(n);\n }\n\n if (!('providers' in s)) {\n return undefined;\n }\n\n if (!Array.isArray(s.providers)) {\n return 'tools.web.search.providers must be an array';\n }\n\n const merged: SearchProviderEntry[] = [];\n for (let i = 0; i < s.providers.length; i++) {\n const rawRow = s.providers[i];\n if (!rawRow || typeof rawRow !== 'object') {\n return 'Invalid search provider entry';\n }\n const row = rawRow as Record<string, unknown>;\n const type = row.type;\n if (type !== 'brave' && type !== 'tavily' && type !== 'bing' && type !== 'searxng') {\n return `Invalid search provider type: ${String(type)}`;\n }\n const prev = prevProviders?.[i];\n let apiKey = typeof row.apiKey === 'string' ? row.apiKey : '';\n if (isMaskedApiKey(apiKey) && prev?.apiKey) {\n apiKey = prev.apiKey;\n }\n const urlRaw = typeof row.url === 'string' ? row.url.trim().replace(/\\/+$/, '') : '';\n const entry: SearchProviderEntry = { type };\n if (type === 'searxng') {\n if (urlRaw) entry.url = urlRaw;\n if (apiKey) entry.apiKey = apiKey;\n } else {\n if (apiKey) entry.apiKey = apiKey;\n }\n if (row.disabled === true) {\n entry.disabled = true;\n }\n merged.push(entry);\n }\n config.tools.web.search.providers = merged;\n return undefined;\n}\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Config } from '../config/schema.js';
2
+ import type { MessageBus } from '../infra/bus/index.js';
3
+ import type { SessionStore } from '../session/store.js';
4
+ export interface GatewayWorkflowAgentSurface {
5
+ getModelForSession(sessionKey: string): string;
6
+ }
7
+ /** Minimal gateway surface for workflow run + session bridge (breaks circular imports). */
8
+ export interface GatewayWorkflowHost {
9
+ readonly currentConfig: Config;
10
+ readonly currentWorkspacePath: string;
11
+ readonly messageBusInstance: MessageBus;
12
+ readonly agentService: GatewayWorkflowAgentSurface;
13
+ emit(event: string, payload: unknown): void;
14
+ readonly sessionIndexInstance: {
15
+ getStore(): SessionStore;
16
+ };
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,10 @@
1
+ import type { AgentTypedModel } from '../../../config/schema.js';
2
+ /**
3
+ * Coerce PATCH body `models[]` into validated typed model entries.
4
+ * Returns `null` to clear, `undefined` when input should be skipped, or cleaned array.
5
+ * Empty array after filtering → `null` (same as clear).
6
+ */
7
+ export declare function normalizePatchTypedModels(v: unknown): AgentTypedModel[] | null | undefined;
1
8
  /** Read `primary` from an `AgentModelConfig` object. */
2
9
  export declare function agentModelRefToString(ref: unknown): string | undefined;
3
10
  export declare function agentModelFallbacksToArray(ref: unknown): string[];