@xopcai/xopc 0.0.117 → 0.0.119

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 (590) hide show
  1. package/README.md +26 -24
  2. package/README.zh-CN.md +46 -42
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BBLhJ2C6.js → agent-avatar-dicebear-CU8J6zou.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-B0hkpLhP.js +2 -0
  7. package/dist/gateway/static/root/assets/app-management-settings-panel-B9vfYoEr.js +1 -0
  8. package/dist/gateway/static/root/assets/appearance-settings-ClOzIO24.js +1 -0
  9. package/dist/gateway/static/root/assets/{apps-page-DdDx0V4z.js → apps-page-DU0dz9Zo.js} +1 -1
  10. package/dist/gateway/static/root/assets/{archive-plugin-DZsVqMhU.js → archive-plugin-BkVIgZzA.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CIol6JWX.js → attachment-load-CaCMWh1D.js} +1 -1
  12. package/dist/gateway/static/root/assets/automation-suggestion-card-DhGDpKP3.js +1 -0
  13. package/dist/gateway/static/root/assets/automations-page-CTedroVo.js +1 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-B3UauMfp.js → binary-plugins-B4TO_bsn.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-2HJoFchH.js → block-editor-DJRWwWNI.js} +6 -6
  16. package/dist/gateway/static/root/assets/browser-settings-page-FCDwOOBK.js +8 -0
  17. package/dist/gateway/static/root/assets/capability-presets-api-Befi89CL.js +1 -0
  18. package/dist/gateway/static/root/assets/capability-presets-settings-panel-_G8eb2vJ.js +1 -0
  19. package/dist/gateway/static/root/assets/channel-recipient-api-BNSuJb5f.js +1 -0
  20. package/dist/gateway/static/root/assets/channels-settings-C3d5JBJX.js +1 -0
  21. package/dist/gateway/static/root/assets/cn-BQ_8x0bT.js +1 -0
  22. package/dist/gateway/static/root/assets/connectors-page-yaUNvt5u.js +2 -0
  23. package/dist/gateway/static/root/assets/cron-expression-B3B1eqnr.js +2 -0
  24. package/dist/gateway/static/root/assets/dreaming-settings-DNrdg1Ve.js +1 -0
  25. package/dist/gateway/static/root/assets/{extension-debug-page-DX5WRE_t.js → extension-debug-page-DYh402XV.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-Buah7Bw-.js → extension-page-Cr_TizrN.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-DuroLVnf.js → extension-settings-page-BU1KoKRj.js} +1 -1
  28. package/dist/gateway/static/root/assets/{fetch-B5zdl27C.js → fetch-Y7AEX4H3.js} +1 -1
  29. package/dist/gateway/static/root/assets/format-cron-label-arqgGlVV.js +1 -0
  30. package/dist/gateway/static/root/assets/{gateway-config-api-gT4ZhE7h.js → gateway-config-api-RbMmAOpX.js} +1 -1
  31. package/dist/gateway/static/root/assets/gateway-settings-C79wkXef.js +1 -0
  32. package/dist/gateway/static/root/assets/{gateway-startup-retry-B3kL8KGY.js → gateway-startup-retry-DiD_Tx4N.js} +1 -1
  33. package/dist/gateway/static/root/assets/goal-detail-page-9paXuHdo.js +1 -0
  34. package/dist/gateway/static/root/assets/{goals-config-api-AR-fVh0P.js → goals-config-api-DqxVi0p5.js} +1 -1
  35. package/dist/gateway/static/root/assets/{goals-page-Cpb21ezg.js → goals-page-CS0R-Af2.js} +2 -2
  36. package/dist/gateway/static/root/assets/goals-settings-CSuzZ8Ae.js +1 -0
  37. package/dist/gateway/static/root/assets/heartbeat-settings-DXPaYJm3.js +1 -0
  38. package/dist/gateway/static/root/assets/index-BoKafJIZ.js +191 -0
  39. package/dist/gateway/static/root/assets/index-CArhCcYn.css +1 -0
  40. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-Dvg53qnd.js +1 -0
  41. package/dist/gateway/static/root/assets/{logs-page-DefCgF4o.js → logs-page-DLXPcksQ.js} +2 -2
  42. package/dist/gateway/static/root/assets/{markdown-split-ChbR9LGO.js → markdown-split-CpHxbtWJ.js} +1 -1
  43. package/dist/gateway/static/root/assets/{media-plugins-B82oP80n.js → media-plugins-SFPQRSxq.js} +1 -1
  44. package/dist/gateway/static/root/assets/messages-CrDHEbss.js +3 -0
  45. package/dist/gateway/static/root/assets/models-hub-panel-BDF2iyRk.js +2 -0
  46. package/dist/gateway/static/root/assets/note-detail-page-CSb_hwig.js +2 -0
  47. package/dist/gateway/static/root/assets/{note-image-lightbox-CDknITM3.js → note-image-lightbox-BUYURRgn.js} +1 -1
  48. package/dist/gateway/static/root/assets/notes-page-BsIBSFyZ.js +1 -0
  49. package/dist/gateway/static/root/assets/{preview-open-alternatives-DV3xQsWB.js → preview-open-alternatives-BHz9iWYA.js} +1 -1
  50. package/dist/gateway/static/root/assets/remote-access-hub-CwzSovcS.js +1 -0
  51. package/dist/gateway/static/root/assets/{save-bar-controls-BODCXnvY.js → save-bar-controls-CefkOlRg.js} +1 -1
  52. package/dist/gateway/static/root/assets/schedule-field-CmTRoOcE.js +1 -0
  53. package/dist/gateway/static/root/assets/{schema-form-qtfEI6QM.js → schema-form-FMKv1vGu.js} +1 -1
  54. package/dist/gateway/static/root/assets/sessions-page-PHmX-NmA.js +1 -0
  55. package/dist/gateway/static/root/assets/settings-advanced-gate-BRAeKJN4.js +1 -0
  56. package/dist/gateway/static/root/assets/{settings-collapsible-section-BHZKjVcS.js → settings-collapsible-section-BouO6JTi.js} +1 -1
  57. package/dist/gateway/static/root/assets/{settings-form-section-5i35D-J1.js → settings-form-section-D-4lBl6X.js} +1 -1
  58. package/dist/gateway/static/root/assets/settings-page-KKrbFjy8.js +1 -0
  59. package/dist/gateway/static/root/assets/settings-page-layout-BJ4Gsef5.js +1 -0
  60. package/dist/gateway/static/root/assets/setup-status-panel-CrUjHPQo.js +1 -0
  61. package/dist/gateway/static/root/assets/{share-preview-page-B0H63tDv.js → share-preview-page-BDy2btRN.js} +1 -1
  62. package/dist/gateway/static/root/assets/shares-settings-B18OBmH5.js +1 -0
  63. package/dist/gateway/static/root/assets/skill-reload-api-C28-2uKQ.js +1 -0
  64. package/dist/gateway/static/root/assets/skills-page-Bm2ebkwl.js +2 -0
  65. package/dist/gateway/static/root/assets/system-settings-panel-CRngrlnZ.js +1 -0
  66. package/dist/gateway/static/root/assets/typed-models-lib-BltBc0b-.js +1 -0
  67. package/dist/gateway/static/root/assets/{use-channel-catalog-BCjP_VHb.js → use-channel-catalog-CjN-51e6.js} +1 -1
  68. package/dist/gateway/static/root/assets/user-profile-settings-panel-DJ1dKwZy.js +1 -0
  69. package/dist/gateway/static/root/assets/utils-B5M5-YvK.js +1 -0
  70. package/dist/gateway/static/root/assets/voice-api-key-field-CeEevm7z.js +1 -0
  71. package/dist/gateway/static/root/assets/{voice-player-store-BlF9V05n.js → voice-player-store-B7ykUlHM.js} +1 -1
  72. package/dist/gateway/static/root/assets/workflow-run-setup-panel-C4PzUQjB.js +3 -0
  73. package/dist/gateway/static/root/assets/workflows-page-BL84_-Ky.js +18 -0
  74. package/dist/gateway/static/root/index.html +6 -7
  75. package/dist/package.js +1 -1
  76. package/dist/src/agent/agent-manager.d.ts +4 -5
  77. package/dist/src/agent/agent-manager.js +6 -9
  78. package/dist/src/agent/agent-manager.js.map +1 -1
  79. package/dist/src/agent/agent-scope.js +0 -2
  80. package/dist/src/agent/agent-scope.js.map +1 -1
  81. package/dist/src/agent/background-review/prompts.d.ts +2 -2
  82. package/dist/src/agent/background-review/prompts.js +2 -2
  83. package/dist/src/agent/background-review/prompts.js.map +1 -1
  84. package/dist/src/agent/background-review/settings.d.ts +1 -1
  85. package/dist/src/agent/background-review/settings.js +7 -8
  86. package/dist/src/agent/background-review/settings.js.map +1 -1
  87. package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +1 -0
  88. package/dist/src/agent/bootstrap/bootstrap-cache.js +4 -3
  89. package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
  90. package/dist/src/agent/bootstrap/bootstrap-files.d.ts +4 -0
  91. package/dist/src/agent/bootstrap/bootstrap-files.js +4 -3
  92. package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
  93. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +4 -2
  94. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  95. package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +2 -1
  96. package/dist/src/agent/bootstrap/load-bootstrap-files.js +17 -16
  97. package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
  98. package/dist/src/agent/bootstrap/types.d.ts +1 -1
  99. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  100. package/dist/src/agent/context/workspace.d.ts +4 -2
  101. package/dist/src/agent/context/workspace.js +5 -4
  102. package/dist/src/agent/context/workspace.js.map +1 -1
  103. package/dist/src/agent/embedded/run-for-session.js +106 -15
  104. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  105. package/dist/src/agent/fallback/candidates.d.ts +0 -8
  106. package/dist/src/agent/fallback/candidates.js +10 -11
  107. package/dist/src/agent/fallback/candidates.js.map +1 -1
  108. package/dist/src/agent/index.js +1 -1
  109. package/dist/src/agent/lifecycle/handlers/compaction.d.ts +1 -3
  110. package/dist/src/agent/lifecycle/handlers/compaction.js +1 -16
  111. package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
  112. package/dist/src/agent/lifecycle/progress.d.ts +0 -1
  113. package/dist/src/agent/lifecycle/progress.js +1 -6
  114. package/dist/src/agent/lifecycle/progress.js.map +1 -1
  115. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +1 -2
  116. package/dist/src/agent/lifecycle/timeout-wrapper.js +1 -7
  117. package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
  118. package/dist/src/agent/memory/builtin-memory-store.d.ts +4 -2
  119. package/dist/src/agent/memory/builtin-memory-store.js +11 -6
  120. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  121. package/dist/src/agent/memory/builtin-provider.js +36 -7
  122. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  123. package/dist/src/agent/memory/candidate-extractor.d.ts +6 -0
  124. package/dist/src/agent/memory/candidate-extractor.js +60 -0
  125. package/dist/src/agent/memory/candidate-extractor.js.map +1 -0
  126. package/dist/src/agent/memory/manager.d.ts +2 -1
  127. package/dist/src/agent/memory/manager.js +27 -3
  128. package/dist/src/agent/memory/manager.js.map +1 -1
  129. package/dist/src/agent/memory/memory-config.js +3 -0
  130. package/dist/src/agent/memory/memory-config.js.map +1 -1
  131. package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -1
  132. package/dist/src/agent/memory/types.d.ts +27 -4
  133. package/dist/src/agent/middleware/self-verify.d.ts +0 -1
  134. package/dist/src/agent/middleware/self-verify.js +0 -2
  135. package/dist/src/agent/middleware/self-verify.js.map +1 -1
  136. package/dist/src/agent/models/index.d.ts +1 -1
  137. package/dist/src/agent/models/manager.d.ts +4 -20
  138. package/dist/src/agent/models/manager.js +9 -126
  139. package/dist/src/agent/models/manager.js.map +1 -1
  140. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +0 -4
  141. package/dist/src/agent/orchestration/agent-orchestrator.js +1 -9
  142. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  143. package/dist/src/agent/prompt/memory/index.d.ts +4 -2
  144. package/dist/src/agent/prompt/memory/index.js +18 -12
  145. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  146. package/dist/src/agent/prompt/sections/memory-skills.js +1 -1
  147. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
  148. package/dist/src/agent/prompt/sections/tooling.js +2 -2
  149. package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
  150. package/dist/src/agent/prompt/service-prompt-builder.d.ts +0 -1
  151. package/dist/src/agent/prompt/service-prompt-builder.js +4 -31
  152. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  153. package/dist/src/agent/prompt/subagent-context.js +1 -1
  154. package/dist/src/agent/prompt/subagent-context.js.map +1 -1
  155. package/dist/src/agent/prompt/types.d.ts +1 -1
  156. package/dist/src/agent/reply/post-compaction-context.js +1 -3
  157. package/dist/src/agent/reply/post-compaction-context.js.map +1 -1
  158. package/dist/src/agent/reply/startup-context.js +1 -5
  159. package/dist/src/agent/reply/startup-context.js.map +1 -1
  160. package/dist/src/agent/service.d.ts +4 -12
  161. package/dist/src/agent/service.js +10 -48
  162. package/dist/src/agent/service.js.map +1 -1
  163. package/dist/src/agent/service.types.d.ts +2 -2
  164. package/dist/src/agent/session/session-config-service.d.ts +11 -11
  165. package/dist/src/agent/session/session-config-service.js +14 -14
  166. package/dist/src/agent/session/session-config-service.js.map +1 -1
  167. package/dist/src/agent/session/session-inspector.js +3 -2
  168. package/dist/src/agent/session/session-inspector.js.map +1 -1
  169. package/dist/src/agent/starter-agents.js +12 -12
  170. package/dist/src/agent/starter-agents.js.map +1 -1
  171. package/dist/src/agent/tools/automation-tool.d.ts +14 -0
  172. package/dist/src/agent/tools/automation-tool.js +102 -0
  173. package/dist/src/agent/tools/automation-tool.js.map +1 -0
  174. package/dist/src/agent/tools/curated-memory-tool.js +57 -2
  175. package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
  176. package/dist/src/agent/tools/delegate-tool.js +1 -1
  177. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  178. package/dist/src/agent/tools/factory.d.ts +3 -3
  179. package/dist/src/agent/tools/factory.js +2 -2
  180. package/dist/src/agent/tools/factory.js.map +1 -1
  181. package/dist/src/agent/tools/index.d.ts +1 -1
  182. package/dist/src/agent/tools/index.js +2 -2
  183. package/dist/src/agent/tools/web-extract.js +6 -8
  184. package/dist/src/agent/tools/web-extract.js.map +1 -1
  185. package/dist/src/agent-manifest/index.js +2 -2
  186. package/dist/src/agent-manifest/resolver.d.ts +4 -2
  187. package/dist/src/agent-manifest/resolver.js +6 -3
  188. package/dist/src/agent-manifest/resolver.js.map +1 -1
  189. package/dist/src/agent-manifest/schema.d.ts +238 -0
  190. package/dist/src/agent-manifest/schema.js +16 -5
  191. package/dist/src/agent-manifest/schema.js.map +1 -1
  192. package/dist/src/agent-manifest/validator.d.ts +3 -2
  193. package/dist/src/agent-manifest/validator.js +2 -1
  194. package/dist/src/agent-manifest/validator.js.map +1 -1
  195. package/dist/src/ai-assist/text-assist.d.ts +1 -1
  196. package/dist/src/ai-assist/text-assist.js +56 -0
  197. package/dist/src/ai-assist/text-assist.js.map +1 -1
  198. package/dist/src/auth/profiles/store.js.map +1 -1
  199. package/dist/src/automations/api/routes.d.ts +3 -0
  200. package/dist/src/automations/api/routes.js +193 -0
  201. package/dist/src/automations/api/routes.js.map +1 -0
  202. package/dist/src/automations/domain/index.d.ts +3 -0
  203. package/dist/src/automations/domain/index.js +3 -0
  204. package/dist/src/automations/domain/schedule.d.ts +4 -0
  205. package/dist/src/automations/domain/schedule.js +37 -0
  206. package/dist/src/automations/domain/schedule.js.map +1 -0
  207. package/dist/src/automations/domain/types.d.ts +160 -0
  208. package/dist/src/automations/domain/validation.d.ts +370 -0
  209. package/dist/src/automations/domain/validation.js +149 -0
  210. package/dist/src/automations/domain/validation.js.map +1 -0
  211. package/dist/src/automations/draft/automation-draft-prompt.d.ts +26 -0
  212. package/dist/src/automations/draft/automation-draft-prompt.js +127 -0
  213. package/dist/src/automations/draft/automation-draft-prompt.js.map +1 -0
  214. package/dist/src/automations/draft/automation-draft-service.d.ts +12 -0
  215. package/dist/src/automations/draft/automation-draft-service.js +135 -0
  216. package/dist/src/automations/draft/automation-draft-service.js.map +1 -0
  217. package/dist/src/automations/draft/automation-draft-validator.d.ts +99 -0
  218. package/dist/src/automations/draft/automation-draft-validator.js +105 -0
  219. package/dist/src/automations/draft/automation-draft-validator.js.map +1 -0
  220. package/dist/src/automations/draft/automation-draft.types.d.ts +40 -0
  221. package/dist/src/automations/draft/index.d.ts +3 -0
  222. package/dist/src/automations/draft/index.js +3 -0
  223. package/dist/src/automations/index.d.ts +3 -0
  224. package/dist/src/automations/index.js +8 -0
  225. package/dist/src/automations/product-events.d.ts +5 -0
  226. package/dist/src/automations/product-events.js +21 -0
  227. package/dist/src/automations/product-events.js.map +1 -0
  228. package/dist/src/automations/service/action-executor.d.ts +9 -0
  229. package/dist/src/automations/service/action-executor.js +157 -0
  230. package/dist/src/automations/service/action-executor.js.map +1 -0
  231. package/dist/src/automations/service/automation-service.d.ts +54 -0
  232. package/dist/src/automations/service/automation-service.js +404 -0
  233. package/dist/src/automations/service/automation-service.js.map +1 -0
  234. package/dist/src/automations/service/index.d.ts +2 -0
  235. package/dist/src/automations/service/index.js +3 -0
  236. package/dist/src/automations/storage/automation-repository.d.ts +6 -0
  237. package/dist/src/automations/storage/automation-repository.js +61 -0
  238. package/dist/src/automations/storage/automation-repository.js.map +1 -0
  239. package/dist/src/automations/storage/automation-run-repository.d.ts +17 -0
  240. package/dist/src/automations/storage/automation-run-repository.js +130 -0
  241. package/dist/src/automations/storage/automation-run-repository.js.map +1 -0
  242. package/dist/src/automations/storage/index.d.ts +2 -0
  243. package/dist/src/automations/storage/index.js +3 -0
  244. package/dist/src/channels/pipeline.d.ts +1 -1
  245. package/dist/src/channels/pipeline.js.map +1 -1
  246. package/dist/src/channels/plugin-types.d.ts +3 -0
  247. package/dist/src/chat-commands/agent-edit.js +0 -1
  248. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  249. package/dist/src/chat-commands/builtins/model.js +2 -2
  250. package/dist/src/chat-commands/builtins/model.js.map +1 -1
  251. package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
  252. package/dist/src/cli/command-catalog.js +0 -39
  253. package/dist/src/cli/command-catalog.js.map +1 -1
  254. package/dist/src/cli/command-loaders.js +0 -1
  255. package/dist/src/cli/command-loaders.js.map +1 -1
  256. package/dist/src/cli/command-manifest.d.ts +1 -1
  257. package/dist/src/cli/command-manifest.js +1 -5
  258. package/dist/src/cli/command-manifest.js.map +1 -1
  259. package/dist/src/cli/commands/agents.js +7 -6
  260. package/dist/src/cli/commands/agents.js.map +1 -1
  261. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  262. package/dist/src/cli/commands/doctor/checks/{cron-health.d.ts → migrations.d.ts} +1 -1
  263. package/dist/src/cli/commands/doctor/checks/migrations.js +40 -0
  264. package/dist/src/cli/commands/doctor/checks/migrations.js.map +1 -0
  265. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -0
  266. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -1
  267. package/dist/src/cli/commands/doctor/checks/workspace-status.js +0 -1
  268. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
  269. package/dist/src/cli/commands/doctor/flow.js +2 -2
  270. package/dist/src/cli/commands/doctor/flow.js.map +1 -1
  271. package/dist/src/cli/commands/gateway/call.js +2 -2
  272. package/dist/src/cli/commands/gateway/call.js.map +1 -1
  273. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  274. package/dist/src/cli/commands/gateway/run-foreground.js +4 -2
  275. package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
  276. package/dist/src/cli/commands/init.js +33 -14
  277. package/dist/src/cli/commands/init.js.map +1 -1
  278. package/dist/src/cli/commands/models.js +13 -31
  279. package/dist/src/cli/commands/models.js.map +1 -1
  280. package/dist/src/cli/commands/onboard/model.js +17 -10
  281. package/dist/src/cli/commands/onboard/model.js.map +1 -1
  282. package/dist/src/cli/commands/onboard.js +2 -2
  283. package/dist/src/cli/commands/onboard.js.map +1 -1
  284. package/dist/src/cli/commands/tui.js +26 -4
  285. package/dist/src/cli/commands/tui.js.map +1 -1
  286. package/dist/src/cli/index.d.ts +1 -0
  287. package/dist/src/cli/index.js +10 -5
  288. package/dist/src/cli/index.js.map +1 -1
  289. package/dist/src/cli/registry.js +1 -1
  290. package/dist/src/cli/registry.js.map +1 -1
  291. package/dist/src/cli/templates.d.ts +1 -1
  292. package/dist/src/cli/templates.js +1 -11
  293. package/dist/src/cli/templates.js.map +1 -1
  294. package/dist/src/cli/utils/init-workspace-core.js +2 -0
  295. package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
  296. package/dist/src/commands/agents.config.d.ts +9 -0
  297. package/dist/src/commands/agents.config.js +42 -12
  298. package/dist/src/commands/agents.config.js.map +1 -1
  299. package/dist/src/config/agent-profile.js +5 -3
  300. package/dist/src/config/agent-profile.js.map +1 -1
  301. package/dist/src/config/agent-typed-models.js +3 -5
  302. package/dist/src/config/agent-typed-models.js.map +1 -1
  303. package/dist/src/config/config-surface.d.ts +3 -0
  304. package/dist/src/config/index.d.ts +0 -1
  305. package/dist/src/config/index.js +3 -3
  306. package/dist/src/config/index.js.map +1 -1
  307. package/dist/src/config/paths.d.ts +8 -10
  308. package/dist/src/config/paths.js +16 -16
  309. package/dist/src/config/paths.js.map +1 -1
  310. package/dist/src/config/profile.js +0 -1
  311. package/dist/src/config/profile.js.map +1 -1
  312. package/dist/src/config/rules.js +0 -5
  313. package/dist/src/config/rules.js.map +1 -1
  314. package/dist/src/config/schema.d.ts +45 -44
  315. package/dist/src/config/schema.js +68 -52
  316. package/dist/src/config/schema.js.map +1 -1
  317. package/dist/src/config/web-patch.d.ts +0 -12
  318. package/dist/src/config/web-patch.js +2 -29
  319. package/dist/src/config/web-patch.js.map +1 -1
  320. package/dist/src/extensions/api.d.ts +2 -0
  321. package/dist/src/extensions/api.js +10 -0
  322. package/dist/src/extensions/api.js.map +1 -1
  323. package/dist/src/extensions/extension-registry-impl.d.ts +4 -1
  324. package/dist/src/extensions/extension-registry-impl.js +8 -0
  325. package/dist/src/extensions/extension-registry-impl.js.map +1 -1
  326. package/dist/src/extensions/loader.d.ts +3 -3
  327. package/dist/src/extensions/loader.js +3 -3
  328. package/dist/src/extensions/loader.js.map +1 -1
  329. package/dist/src/extensions/sdk/testing.d.ts +2 -0
  330. package/dist/src/extensions/sdk/testing.js +7 -0
  331. package/dist/src/extensions/sdk/testing.js.map +1 -1
  332. package/dist/src/extensions/types/core.d.ts +10 -1
  333. package/dist/src/extensions/types/index.d.ts +1 -1
  334. package/dist/src/extensions/types/{phase4.d.ts → registration.d.ts} +2 -2
  335. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  336. package/dist/src/gateway/agent-builtin-tools.js +1 -1
  337. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  338. package/dist/src/gateway/agents-admin.d.ts +8 -0
  339. package/dist/src/gateway/agents-admin.js +63 -34
  340. package/dist/src/gateway/agents-admin.js.map +1 -1
  341. package/dist/src/gateway/capability-presets-admin.d.ts +1 -0
  342. package/dist/src/gateway/capability-presets-admin.js +19 -10
  343. package/dist/src/gateway/capability-presets-admin.js.map +1 -1
  344. package/dist/src/gateway/chat-stream/mapper.js +9 -2
  345. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  346. package/dist/src/gateway/dreaming-automation-reconciler.d.ts +11 -0
  347. package/dist/src/gateway/dreaming-automation-reconciler.js +105 -0
  348. package/dist/src/gateway/dreaming-automation-reconciler.js.map +1 -0
  349. package/dist/src/gateway/global-defaults-admin.d.ts +36 -0
  350. package/dist/src/gateway/global-defaults-admin.js +146 -0
  351. package/dist/src/gateway/global-defaults-admin.js.map +1 -0
  352. package/dist/src/gateway/heartbeat/service.d.ts +0 -2
  353. package/dist/src/gateway/heartbeat/service.js +0 -7
  354. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  355. package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -6
  356. package/dist/src/gateway/hono/lib/config-payload.js +2 -2
  357. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  358. package/dist/src/gateway/hono/routes/agents.js +33 -1
  359. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  360. package/dist/src/gateway/hono/routes/commands-skills.js +4 -5
  361. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  362. package/dist/src/gateway/hono/routes/config-patch/misc.d.ts +1 -1
  363. package/dist/src/gateway/hono/routes/config-patch/misc.js +12 -6
  364. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
  365. package/dist/src/gateway/hono/routes/dreaming.js +27 -37
  366. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  367. package/dist/src/gateway/hono/routes/global-defaults.d.ts +3 -0
  368. package/dist/src/gateway/hono/routes/global-defaults.js +61 -0
  369. package/dist/src/gateway/hono/routes/global-defaults.js.map +1 -0
  370. package/dist/src/gateway/hono/routes/home.d.ts +2 -2
  371. package/dist/src/gateway/hono/routes/home.js +34 -25
  372. package/dist/src/gateway/hono/routes/home.js.map +1 -1
  373. package/dist/src/gateway/hono/routes/lazy-bundles.js +21 -9
  374. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  375. package/dist/src/gateway/hono/routes/memory.js +75 -4
  376. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  377. package/dist/src/gateway/hono/routes/migrations.d.ts +3 -0
  378. package/dist/src/gateway/hono/routes/migrations.js +35 -0
  379. package/dist/src/gateway/hono/routes/migrations.js.map +1 -0
  380. package/dist/src/gateway/hono/routes/models.js +1 -1
  381. package/dist/src/gateway/hono/routes/public-gateway.js +2 -1
  382. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  383. package/dist/src/gateway/hono/routes/sessions.js +14 -0
  384. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  385. package/dist/src/gateway/run-loop.js +2 -0
  386. package/dist/src/gateway/run-loop.js.map +1 -1
  387. package/dist/src/gateway/security/operator-scopes.js +5 -1
  388. package/dist/src/gateway/security/operator-scopes.js.map +1 -1
  389. package/dist/src/gateway/service/config-coordinator.d.ts +4 -4
  390. package/dist/src/gateway/service/config-coordinator.js +14 -8
  391. package/dist/src/gateway/service/config-coordinator.js.map +1 -1
  392. package/dist/src/gateway/service/marketplace-service.d.ts +4 -4
  393. package/dist/src/gateway/service/marketplace-service.js +11 -7
  394. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  395. package/dist/src/gateway/service/sessions-api.d.ts +2 -1
  396. package/dist/src/gateway/service/sessions-api.js +6 -1
  397. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  398. package/dist/src/gateway/service.d.ts +7 -3
  399. package/dist/src/gateway/service.js +61 -27
  400. package/dist/src/gateway/service.js.map +1 -1
  401. package/dist/src/goals/goal-service.js +45 -4
  402. package/dist/src/goals/goal-service.js.map +1 -1
  403. package/dist/src/infra/update-restart.js +1 -1
  404. package/dist/src/migrations/config/default-capability-preset.d.ts +10 -0
  405. package/dist/src/migrations/config/default-capability-preset.js +135 -0
  406. package/dist/src/migrations/config/default-capability-preset.js.map +1 -0
  407. package/dist/src/migrations/index.d.ts +4 -0
  408. package/dist/src/migrations/index.js +4 -0
  409. package/dist/src/migrations/registry.d.ts +8 -0
  410. package/dist/src/migrations/registry.js +18 -0
  411. package/dist/src/migrations/registry.js.map +1 -0
  412. package/dist/src/migrations/runner.d.ts +17 -0
  413. package/dist/src/migrations/runner.js +191 -0
  414. package/dist/src/migrations/runner.js.map +1 -0
  415. package/dist/src/migrations/types.d.ts +34 -0
  416. package/dist/src/notes/service.js +27 -2
  417. package/dist/src/notes/service.js.map +1 -1
  418. package/dist/src/providers/index.d.ts +4 -5
  419. package/dist/src/providers/index.js +16 -16
  420. package/dist/src/providers/index.js.map +1 -1
  421. package/dist/src/providers/model-registry.d.ts +0 -1
  422. package/dist/src/providers/model-registry.js +0 -3
  423. package/dist/src/providers/model-registry.js.map +1 -1
  424. package/dist/src/routing/resolve-tui-session-key.d.ts +3 -0
  425. package/dist/src/routing/resolve-tui-session-key.js +14 -3
  426. package/dist/src/routing/resolve-tui-session-key.js.map +1 -1
  427. package/dist/src/session/session-workspace.js +2 -7
  428. package/dist/src/session/session-workspace.js.map +1 -1
  429. package/dist/src/session/store.js +0 -5
  430. package/dist/src/session/store.js.map +1 -1
  431. package/dist/src/session/transcript-outline.d.ts +47 -0
  432. package/dist/src/session/transcript-outline.js +481 -0
  433. package/dist/src/session/transcript-outline.js.map +1 -0
  434. package/dist/src/storage/sqlite/index.d.ts +1 -3
  435. package/dist/src/storage/sqlite/index.js +2 -4
  436. package/dist/src/storage/sqlite/memory-index-repository.d.ts +1 -0
  437. package/dist/src/storage/sqlite/memory-index-repository.js +15 -14
  438. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
  439. package/dist/src/storage/sqlite/memory-records-repository.d.ts +44 -1
  440. package/dist/src/storage/sqlite/memory-records-repository.js +164 -20
  441. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  442. package/dist/src/storage/sqlite/migrations/014_memory_record_governance.sql +10 -0
  443. package/dist/src/storage/sqlite/migrations/015_memory_trace_feedback.sql +4 -0
  444. package/dist/src/storage/sqlite/migrations/016_automations.sql +47 -0
  445. package/dist/src/storage/sqlite/migrations/017_automation_run_events.sql +14 -0
  446. package/dist/src/storage/sqlite/migrations/018_automation_safety.sql +1 -0
  447. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  448. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  449. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  450. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  451. package/dist/src/storage/sqlite/schema.js +1 -1
  452. package/dist/src/storage/sqlite/schema.sql +56 -46
  453. package/dist/src/tui/backends/embedded-backend.d.ts +9 -0
  454. package/dist/src/tui/backends/embedded-backend.js +55 -27
  455. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  456. package/dist/src/tui/backends/gateway-sse-backend.d.ts +4 -0
  457. package/dist/src/tui/backends/gateway-sse-backend.js +30 -3
  458. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  459. package/dist/src/tui/components/tui-header.d.ts +0 -1
  460. package/dist/src/tui/components/tui-header.js +3 -8
  461. package/dist/src/tui/components/tui-header.js.map +1 -1
  462. package/dist/src/tui/tui-backend.d.ts +4 -0
  463. package/dist/src/tui/tui-commands.d.ts +5 -0
  464. package/dist/src/tui/tui-commands.js +43 -0
  465. package/dist/src/tui/tui-commands.js.map +1 -1
  466. package/dist/src/tui/tui-startup-resources.js +2 -1
  467. package/dist/src/tui/tui-startup-resources.js.map +1 -1
  468. package/dist/src/tui/tui-startup-text.js +12 -12
  469. package/dist/src/tui/tui-startup-text.js.map +1 -1
  470. package/dist/src/tui/tui-transcript-tree.d.ts +1 -1
  471. package/dist/src/tui/tui-transcript-tree.js +17 -257
  472. package/dist/src/tui/tui-transcript-tree.js.map +1 -1
  473. package/dist/src/tui/tui-types.d.ts +2 -0
  474. package/dist/src/tui/tui-types.js.map +1 -1
  475. package/dist/src/tui/tui.js +11 -3
  476. package/dist/src/tui/tui.js.map +1 -1
  477. package/dist/src/workflows/domain/index.js +1 -1
  478. package/dist/src/workflows/domain/run.d.ts +7 -7
  479. package/dist/src/workflows/domain/run.js.map +1 -1
  480. package/dist/src/workflows/index.js +2 -2
  481. package/dist/src/workflows/service/workflow-run-service.d.ts +2 -0
  482. package/dist/src/workflows/service/workflow-run-service.js +31 -9
  483. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  484. package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -1
  485. package/package.json +4 -2
  486. package/dist/gateway/static/root/assets/agents-C6xgagZd.js +0 -71
  487. package/dist/gateway/static/root/assets/app-management-settings-panel-CBpLONvG.js +0 -1
  488. package/dist/gateway/static/root/assets/appearance-settings-CGcc54yj.js +0 -1
  489. package/dist/gateway/static/root/assets/browser-settings-page-CUOO-PQk.js +0 -8
  490. package/dist/gateway/static/root/assets/capability-presets-api-XQoMTI0A.js +0 -1
  491. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CfqF-kGr.js +0 -1
  492. package/dist/gateway/static/root/assets/channels-settings-BYyRzsex.js +0 -1
  493. package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +0 -1
  494. package/dist/gateway/static/root/assets/connectors-page-DIvEz4NX.js +0 -2
  495. package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +0 -1
  496. package/dist/gateway/static/root/assets/cron-api-R41aQCX9.js +0 -1
  497. package/dist/gateway/static/root/assets/cron-dreaming-jobs-nCMWCf0p.js +0 -1
  498. package/dist/gateway/static/root/assets/cron-page-D97nFqQD.js +0 -3
  499. package/dist/gateway/static/root/assets/dreaming-settings-Bceaw1PG.js +0 -1
  500. package/dist/gateway/static/root/assets/format-cron-label-BsotZDO5.js +0 -2
  501. package/dist/gateway/static/root/assets/format-interval-label-Dn_QKIqb.js +0 -1
  502. package/dist/gateway/static/root/assets/gateway-settings-iISWAZRm.js +0 -1
  503. package/dist/gateway/static/root/assets/goal-detail-page-DCUROLMQ.js +0 -1
  504. package/dist/gateway/static/root/assets/goals-settings-CPAuFwoD.js +0 -1
  505. package/dist/gateway/static/root/assets/heartbeat-config-api-D7bKFEeO.js +0 -1
  506. package/dist/gateway/static/root/assets/heartbeat-settings-CC69vNx0.js +0 -1
  507. package/dist/gateway/static/root/assets/index-CwhK8qCh.js +0 -122
  508. package/dist/gateway/static/root/assets/index-CyfhARL-.css +0 -1
  509. package/dist/gateway/static/root/assets/interval-presets-0DtsnxE0.js +0 -1
  510. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-BPREtN8B.js +0 -1
  511. package/dist/gateway/static/root/assets/messages-Cf1O9JB0.js +0 -3
  512. package/dist/gateway/static/root/assets/models-hub-panel-DmKiIihT.js +0 -2
  513. package/dist/gateway/static/root/assets/note-detail-page-CxxfLRJZ.js +0 -2
  514. package/dist/gateway/static/root/assets/notes-page-X2slSx7V.js +0 -1
  515. package/dist/gateway/static/root/assets/remote-access-hub-BvmUWqgu.js +0 -1
  516. package/dist/gateway/static/root/assets/schedule-field-CgJSCD3B.js +0 -1
  517. package/dist/gateway/static/root/assets/sessions-page-B0yESuuL.js +0 -1
  518. package/dist/gateway/static/root/assets/settings-advanced-gate-q2M8RKOo.js +0 -1
  519. package/dist/gateway/static/root/assets/settings-page-CGozabLD.js +0 -1
  520. package/dist/gateway/static/root/assets/setup-status-panel-FoOSe6nU.js +0 -1
  521. package/dist/gateway/static/root/assets/shares-settings-Cy5zWyYQ.js +0 -1
  522. package/dist/gateway/static/root/assets/skill-reload-api-DaZNFro9.js +0 -1
  523. package/dist/gateway/static/root/assets/skills-page-CnwtUzvw.js +0 -2
  524. package/dist/gateway/static/root/assets/system-settings-panel-DXs2VmBt.js +0 -1
  525. package/dist/gateway/static/root/assets/typed-models-lib-4gPuydz_.js +0 -1
  526. package/dist/gateway/static/root/assets/voice-api-key-field-DvKiOglO.js +0 -1
  527. package/dist/gateway/static/root/assets/workflows-page-CAVZcONQ.js +0 -18
  528. package/dist/src/agent/context/workspace-templates/USER.md +0 -24
  529. package/dist/src/agent/fallback/error.d.ts +0 -22
  530. package/dist/src/agent/fallback/error.js +0 -32
  531. package/dist/src/agent/fallback/error.js.map +0 -1
  532. package/dist/src/agent/fallback/index.d.ts +0 -5
  533. package/dist/src/agent/fallback/index.js +0 -9
  534. package/dist/src/agent/fallback/index.js.map +0 -1
  535. package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.d.ts +0 -29
  536. package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js +0 -76
  537. package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js.map +0 -1
  538. package/dist/src/agent/service/reconcile-dreaming-cron.d.ts +0 -6
  539. package/dist/src/agent/service/reconcile-dreaming-cron.js +0 -84
  540. package/dist/src/agent/service/reconcile-dreaming-cron.js.map +0 -1
  541. package/dist/src/agent/tools/cronjob-tool.d.ts +0 -29
  542. package/dist/src/agent/tools/cronjob-tool.js +0 -274
  543. package/dist/src/agent/tools/cronjob-tool.js.map +0 -1
  544. package/dist/src/cli/commands/cron-cli.d.ts +0 -2
  545. package/dist/src/cli/commands/cron-cli.js +0 -15
  546. package/dist/src/cli/commands/cron-cli.js.map +0 -1
  547. package/dist/src/cli/commands/cron.d.ts +0 -4
  548. package/dist/src/cli/commands/cron.js +0 -188
  549. package/dist/src/cli/commands/cron.js.map +0 -1
  550. package/dist/src/cli/commands/doctor/checks/cron-health.js +0 -35
  551. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +0 -1
  552. package/dist/src/cron/execution-types.d.ts +0 -42
  553. package/dist/src/cron/executor.d.ts +0 -67
  554. package/dist/src/cron/executor.js +0 -669
  555. package/dist/src/cron/executor.js.map +0 -1
  556. package/dist/src/cron/index.d.ts +0 -7
  557. package/dist/src/cron/index.js +0 -7
  558. package/dist/src/cron/job-content.d.ts +0 -5
  559. package/dist/src/cron/job-content.js +0 -15
  560. package/dist/src/cron/job-content.js.map +0 -1
  561. package/dist/src/cron/run-log-store.d.ts +0 -12
  562. package/dist/src/cron/run-log-store.js +0 -58
  563. package/dist/src/cron/run-log-store.js.map +0 -1
  564. package/dist/src/cron/schedule.d.ts +0 -10
  565. package/dist/src/cron/schedule.js +0 -85
  566. package/dist/src/cron/schedule.js.map +0 -1
  567. package/dist/src/cron/service.d.ts +0 -58
  568. package/dist/src/cron/service.js +0 -419
  569. package/dist/src/cron/service.js.map +0 -1
  570. package/dist/src/cron/types.d.ts +0 -183
  571. package/dist/src/cron/validation.d.ts +0 -500
  572. package/dist/src/cron/validation.js +0 -192
  573. package/dist/src/cron/validation.js.map +0 -1
  574. package/dist/src/cron/workflow-run-completion.d.ts +0 -23
  575. package/dist/src/cron/workflow-run-completion.js +0 -72
  576. package/dist/src/cron/workflow-run-completion.js.map +0 -1
  577. package/dist/src/extensions/types/phase4.js +0 -1
  578. package/dist/src/gateway/hono/routes/cron.d.ts +0 -3
  579. package/dist/src/gateway/hono/routes/cron.js +0 -109
  580. package/dist/src/gateway/hono/routes/cron.js.map +0 -1
  581. package/dist/src/storage/sqlite/cron-job-repository.d.ts +0 -6
  582. package/dist/src/storage/sqlite/cron-job-repository.js +0 -70
  583. package/dist/src/storage/sqlite/cron-job-repository.js.map +0 -1
  584. package/dist/src/storage/sqlite/cron-run-repository.d.ts +0 -5
  585. package/dist/src/storage/sqlite/cron-run-repository.js +0 -96
  586. package/dist/src/storage/sqlite/cron-run-repository.js.map +0 -1
  587. /package/dist/src/{cron → automations/domain}/types.js +0 -0
  588. /package/dist/src/{config/defaults.d.ts → automations/draft/automation-draft.types.js} +0 -0
  589. /package/dist/src/{config/defaults.js → extensions/types/registration.js} +0 -0
  590. /package/dist/src/{cron/execution-types.js → migrations/types.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation-service.js","names":[],"sources":["../../../../src/automations/service/automation-service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n computeNextAutomationRunAtMs,\n timerDelayUntil,\n} from '../domain/schedule.js';\nimport type {\n Automation,\n AutomationDeps,\n AutomationEvent,\n AutomationMetrics,\n AutomationProductEventRun,\n AutomationRun,\n AutomationRunEvent,\n AutomationRunEventType,\n AutomationRunStatus,\n} from '../domain/types.js';\nimport {\n CreateAutomationSchema,\n UpdateAutomationSchema,\n type CreateAutomationInput,\n type UpdateAutomationInput,\n AutomationSchema,\n} from '../domain/validation.js';\nimport {\n appendAutomationRunEvent,\n deleteAutomation,\n getAutomation,\n getAutomationRun,\n listAutomationRunEvents,\n listAutomationRuns,\n listAutomationRunsForProductEvent,\n listAutomations,\n saveAutomation,\n saveAutomationRun,\n saveAutomations,\n} from '../storage/index.js';\nimport { AutomationActionExecutor } from './action-executor.js';\n\nconst log = createLogger('AutomationService');\n\ntype TimerHandle = ReturnType<typeof setTimeout> & { unref?: () => void };\n\nconst DEFAULT_MAX_CONCURRENT_RUNS = 5;\nconst MISSED_RUN_STAGGER_MS = 5_000;\n\nexport class AutomationAlreadyRunningError extends Error {\n constructor(readonly automationId: string, readonly runningRunId?: string) {\n super(`Automation is already running: ${automationId}`);\n this.name = 'AutomationAlreadyRunningError';\n }\n}\n\nexport class AutomationService {\n private readonly executor = new AutomationActionExecutor();\n private timer: TimerHandle | null = null;\n private stopped = true;\n private runningScheduler = false;\n private deps: AutomationDeps = {};\n private maxConcurrentRuns = DEFAULT_MAX_CONCURRENT_RUNS;\n private readonly activeRuns = new Map<string, AbortController>();\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n this.executor.setDeps(this.deps);\n }\n\n async initialize(options?: { maxConcurrentRuns?: number }): Promise<void> {\n this.maxConcurrentRuns = Math.max(1, Math.floor(options?.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS));\n this.stopped = false;\n await this.recomputeNextRuns({ catchUp: true });\n this.armTimer();\n log.info('Automation service initialized');\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n for (const controller of this.activeRuns.values()) {\n controller.abort();\n }\n log.info('Automation service stopped');\n }\n\n async create(input: CreateAutomationInput): Promise<Automation> {\n const parsed = CreateAutomationSchema.parse(input);\n const now = Date.now();\n const automation = AutomationSchema.parse({\n ...parsed,\n id: parsed.id?.trim() || randomUUID().slice(0, 12),\n enabled: parsed.enabled ?? true,\n safety: parsed.safety ?? { mode: 'auto_apply' },\n afterRun: parsed.afterRun ?? { kind: 'none' },\n state: parsed.state ?? {},\n createdAtMs: now,\n updatedAtMs: now,\n }) as Automation;\n automation.state.nextRunAtMs = computeNextAutomationRunAtMs(automation, now);\n saveAutomation(automation);\n this.armTimer();\n return automation;\n }\n\n async list(): Promise<Automation[]> {\n return listAutomations();\n }\n\n async get(id: string): Promise<Automation | null> {\n return getAutomation(id);\n }\n\n async update(id: string, patch: UpdateAutomationInput): Promise<Automation | null> {\n const parsed = UpdateAutomationSchema.parse(patch);\n const current = getAutomation(id);\n if (!current) return null;\n const now = Date.now();\n const next = AutomationSchema.parse({\n ...current,\n ...parsed,\n id: current.id,\n createdAtMs: current.createdAtMs,\n updatedAtMs: now,\n state: {\n ...current.state,\n ...(parsed.state ?? {}),\n },\n }) as Automation;\n if ('enabled' in parsed || 'trigger' in parsed) {\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, now);\n }\n saveAutomation(next);\n this.armTimer();\n return next;\n }\n\n async remove(id: string): Promise<boolean> {\n const automation = getAutomation(id);\n if (automation?.state.runningRunId) {\n this.activeRuns.get(automation.state.runningRunId)?.abort();\n }\n const removed = deleteAutomation(id);\n if (removed) {\n this.armTimer();\n }\n return removed;\n }\n\n async pause(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: false });\n }\n\n async resume(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: true });\n }\n\n async runNow(id: string): Promise<AutomationRun> {\n const automation = getAutomation(id);\n if (!automation) throw new Error(`Automation not found: ${id}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(id, automation.state.runningRunId);\n }\n return this.startRun(automation, { manual: true });\n }\n\n async rerunFromRun(runId: string): Promise<AutomationRun> {\n const previous = getAutomationRun(runId);\n if (!previous) throw new Error(`Automation run not found: ${runId}`);\n const automation = getAutomation(previous.automationId);\n if (!automation) throw new Error(`Automation not found: ${previous.automationId}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(automation.id, automation.state.runningRunId);\n }\n const triggerEvent = listAutomationRunEvents(runId)\n .map((event) => readAutomationEventFromRunEvent(event))\n .find((event): event is AutomationEvent => event !== null);\n return this.startRun(automation, triggerEvent ? { manual: false, event: triggerEvent } : { manual: true });\n }\n\n async triggerEvent(event: AutomationEvent): Promise<AutomationRun[]> {\n const normalized: AutomationEvent = {\n ...event,\n occurredAtMs: event.occurredAtMs ?? Date.now(),\n };\n const automations = listAutomations().filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'event' &&\n !automation.state.runningRunId &&\n matchesAutomationEvent(automation.trigger, normalized),\n );\n const availableSlots = Math.max(0, this.maxConcurrentRuns - this.activeRuns.size);\n const started: AutomationRun[] = [];\n for (const automation of automations.slice(0, availableSlots)) {\n started.push(await this.startRun(automation, { manual: false, event: normalized }));\n }\n return started;\n }\n\n async listRuns(options?: { automationId?: string; limit?: number }): Promise<AutomationRun[]> {\n return listAutomationRuns(options);\n }\n\n async listRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n }): Promise<AutomationProductEventRun[]> {\n return listAutomationRunsForProductEvent(options);\n }\n\n async getRun(runId: string): Promise<AutomationRun | null> {\n return getAutomationRun(runId);\n }\n\n async listRunEvents(runId: string): Promise<AutomationRunEvent[]> {\n return listAutomationRunEvents(runId);\n }\n\n async cancelRun(runId: string): Promise<boolean> {\n const controller = this.activeRuns.get(runId);\n if (!controller) return false;\n controller.abort();\n return true;\n }\n\n async getMetrics(): Promise<AutomationMetrics> {\n const automations = listAutomations();\n const enabled = automations.filter((automation) => automation.enabled);\n const next = enabled\n .filter((automation) => automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n const oneHourAgo = Date.now() - 60 * 60_000;\n return {\n totalAutomations: automations.length,\n enabledAutomations: enabled.length,\n runningRuns: this.activeRuns.size,\n failedLastHour: automations.filter(\n (automation) =>\n (automation.state.lastRunStatus === 'failed' || automation.state.lastRunStatus === 'timeout') &&\n (automation.state.lastRunAtMs ?? 0) >= oneHourAgo,\n ).length,\n nextRun: next?.state.nextRunAtMs\n ? {\n automationId: next.id,\n name: next.name,\n runAtMs: next.state.nextRunAtMs,\n }\n : undefined,\n };\n }\n\n private async recomputeNextRuns(options?: { catchUp?: boolean }): Promise<void> {\n const now = Date.now();\n const automations = listAutomations().map((automation, index) => {\n const next: Automation = {\n ...automation,\n state: { ...automation.state },\n };\n if (next.state.runningRunId) {\n next.state.lastRunStatus = 'failed';\n next.state.lastError = 'Interrupted by gateway restart';\n next.state.consecutiveFailures = (next.state.consecutiveFailures ?? 0) + 1;\n delete next.state.runningRunId;\n }\n const computed = computeNextAutomationRunAtMs(next, now);\n next.state.nextRunAtMs =\n options?.catchUp && computed != null && computed <= now\n ? now + index * MISSED_RUN_STAGGER_MS\n : computed;\n return next;\n });\n saveAutomations(automations);\n }\n\n private armTimer(): void {\n if (this.stopped) return;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const next = listAutomations()\n .filter((automation) => automation.enabled && automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n if (!next?.state.nextRunAtMs) return;\n this.timer = setTimeout(() => {\n void this.onTimer();\n }, timerDelayUntil(next.state.nextRunAtMs)) as TimerHandle;\n this.timer.unref?.();\n }\n\n private async onTimer(): Promise<void> {\n if (this.stopped) return;\n if (this.runningScheduler) {\n this.armTimer();\n return;\n }\n this.runningScheduler = true;\n try {\n const now = Date.now();\n const due = listAutomations()\n .filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'schedule' &&\n automation.state.nextRunAtMs != null &&\n automation.state.nextRunAtMs <= now &&\n !automation.state.runningRunId,\n )\n .slice(0, this.maxConcurrentRuns);\n await Promise.all(due.map((automation) => this.startRun(automation, { manual: false })));\n } finally {\n this.runningScheduler = false;\n this.armTimer();\n }\n }\n\n private async startRun(\n automation: Automation,\n opts: { manual: boolean; event?: AutomationEvent },\n ): Promise<AutomationRun> {\n const now = Date.now();\n const run: AutomationRun = {\n id: randomUUID(),\n automationId: automation.id,\n automationName: automation.name,\n status: 'queued',\n triggerSnapshot: automation.trigger,\n actionSnapshot: automation.action,\n manual: opts.manual,\n createdAtMs: now,\n };\n saveAutomationRun(run);\n this.appendRunEvent(\n run,\n 'run.queued',\n opts.event\n ? `Event ${opts.event.type} queued automation`\n : opts.manual\n ? 'Manual run queued'\n : 'Scheduled run queued',\n {\n trigger: automation.trigger,\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n event: opts.event,\n },\n );\n const nextAutomation: Automation = {\n ...automation,\n state: {\n ...automation.state,\n runningRunId: run.id,\n },\n };\n saveAutomation(nextAutomation);\n\n void this.executeRun(nextAutomation, run).catch((err) => {\n log.error(\n { err, automationId: automation.id, runId: run.id },\n `Automation run failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n\n return run;\n }\n\n private async executeRun(automation: Automation, initialRun: AutomationRun): Promise<void> {\n const controller = new AbortController();\n this.activeRuns.set(initialRun.id, controller);\n const startedAtMs = Date.now();\n let run: AutomationRun = {\n ...initialRun,\n status: 'running',\n startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.started', 'Automation run started');\n\n let status: AutomationRunStatus = 'failed';\n let error: string | undefined;\n let activePhase: 'action' | 'after_run' = 'action';\n try {\n this.appendRunEvent(run, 'action.started', `Running ${automation.action.kind} action`, {\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n const outcome = await this.executor.execute(automation, run, controller.signal);\n status = outcome.status;\n error = outcome.error;\n this.appendRunEvent(\n run,\n status === 'succeeded' ? 'action.completed' : 'action.failed',\n status === 'succeeded'\n ? `${automation.action.kind} action completed`\n : `${automation.action.kind} action ${status}`,\n {\n summary: outcome.summary,\n error: outcome.error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n },\n );\n run = {\n ...run,\n status,\n summary: outcome.summary,\n error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n };\n if ((automation.safety?.mode ?? 'auto_apply') !== 'auto_apply' && automation.afterRun?.kind === 'webhook') {\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook skipped by safety mode', {\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n } else if (automation.afterRun?.kind === 'webhook') {\n activePhase = 'after_run';\n this.appendRunEvent(run, 'after_run.started', 'Calling after-run webhook', {\n url: automation.afterRun.url,\n });\n await this.postAfterRunWebhook(automation.afterRun.url, run);\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook completed');\n }\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n status = controller.signal.aborted ? 'cancelled' : 'failed';\n this.appendRunEvent(run, activePhase === 'after_run' ? 'after_run.failed' : 'action.failed', `Automation run ${status}`, {\n error,\n });\n run = { ...run, status, error };\n } finally {\n const endedAtMs = Date.now();\n run = {\n ...run,\n status,\n endedAtMs,\n durationMs: endedAtMs - startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.completed', `Automation run ${status}`, {\n status,\n durationMs: run.durationMs,\n error,\n });\n this.activeRuns.delete(initialRun.id);\n this.finishAutomationRun(automation.id, status, error, endedAtMs);\n }\n }\n\n private appendRunEvent(\n run: Pick<AutomationRun, 'id' | 'automationId'>,\n type: AutomationRunEventType,\n message: string,\n data?: unknown,\n ): void {\n appendAutomationRunEvent({\n id: randomUUID(),\n runId: run.id,\n automationId: run.automationId,\n type,\n message,\n data,\n createdAtMs: Date.now(),\n });\n }\n\n private finishAutomationRun(\n automationId: string,\n status: AutomationRunStatus,\n error: string | undefined,\n finishedAtMs: number,\n ): void {\n const current = getAutomation(automationId);\n if (!current) return;\n const failed = status === 'failed' || status === 'timeout';\n const consecutiveFailures = failed ? (current.state.consecutiveFailures ?? 0) + 1 : 0;\n const disableAfter = current.reliability?.disableAfterConsecutiveFailures;\n const shouldDisable = disableAfter != null && consecutiveFailures >= disableAfter;\n const next: Automation = {\n ...current,\n enabled: shouldDisable ? false : current.enabled,\n state: {\n ...current.state,\n lastRunAtMs: finishedAtMs,\n lastRunStatus: status,\n lastError: error,\n consecutiveFailures,\n },\n updatedAtMs: shouldDisable ? finishedAtMs : current.updatedAtMs,\n };\n delete next.state.runningRunId;\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, finishedAtMs);\n saveAutomation(next);\n this.armTimer();\n }\n\n private async postAfterRunWebhook(url: string, run: AutomationRun): Promise<void> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ run }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Automation webhook failed: HTTP ${response.status}${text ? ` ${text.slice(0, 200)}` : ''}`);\n }\n }\n}\n\nfunction matchesAutomationEvent(\n trigger: Extract<Automation['trigger'], { kind: 'event' }>,\n event: AutomationEvent,\n): boolean {\n if (trigger.eventType !== event.type) return false;\n if (trigger.source && trigger.source !== event.source) return false;\n const payloadMatch = trigger.payloadMatch;\n if (!payloadMatch) return true;\n const payload = event.payload ?? {};\n return Object.entries(payloadMatch).every(([key, expected]) => Object.is(payload[key], expected));\n}\n\nfunction readAutomationEventFromRunEvent(event: AutomationRunEvent): AutomationEvent | null {\n if (!event.data || typeof event.data !== 'object' || Array.isArray(event.data)) return null;\n const raw = (event.data as { event?: unknown }).event;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const candidate = raw as Partial<AutomationEvent>;\n if (typeof candidate.type !== 'string' || !candidate.type.trim()) return null;\n return {\n type: candidate.type,\n source: typeof candidate.source === 'string' ? candidate.source : undefined,\n payload: candidate.payload && typeof candidate.payload === 'object' && !Array.isArray(candidate.payload)\n ? candidate.payload as Record<string, unknown>\n : undefined,\n occurredAtMs: typeof candidate.occurredAtMs === 'number' ? candidate.occurredAtMs : undefined,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './action-executor.js';
2
+ export * from './automation-service.js';
@@ -0,0 +1,3 @@
1
+ import { AutomationActionExecutor } from "./action-executor.js";
2
+ import { AutomationAlreadyRunningError, AutomationService } from "./automation-service.js";
3
+ export { AutomationActionExecutor, AutomationAlreadyRunningError, AutomationService };
@@ -0,0 +1,6 @@
1
+ import type { Automation } from '../domain/types.js';
2
+ export declare function listAutomations(): Automation[];
3
+ export declare function getAutomation(automationId: string): Automation | null;
4
+ export declare function saveAutomation(automation: Automation): void;
5
+ export declare function saveAutomations(automations: Automation[]): void;
6
+ export declare function deleteAutomation(automationId: string): boolean;
@@ -0,0 +1,61 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
2
+ import { AutomationSchema } from "../domain/validation.js";
3
+ //#region src/automations/storage/automation-repository.ts
4
+ function parseJson(value) {
5
+ if (!value) return void 0;
6
+ return JSON.parse(value);
7
+ }
8
+ function bindJson(value) {
9
+ return value === void 0 ? null : JSON.stringify(value);
10
+ }
11
+ function rowToAutomation(row) {
12
+ return AutomationSchema.parse({
13
+ id: row.automation_id,
14
+ name: row.name,
15
+ description: row.description ?? void 0,
16
+ enabled: row.enabled !== 0,
17
+ trigger: parseJson(row.trigger_json),
18
+ action: parseJson(row.action_json),
19
+ safety: parseJson(row.safety_json) ?? { mode: "auto_apply" },
20
+ afterRun: parseJson(row.after_run_json),
21
+ reliability: parseJson(row.reliability_json),
22
+ state: parseJson(row.state_json) ?? {},
23
+ createdAtMs: row.created_at_ms,
24
+ updatedAtMs: row.updated_at_ms
25
+ });
26
+ }
27
+ function upsertAutomation(db, automation) {
28
+ db.prepare(`INSERT OR REPLACE INTO automations (
29
+ automation_id, name, description, enabled, trigger_json, action_json,
30
+ safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms
31
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(automation.id, automation.name, automation.description ?? null, automation.enabled ? 1 : 0, JSON.stringify(automation.trigger), JSON.stringify(automation.action), bindJson(automation.safety ?? { mode: "auto_apply" }), bindJson(automation.afterRun), bindJson(automation.reliability), JSON.stringify(automation.state ?? {}), automation.createdAtMs, automation.updatedAtMs);
32
+ }
33
+ const AUTOMATION_SELECT = `
34
+ SELECT automation_id, name, description, enabled, trigger_json, action_json,
35
+ safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms
36
+ FROM automations
37
+ `;
38
+ function listAutomations() {
39
+ return getSqliteDatabase().prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`).all().map(rowToAutomation);
40
+ }
41
+ function getAutomation(automationId) {
42
+ const row = getSqliteDatabase().prepare(`${AUTOMATION_SELECT} WHERE automation_id = ?`).get(automationId);
43
+ return row ? rowToAutomation(row) : null;
44
+ }
45
+ function saveAutomation(automation) {
46
+ runSqliteWriteTransaction((db) => upsertAutomation(db, automation));
47
+ }
48
+ function saveAutomations(automations) {
49
+ runSqliteWriteTransaction((db) => {
50
+ for (const automation of automations) upsertAutomation(db, automation);
51
+ });
52
+ }
53
+ function deleteAutomation(automationId) {
54
+ return runSqliteWriteTransaction((db) => {
55
+ return db.prepare(`DELETE FROM automations WHERE automation_id = ?`).run(automationId).changes > 0;
56
+ });
57
+ }
58
+ //#endregion
59
+ export { deleteAutomation, getAutomation, listAutomations, saveAutomation, saveAutomations };
60
+
61
+ //# sourceMappingURL=automation-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-repository.ts"],"sourcesContent":["import type { Automation } from '../domain/types.js';\nimport { AutomationSchema } from '../domain/validation.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\ntype AutomationRow = {\n automation_id: string;\n name: string;\n description: string | null;\n enabled: number;\n trigger_json: string;\n action_json: string;\n safety_json: string | null;\n after_run_json: string | null;\n reliability_json: string | null;\n state_json: string;\n created_at_ms: number;\n updated_at_ms: number;\n};\n\nfunction parseJson(value: string | null): unknown {\n if (!value) return undefined;\n return JSON.parse(value);\n}\n\nfunction bindJson(value: unknown): string | null {\n return value === undefined ? null : JSON.stringify(value);\n}\n\nfunction rowToAutomation(row: AutomationRow): Automation {\n return AutomationSchema.parse({\n id: row.automation_id,\n name: row.name,\n description: row.description ?? undefined,\n enabled: row.enabled !== 0,\n trigger: parseJson(row.trigger_json),\n action: parseJson(row.action_json),\n safety: parseJson(row.safety_json) ?? { mode: 'auto_apply' },\n afterRun: parseJson(row.after_run_json),\n reliability: parseJson(row.reliability_json),\n state: parseJson(row.state_json) ?? {},\n createdAtMs: row.created_at_ms,\n updatedAtMs: row.updated_at_ms,\n }) as Automation;\n}\n\nfunction upsertAutomation(db: ReturnType<typeof getSqliteDatabase>, automation: Automation): void {\n db.prepare(\n `INSERT OR REPLACE INTO automations (\n automation_id, name, description, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n automation.id,\n automation.name,\n automation.description ?? null,\n automation.enabled ? 1 : 0,\n JSON.stringify(automation.trigger),\n JSON.stringify(automation.action),\n bindJson(automation.safety ?? { mode: 'auto_apply' }),\n bindJson(automation.afterRun),\n bindJson(automation.reliability),\n JSON.stringify(automation.state ?? {}),\n automation.createdAtMs,\n automation.updatedAtMs,\n );\n}\n\nconst AUTOMATION_SELECT = `\n SELECT automation_id, name, description, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n FROM automations\n`;\n\nexport function listAutomations(): Automation[] {\n const rows = getSqliteDatabase()\n .prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`)\n .all() as AutomationRow[];\n return rows.map(rowToAutomation);\n}\n\nexport function getAutomation(automationId: string): Automation | null {\n const row = getSqliteDatabase()\n .prepare(`${AUTOMATION_SELECT} WHERE automation_id = ?`)\n .get(automationId) as AutomationRow | undefined;\n return row ? rowToAutomation(row) : null;\n}\n\nexport function saveAutomation(automation: Automation): void {\n runSqliteWriteTransaction((db) => upsertAutomation(db, automation));\n}\n\nexport function saveAutomations(automations: Automation[]): void {\n runSqliteWriteTransaction((db) => {\n for (const automation of automations) {\n upsertAutomation(db, automation);\n }\n });\n}\n\nexport function deleteAutomation(automationId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const result = db.prepare(`DELETE FROM automations WHERE automation_id = ?`).run(automationId);\n return result.changes > 0;\n });\n}\n"],"mappings":";;;AAmBA,SAAS,UAAU,OAA+B;AAChD,KAAI,CAAC,MAAO,QAAO,KAAA;AACnB,QAAO,KAAK,MAAM,MAAM;;AAG1B,SAAS,SAAS,OAA+B;AAC/C,QAAO,UAAU,KAAA,IAAY,OAAO,KAAK,UAAU,MAAM;;AAG3D,SAAS,gBAAgB,KAAgC;AACvD,QAAO,iBAAiB,MAAM;EAC5B,IAAI,IAAI;EACR,MAAM,IAAI;EACV,aAAa,IAAI,eAAe,KAAA;EAChC,SAAS,IAAI,YAAY;EACzB,SAAS,UAAU,IAAI,aAAa;EACpC,QAAQ,UAAU,IAAI,YAAY;EAClC,QAAQ,UAAU,IAAI,YAAY,IAAI,EAAE,MAAM,cAAc;EAC5D,UAAU,UAAU,IAAI,eAAe;EACvC,aAAa,UAAU,IAAI,iBAAiB;EAC5C,OAAO,UAAU,IAAI,WAAW,IAAI,EAAE;EACtC,aAAa,IAAI;EACjB,aAAa,IAAI;EAClB,CAAC;;AAGJ,SAAS,iBAAiB,IAA0C,YAA8B;AAChG,IAAG,QACD;;;mDAID,CAAC,IACA,WAAW,IACX,WAAW,MACX,WAAW,eAAe,MAC1B,WAAW,UAAU,IAAI,GACzB,KAAK,UAAU,WAAW,QAAQ,EAClC,KAAK,UAAU,WAAW,OAAO,EACjC,SAAS,WAAW,UAAU,EAAE,MAAM,cAAc,CAAC,EACrD,SAAS,WAAW,SAAS,EAC7B,SAAS,WAAW,YAAY,EAChC,KAAK,UAAU,WAAW,SAAS,EAAE,CAAC,EACtC,WAAW,aACX,WAAW,YACZ;;AAGH,MAAM,oBAAoB;;;;;AAM1B,SAAgB,kBAAgC;AAI9C,QAHa,mBAAmB,CAC7B,QAAQ,GAAG,kBAAkB,8BAA8B,CAC3D,KACQ,CAAC,IAAI,gBAAgB;;AAGlC,SAAgB,cAAc,cAAyC;CACrE,MAAM,MAAM,mBAAmB,CAC5B,QAAQ,GAAG,kBAAkB,0BAA0B,CACvD,IAAI,aAAa;AACpB,QAAO,MAAM,gBAAgB,IAAI,GAAG;;AAGtC,SAAgB,eAAe,YAA8B;AAC3D,4BAA2B,OAAO,iBAAiB,IAAI,WAAW,CAAC;;AAGrE,SAAgB,gBAAgB,aAAiC;AAC/D,4BAA2B,OAAO;AAChC,OAAK,MAAM,cAAc,YACvB,kBAAiB,IAAI,WAAW;GAElC;;AAGJ,SAAgB,iBAAiB,cAA+B;AAC9D,QAAO,2BAA2B,OAAO;AAEvC,SADe,GAAG,QAAQ,kDAAkD,CAAC,IAAI,aACpE,CAAC,UAAU;GACxB"}
@@ -0,0 +1,17 @@
1
+ import type { AutomationProductEventRun, AutomationRun, AutomationRunEvent } from '../domain/types.js';
2
+ export declare function appendAutomationRunEvent(event: AutomationRunEvent): void;
3
+ export declare function saveAutomationRun(run: AutomationRun): void;
4
+ export declare function listAutomationRunEvents(runId: string): AutomationRunEvent[];
5
+ export declare function getAutomationRun(runId: string): AutomationRun | null;
6
+ export declare function listAutomationRuns(options?: {
7
+ automationId?: string;
8
+ limit?: number;
9
+ }): AutomationRun[];
10
+ export declare function listAutomationRunsForProductEvent(options: {
11
+ eventType: string;
12
+ source?: string;
13
+ payloadKey?: string;
14
+ payloadValue?: string;
15
+ limit?: number;
16
+ }): AutomationProductEventRun[];
17
+ export declare function deleteAutomationRunsForAutomation(automationId: string): void;
@@ -0,0 +1,130 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
2
+ //#region src/automations/storage/automation-run-repository.ts
3
+ const MAX_RUNS_PER_AUTOMATION = 2e3;
4
+ const TRIM_TO_RUNS = 1500;
5
+ function rowToRun(row) {
6
+ return {
7
+ id: row.run_id,
8
+ automationId: row.automation_id,
9
+ automationName: row.automation_name,
10
+ status: row.status,
11
+ triggerSnapshot: JSON.parse(row.trigger_snapshot_json),
12
+ actionSnapshot: JSON.parse(row.action_snapshot_json),
13
+ manual: row.manual !== 0,
14
+ createdAtMs: row.created_at_ms,
15
+ startedAtMs: row.started_at_ms ?? void 0,
16
+ endedAtMs: row.ended_at_ms ?? void 0,
17
+ durationMs: row.duration_ms ?? void 0,
18
+ summary: row.summary ?? void 0,
19
+ error: row.error ?? void 0,
20
+ sessionKey: row.session_key ?? void 0,
21
+ workflowRunId: row.workflow_run_id ?? void 0,
22
+ model: row.model ?? void 0
23
+ };
24
+ }
25
+ function rowToEvent(row) {
26
+ return {
27
+ id: row.event_id,
28
+ runId: row.run_id,
29
+ automationId: row.automation_id,
30
+ type: row.type,
31
+ message: row.message,
32
+ data: row.data_json ? JSON.parse(row.data_json) : void 0,
33
+ createdAtMs: row.created_at_ms
34
+ };
35
+ }
36
+ function trimRuns(db, automationId) {
37
+ const countRow = db.prepare(`SELECT COUNT(*) AS count FROM automation_runs WHERE automation_id = ?`).get(automationId);
38
+ if (countRow.count <= MAX_RUNS_PER_AUTOMATION) return;
39
+ const staleRunIds = db.prepare(`SELECT run_id FROM automation_runs WHERE automation_id = ?
40
+ ORDER BY created_at_ms ASC LIMIT ?`).all(automationId, countRow.count - TRIM_TO_RUNS);
41
+ for (const { run_id: runId } of staleRunIds) {
42
+ db.prepare(`DELETE FROM automation_run_events WHERE run_id = ?`).run(runId);
43
+ db.prepare(`DELETE FROM automation_runs WHERE run_id = ?`).run(runId);
44
+ }
45
+ }
46
+ function appendAutomationRunEvent(event) {
47
+ runSqliteWriteTransaction((db) => {
48
+ db.prepare(`INSERT INTO automation_run_events (
49
+ event_id, run_id, automation_id, type, message, data_json, created_at_ms
50
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(event.id, event.runId, event.automationId, event.type, event.message, event.data === void 0 ? null : JSON.stringify(event.data), event.createdAtMs);
51
+ });
52
+ }
53
+ function saveAutomationRun(run) {
54
+ runSqliteWriteTransaction((db) => {
55
+ db.prepare(`INSERT OR REPLACE INTO automation_runs (
56
+ run_id, automation_id, automation_name, status, trigger_snapshot_json,
57
+ action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,
58
+ duration_ms, summary, error, session_key, workflow_run_id, model
59
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(run.id, run.automationId, run.automationName, run.status, JSON.stringify(run.triggerSnapshot), JSON.stringify(run.actionSnapshot), run.manual ? 1 : 0, run.createdAtMs, run.startedAtMs ?? null, run.endedAtMs ?? null, run.durationMs ?? null, run.summary ?? null, run.error ?? null, run.sessionKey ?? null, run.workflowRunId ?? null, run.model ?? null);
60
+ trimRuns(db, run.automationId);
61
+ });
62
+ }
63
+ function listAutomationRunEvents(runId) {
64
+ return getSqliteDatabase().prepare(`SELECT event_id, run_id, automation_id, type, message, data_json, created_at_ms
65
+ FROM automation_run_events
66
+ WHERE run_id = ?
67
+ ORDER BY created_at_ms ASC`).all(runId).map(rowToEvent);
68
+ }
69
+ function getAutomationRun(runId) {
70
+ const row = getSqliteDatabase().prepare(`SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,
71
+ action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,
72
+ duration_ms, summary, error, session_key, workflow_run_id, model
73
+ FROM automation_runs
74
+ WHERE run_id = ?`).get(runId);
75
+ return row ? rowToRun(row) : null;
76
+ }
77
+ function listAutomationRuns(options) {
78
+ const limit = Math.min(Math.max(options?.limit ?? 50, 1), 500);
79
+ const db = getSqliteDatabase();
80
+ const base = `SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,
81
+ action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,
82
+ duration_ms, summary, error, session_key, workflow_run_id, model
83
+ FROM automation_runs`;
84
+ return (options?.automationId ? db.prepare(`${base} WHERE automation_id = ? ORDER BY created_at_ms DESC LIMIT ?`).all(options.automationId, limit) : db.prepare(`${base} ORDER BY created_at_ms DESC LIMIT ?`).all(limit)).map(rowToRun);
85
+ }
86
+ function listAutomationRunsForProductEvent(options) {
87
+ const limit = Math.min(Math.max(options.limit ?? 10, 1), 100);
88
+ const clauses = [`e.type = 'run.queued'`, `json_extract(e.data_json, '$.event.type') = ?`];
89
+ const params = [options.eventType, limit];
90
+ if (options.source) {
91
+ clauses.push(`json_extract(e.data_json, '$.event.source') = ?`);
92
+ params.splice(params.length - 1, 0, options.source);
93
+ }
94
+ if (options.payloadKey && options.payloadValue !== void 0) {
95
+ clauses.push(`json_extract(e.data_json, '$.event.payload.' || ?) = ?`);
96
+ params.splice(params.length - 1, 0, options.payloadKey, options.payloadValue);
97
+ }
98
+ return getSqliteDatabase().prepare(`SELECT r.run_id, r.automation_id, r.automation_name, r.status, r.trigger_snapshot_json,
99
+ r.action_snapshot_json, r.manual, r.created_at_ms, r.started_at_ms, r.ended_at_ms,
100
+ r.duration_ms, r.summary, r.error, r.session_key, r.workflow_run_id, r.model,
101
+ e.event_id, e.run_id AS event_run_id, e.automation_id AS event_automation_id,
102
+ e.type AS event_type, e.message AS event_message, e.data_json AS event_data_json,
103
+ e.created_at_ms AS event_created_at_ms
104
+ FROM automation_run_events e
105
+ JOIN automation_runs r ON r.run_id = e.run_id
106
+ WHERE ${clauses.join(" AND ")}
107
+ ORDER BY e.created_at_ms DESC
108
+ LIMIT ?`).all(...params).map((row) => ({
109
+ run: rowToRun(row),
110
+ triggerEvent: rowToEvent({
111
+ event_id: row.event_id,
112
+ run_id: row.event_run_id,
113
+ automation_id: row.event_automation_id,
114
+ type: row.event_type,
115
+ message: row.event_message,
116
+ data_json: row.event_data_json,
117
+ created_at_ms: row.event_created_at_ms
118
+ })
119
+ }));
120
+ }
121
+ function deleteAutomationRunsForAutomation(automationId) {
122
+ runSqliteWriteTransaction((db) => {
123
+ db.prepare(`DELETE FROM automation_run_events WHERE automation_id = ?`).run(automationId);
124
+ db.prepare(`DELETE FROM automation_runs WHERE automation_id = ?`).run(automationId);
125
+ });
126
+ }
127
+ //#endregion
128
+ export { appendAutomationRunEvent, deleteAutomationRunsForAutomation, getAutomationRun, listAutomationRunEvents, listAutomationRuns, listAutomationRunsForProductEvent, saveAutomationRun };
129
+
130
+ //# sourceMappingURL=automation-run-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation-run-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-run-repository.ts"],"sourcesContent":["import type { AutomationProductEventRun, AutomationRun, AutomationRunEvent } from '../domain/types.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\nconst MAX_RUNS_PER_AUTOMATION = 2000;\nconst TRIM_TO_RUNS = 1500;\n\ntype AutomationRunRow = {\n run_id: string;\n automation_id: string;\n automation_name: string;\n status: string;\n trigger_snapshot_json: string;\n action_snapshot_json: string;\n manual: number;\n created_at_ms: number;\n started_at_ms: number | null;\n ended_at_ms: number | null;\n duration_ms: number | null;\n summary: string | null;\n error: string | null;\n session_key: string | null;\n workflow_run_id: string | null;\n model: string | null;\n};\n\ntype AutomationRunEventRow = {\n event_id: string;\n run_id: string;\n automation_id: string;\n type: string;\n message: string;\n data_json: string | null;\n created_at_ms: number;\n};\n\nfunction rowToRun(row: AutomationRunRow): AutomationRun {\n return {\n id: row.run_id,\n automationId: row.automation_id,\n automationName: row.automation_name,\n status: row.status as AutomationRun['status'],\n triggerSnapshot: JSON.parse(row.trigger_snapshot_json),\n actionSnapshot: JSON.parse(row.action_snapshot_json),\n manual: row.manual !== 0,\n createdAtMs: row.created_at_ms,\n startedAtMs: row.started_at_ms ?? undefined,\n endedAtMs: row.ended_at_ms ?? undefined,\n durationMs: row.duration_ms ?? undefined,\n summary: row.summary ?? undefined,\n error: row.error ?? undefined,\n sessionKey: row.session_key ?? undefined,\n workflowRunId: row.workflow_run_id ?? undefined,\n model: row.model ?? undefined,\n };\n}\n\nfunction rowToEvent(row: AutomationRunEventRow): AutomationRunEvent {\n return {\n id: row.event_id,\n runId: row.run_id,\n automationId: row.automation_id,\n type: row.type as AutomationRunEvent['type'],\n message: row.message,\n data: row.data_json ? JSON.parse(row.data_json) : undefined,\n createdAtMs: row.created_at_ms,\n };\n}\n\nfunction trimRuns(db: ReturnType<typeof getSqliteDatabase>, automationId: string): void {\n const countRow = db\n .prepare(`SELECT COUNT(*) AS count FROM automation_runs WHERE automation_id = ?`)\n .get(automationId) as { count: number };\n if (countRow.count <= MAX_RUNS_PER_AUTOMATION) return;\n const staleRunIds = db\n .prepare(\n `SELECT run_id FROM automation_runs WHERE automation_id = ?\n ORDER BY created_at_ms ASC LIMIT ?`,\n )\n .all(automationId, countRow.count - TRIM_TO_RUNS) as { run_id: string }[];\n for (const { run_id: runId } of staleRunIds) {\n db.prepare(`DELETE FROM automation_run_events WHERE run_id = ?`).run(runId);\n db.prepare(`DELETE FROM automation_runs WHERE run_id = ?`).run(runId);\n }\n}\n\nexport function appendAutomationRunEvent(event: AutomationRunEvent): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO automation_run_events (\n event_id, run_id, automation_id, type, message, data_json, created_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n event.id,\n event.runId,\n event.automationId,\n event.type,\n event.message,\n event.data === undefined ? null : JSON.stringify(event.data),\n event.createdAtMs,\n );\n });\n}\n\nexport function saveAutomationRun(run: AutomationRun): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT OR REPLACE INTO automation_runs (\n run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n run.id,\n run.automationId,\n run.automationName,\n run.status,\n JSON.stringify(run.triggerSnapshot),\n JSON.stringify(run.actionSnapshot),\n run.manual ? 1 : 0,\n run.createdAtMs,\n run.startedAtMs ?? null,\n run.endedAtMs ?? null,\n run.durationMs ?? null,\n run.summary ?? null,\n run.error ?? null,\n run.sessionKey ?? null,\n run.workflowRunId ?? null,\n run.model ?? null,\n );\n trimRuns(db, run.automationId);\n });\n}\n\nexport function listAutomationRunEvents(runId: string): AutomationRunEvent[] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT event_id, run_id, automation_id, type, message, data_json, created_at_ms\n FROM automation_run_events\n WHERE run_id = ?\n ORDER BY created_at_ms ASC`,\n )\n .all(runId) as AutomationRunEventRow[];\n return rows.map(rowToEvent);\n}\n\nexport function getAutomationRun(runId: string): AutomationRun | null {\n const row = getSqliteDatabase()\n .prepare(\n `SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n FROM automation_runs\n WHERE run_id = ?`,\n )\n .get(runId) as AutomationRunRow | undefined;\n return row ? rowToRun(row) : null;\n}\n\nexport function listAutomationRuns(options?: {\n automationId?: string;\n limit?: number;\n}): AutomationRun[] {\n const limit = Math.min(Math.max(options?.limit ?? 50, 1), 500);\n const db = getSqliteDatabase();\n const base = `SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n FROM automation_runs`;\n const rows = options?.automationId\n ? db.prepare(`${base} WHERE automation_id = ? ORDER BY created_at_ms DESC LIMIT ?`).all(options.automationId, limit)\n : db.prepare(`${base} ORDER BY created_at_ms DESC LIMIT ?`).all(limit);\n return (rows as AutomationRunRow[]).map(rowToRun);\n}\n\nexport function listAutomationRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n}): AutomationProductEventRun[] {\n const limit = Math.min(Math.max(options.limit ?? 10, 1), 100);\n const clauses = [\n `e.type = 'run.queued'`,\n `json_extract(e.data_json, '$.event.type') = ?`,\n ];\n const params: Array<string | number> = [options.eventType, limit];\n\n if (options.source) {\n clauses.push(`json_extract(e.data_json, '$.event.source') = ?`);\n params.splice(params.length - 1, 0, options.source);\n }\n if (options.payloadKey && options.payloadValue !== undefined) {\n clauses.push(`json_extract(e.data_json, '$.event.payload.' || ?) = ?`);\n params.splice(params.length - 1, 0, options.payloadKey, options.payloadValue);\n }\n\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT r.run_id, r.automation_id, r.automation_name, r.status, r.trigger_snapshot_json,\n r.action_snapshot_json, r.manual, r.created_at_ms, r.started_at_ms, r.ended_at_ms,\n r.duration_ms, r.summary, r.error, r.session_key, r.workflow_run_id, r.model,\n e.event_id, e.run_id AS event_run_id, e.automation_id AS event_automation_id,\n e.type AS event_type, e.message AS event_message, e.data_json AS event_data_json,\n e.created_at_ms AS event_created_at_ms\n FROM automation_run_events e\n JOIN automation_runs r ON r.run_id = e.run_id\n WHERE ${clauses.join(' AND ')}\n ORDER BY e.created_at_ms DESC\n LIMIT ?`,\n )\n .all(...params) as Array<AutomationRunRow & {\n event_id: string;\n event_run_id: string;\n event_automation_id: string;\n event_type: string;\n event_message: string;\n event_data_json: string | null;\n event_created_at_ms: number;\n }>;\n\n return rows.map((row) => ({\n run: rowToRun(row),\n triggerEvent: rowToEvent({\n event_id: row.event_id,\n run_id: row.event_run_id,\n automation_id: row.event_automation_id,\n type: row.event_type,\n message: row.event_message,\n data_json: row.event_data_json,\n created_at_ms: row.event_created_at_ms,\n }),\n }));\n}\n\nexport function deleteAutomationRunsForAutomation(automationId: string): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(`DELETE FROM automation_run_events WHERE automation_id = ?`).run(automationId);\n db.prepare(`DELETE FROM automation_runs WHERE automation_id = ?`).run(automationId);\n });\n}\n"],"mappings":";;AAGA,MAAM,0BAA0B;AAChC,MAAM,eAAe;AA+BrB,SAAS,SAAS,KAAsC;AACtD,QAAO;EACL,IAAI,IAAI;EACR,cAAc,IAAI;EAClB,gBAAgB,IAAI;EACpB,QAAQ,IAAI;EACZ,iBAAiB,KAAK,MAAM,IAAI,sBAAsB;EACtD,gBAAgB,KAAK,MAAM,IAAI,qBAAqB;EACpD,QAAQ,IAAI,WAAW;EACvB,aAAa,IAAI;EACjB,aAAa,IAAI,iBAAiB,KAAA;EAClC,WAAW,IAAI,eAAe,KAAA;EAC9B,YAAY,IAAI,eAAe,KAAA;EAC/B,SAAS,IAAI,WAAW,KAAA;EACxB,OAAO,IAAI,SAAS,KAAA;EACpB,YAAY,IAAI,eAAe,KAAA;EAC/B,eAAe,IAAI,mBAAmB,KAAA;EACtC,OAAO,IAAI,SAAS,KAAA;EACrB;;AAGH,SAAS,WAAW,KAAgD;AAClE,QAAO;EACL,IAAI,IAAI;EACR,OAAO,IAAI;EACX,cAAc,IAAI;EAClB,MAAM,IAAI;EACV,SAAS,IAAI;EACb,MAAM,IAAI,YAAY,KAAK,MAAM,IAAI,UAAU,GAAG,KAAA;EAClD,aAAa,IAAI;EAClB;;AAGH,SAAS,SAAS,IAA0C,cAA4B;CACtF,MAAM,WAAW,GACd,QAAQ,wEAAwE,CAChF,IAAI,aAAa;AACpB,KAAI,SAAS,SAAS,wBAAyB;CAC/C,MAAM,cAAc,GACjB,QACC;2CAED,CACA,IAAI,cAAc,SAAS,QAAQ,aAAa;AACnD,MAAK,MAAM,EAAE,QAAQ,WAAW,aAAa;AAC3C,KAAG,QAAQ,qDAAqD,CAAC,IAAI,MAAM;AAC3E,KAAG,QAAQ,+CAA+C,CAAC,IAAI,MAAM;;;AAIzE,SAAgB,yBAAyB,OAAiC;AACxE,4BAA2B,OAAO;AAChC,KAAG,QACD;;sCAGD,CAAC,IACA,MAAM,IACN,MAAM,OACN,MAAM,cACN,MAAM,MACN,MAAM,SACN,MAAM,SAAS,KAAA,IAAY,OAAO,KAAK,UAAU,MAAM,KAAK,EAC5D,MAAM,YACP;GACD;;AAGJ,SAAgB,kBAAkB,KAA0B;AAC1D,4BAA2B,OAAO;AAChC,KAAG,QACD;;;;iEAKD,CAAC,IACA,IAAI,IACJ,IAAI,cACJ,IAAI,gBACJ,IAAI,QACJ,KAAK,UAAU,IAAI,gBAAgB,EACnC,KAAK,UAAU,IAAI,eAAe,EAClC,IAAI,SAAS,IAAI,GACjB,IAAI,aACJ,IAAI,eAAe,MACnB,IAAI,aAAa,MACjB,IAAI,cAAc,MAClB,IAAI,WAAW,MACf,IAAI,SAAS,MACb,IAAI,cAAc,MAClB,IAAI,iBAAiB,MACrB,IAAI,SAAS,KACd;AACD,WAAS,IAAI,IAAI,aAAa;GAC9B;;AAGJ,SAAgB,wBAAwB,OAAqC;AAS3E,QARa,mBAAmB,CAC7B,QACC;;;mCAID,CACA,IAAI,MACI,CAAC,IAAI,WAAW;;AAG7B,SAAgB,iBAAiB,OAAqC;CACpE,MAAM,MAAM,mBAAmB,CAC5B,QACC;;;;yBAKD,CACA,IAAI,MAAM;AACb,QAAO,MAAM,SAAS,IAAI,GAAG;;AAG/B,SAAgB,mBAAmB,SAGf;CAClB,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,SAAS,SAAS,IAAI,EAAE,EAAE,IAAI;CAC9D,MAAM,KAAK,mBAAmB;CAC9B,MAAM,OAAO;;;;AAOb,SAHa,SAAS,eAClB,GAAG,QAAQ,GAAG,KAAK,8DAA8D,CAAC,IAAI,QAAQ,cAAc,MAAM,GAClH,GAAG,QAAQ,GAAG,KAAK,sCAAsC,CAAC,IAAI,MAAM,EACpC,IAAI,SAAS;;AAGnD,SAAgB,kCAAkC,SAMlB;CAC9B,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,QAAQ,SAAS,IAAI,EAAE,EAAE,IAAI;CAC7D,MAAM,UAAU,CACd,yBACA,gDACD;CACD,MAAM,SAAiC,CAAC,QAAQ,WAAW,MAAM;AAEjE,KAAI,QAAQ,QAAQ;AAClB,UAAQ,KAAK,kDAAkD;AAC/D,SAAO,OAAO,OAAO,SAAS,GAAG,GAAG,QAAQ,OAAO;;AAErD,KAAI,QAAQ,cAAc,QAAQ,iBAAiB,KAAA,GAAW;AAC5D,UAAQ,KAAK,yDAAyD;AACtE,SAAO,OAAO,OAAO,SAAS,GAAG,GAAG,QAAQ,YAAY,QAAQ,aAAa;;AA2B/E,QAxBa,mBAAmB,CAC7B,QACC;;;;;;;;eAQS,QAAQ,KAAK,QAAQ,CAAC;;gBAGhC,CACA,IAAI,GAAG,OAUC,CAAC,KAAK,SAAS;EACxB,KAAK,SAAS,IAAI;EAClB,cAAc,WAAW;GACvB,UAAU,IAAI;GACd,QAAQ,IAAI;GACZ,eAAe,IAAI;GACnB,MAAM,IAAI;GACV,SAAS,IAAI;GACb,WAAW,IAAI;GACf,eAAe,IAAI;GACpB,CAAC;EACH,EAAE;;AAGL,SAAgB,kCAAkC,cAA4B;AAC5E,4BAA2B,OAAO;AAChC,KAAG,QAAQ,4DAA4D,CAAC,IAAI,aAAa;AACzF,KAAG,QAAQ,sDAAsD,CAAC,IAAI,aAAa;GACnF"}
@@ -0,0 +1,2 @@
1
+ export * from './automation-repository.js';
2
+ export * from './automation-run-repository.js';
@@ -0,0 +1,3 @@
1
+ import { deleteAutomation, getAutomation, listAutomations, saveAutomation, saveAutomations } from "./automation-repository.js";
2
+ import { appendAutomationRunEvent, deleteAutomationRunsForAutomation, getAutomationRun, listAutomationRunEvents, listAutomationRuns, listAutomationRunsForProductEvent, saveAutomationRun } from "./automation-run-repository.js";
3
+ export { appendAutomationRunEvent, deleteAutomation, deleteAutomationRunsForAutomation, getAutomation, getAutomationRun, listAutomationRunEvents, listAutomationRuns, listAutomationRunsForProductEvent, listAutomations, saveAutomation, saveAutomationRun, saveAutomations };
@@ -115,7 +115,7 @@ export interface CreatePipelineParams {
115
115
  botId: string;
116
116
  agentInvoke: PipelineOptions['agentInvoke'];
117
117
  onError?: PipelineOptions['onError'];
118
- /** IANA timezone — matches userTimezone from agent config / USER.md */
118
+ /** IANA timezone — matches userTimezone from config or global user profile */
119
119
  timezone?: string;
120
120
  }
121
121
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","names":[],"sources":["../../../src/channels/pipeline.ts"],"sourcesContent":["/**\n * Message Processing Pipeline\n * \n * Three-stage pipeline:\n * - Preflight: Filter empty messages, self-messages, detect commands\n * - Process: Transform format, extract metadata\n * - Delivery: Send to Agent\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { createLogger, runWithLogContext, updateAsyncLogContext } from '../utils/logger.js';\nimport type { AgentResponse } from './plugin-types.js';\nimport { formatEnvelopeTimestamp } from './envelope-timestamp.js';\n\n// Re-export for convenience\nexport type { AgentResponse } from './plugin-types.js';\n\nconst log = createLogger('Pipeline');\n\nfunction pipelineLogRequestId(ctx: PipelineMessageContext): string {\n const raw = ctx.metadata?.requestId;\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return raw.trim();\n }\n return randomUUID();\n}\n\nfunction pipelineLogSessionId(ctx: PipelineMessageContext): string {\n const sk = ctx.metadata?.sessionKey;\n if (typeof sk === 'string' && sk.trim().length > 0) {\n return sk.trim();\n }\n return `${ctx.channel}:${ctx.chatId}`;\n}\n\n// ============================================\n// Types\n// ============================================\n\nexport interface PipelineMessageContext {\n /** Channel identifier */\n channel: string;\n /** Account ID */\n accountId: string;\n /** Chat ID */\n chatId: string;\n /** Sender ID */\n senderId: string;\n /** Message content */\n content: string;\n /** Original message metadata */\n metadata: Record<string, unknown>;\n /** Is group chat */\n isGroup: boolean;\n /** Is direct message */\n isDm: boolean;\n /** Thread ID (optional) */\n threadId?: string;\n /** Message ID (optional) */\n messageId?: string;\n}\n\nexport interface PipelineMediaRef {\n type: 'photo' | 'video' | 'audio' | 'document' | 'voice';\n fileId: string;\n mimeType?: string;\n fileName?: string;\n url?: string;\n}\n\n// ============================================\n// Handler Interfaces\n// ============================================\n\nexport interface PreflightHandler {\n /** Handler name */\n name: string;\n /** Preflight - return null to skip message */\n preflight?(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null>;\n}\n\nexport interface ProcessHandler {\n /** Handler name */\n name: string;\n /** Process message - transform and extract */\n process(ctx: PipelineMessageContext): Promise<PipelineMessageContext>;\n}\n\nexport interface DeliveryHandler {\n /** Handler name */\n name: string;\n /** Deliver message to Agent */\n deliver(ctx: PipelineMessageContext, response: AgentResponse): Promise<void>;\n}\n\n// ============================================\n// Pipeline Options\n// ============================================\n\nexport interface PipelineOptions {\n /** Channel name */\n channel: string;\n /** Preflight handlers */\n preflightHandlers?: PreflightHandler[];\n /** Process handlers */\n processHandlers?: ProcessHandler[];\n /** Delivery handlers */\n deliveryHandlers?: DeliveryHandler[];\n /** Agent callback */\n agentInvoke?: (ctx: PipelineMessageContext) => Promise<AgentResponse>;\n /** Error handler */\n onError?: (err: unknown, ctx: PipelineMessageContext) => void;\n}\n\n// ============================================\n// Pipeline Implementation\n// ============================================\n\nexport class MessagePipeline {\n private channel: string;\n private preflightHandlers: PreflightHandler[];\n private processHandlers: ProcessHandler[];\n private deliveryHandlers: DeliveryHandler[];\n private agentInvoke?: PipelineOptions['agentInvoke'];\n private onError?: PipelineOptions['onError'];\n\n constructor(options: PipelineOptions) {\n this.channel = options.channel;\n this.preflightHandlers = options.preflightHandlers ?? [];\n this.processHandlers = options.processHandlers ?? [];\n this.deliveryHandlers = options.deliveryHandlers ?? [];\n this.agentInvoke = options.agentInvoke;\n this.onError = options.onError;\n }\n\n /**\n * Handle inbound message\n */\n async handleMessage(ctx: PipelineMessageContext): Promise<void> {\n const channel = this.channel;\n const requestId = pipelineLogRequestId(ctx);\n\n await runWithLogContext(\n {\n requestId,\n sessionId: pipelineLogSessionId(ctx),\n },\n async () => {\n // 1. Preflight stage\n let processedCtx = await this.runPreflight(ctx);\n if (!processedCtx) {\n log.debug({ channel, chatId: ctx.chatId }, 'Message filtered in preflight');\n return;\n }\n\n // 2. Process stage\n try {\n processedCtx = await this.runProcess(processedCtx);\n } catch (err) {\n log.error({ channel, err, phase: 'channel.inbound' }, `Process handler error: ${err instanceof Error ? err.message : String(err)}`);\n this.onError?.(err, processedCtx);\n return;\n }\n\n const resolvedSk = processedCtx.metadata?.sessionKey;\n if (typeof resolvedSk === 'string' && resolvedSk.trim().length > 0) {\n updateAsyncLogContext({ sessionId: resolvedSk.trim() });\n }\n\n // 3. Deliver to Agent\n if (!this.agentInvoke) {\n log.warn({ channel }, 'No agentInvoke configured');\n return;\n }\n\n let response: AgentResponse;\n try {\n response = await this.agentInvoke(processedCtx);\n } catch (err) {\n log.error({ channel, err }, 'Agent invocation error');\n this.onError?.(err, processedCtx);\n return;\n }\n\n // 4. Delivery stage\n try {\n await this.runDelivery(processedCtx, response);\n } catch (err) {\n log.error({ channel, err }, 'Delivery handler error');\n this.onError?.(err, processedCtx);\n }\n },\n );\n }\n\n private async runPreflight(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null> {\n for (const handler of this.preflightHandlers) {\n try {\n const result = await handler.preflight?.(ctx);\n if (!result) {\n log.debug({ channel: this.channel, handler: handler.name }, 'Preflight filtered message');\n return null;\n }\n ctx = result;\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Preflight handler error');\n return null;\n }\n }\n return ctx;\n }\n\n private async runProcess(ctx: PipelineMessageContext): Promise<PipelineMessageContext> {\n for (const handler of this.processHandlers) {\n try {\n ctx = await handler.process(ctx);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Process handler error');\n throw err;\n }\n }\n return ctx;\n }\n\n private async runDelivery(ctx: PipelineMessageContext, response: AgentResponse): Promise<void> {\n for (const handler of this.deliveryHandlers) {\n try {\n await handler.deliver(ctx, response);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Delivery handler error');\n throw err;\n }\n }\n }\n}\n\n// ============================================\n// Standard Handlers\n// ============================================\n\n/**\n * Create filter-self handler\n */\nexport function createFilterSelfHandler(currentBotId: string): PreflightHandler {\n return {\n name: 'filterSelf',\n preflight: async (ctx) => {\n if (ctx.senderId === currentBotId) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-empty handler\n */\nexport function createFilterEmptyHandler(): PreflightHandler {\n return {\n name: 'filterEmpty',\n preflight: async (ctx) => {\n const content = ctx.content?.trim() ?? '';\n if (!content && (!ctx.metadata.media || (ctx.metadata.media as PipelineMediaRef[]).length === 0)) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-commands handler\n */\nexport function createFilterCommandsHandler(commands: string[]): PreflightHandler {\n const commandSet = new Set(commands.map(c => c.toLowerCase()));\n return {\n name: 'filterCommands',\n preflight: async (ctx) => {\n const firstWord = ctx.content?.split(/\\s/)[0]?.toLowerCase() ?? '';\n if (firstWord.startsWith('/') && commandSet.has(firstWord.slice(1))) {\n ctx.metadata.isCommand = true;\n ctx.metadata.command = firstWord.slice(1);\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create standard preflight handlers\n */\nexport function standardPreflightHandlers(botId: string): PreflightHandler[] {\n return [\n createFilterSelfHandler(botId),\n createFilterEmptyHandler(),\n createFilterCommandsHandler(['start', 'help', 'status', 'stop']),\n ];\n}\n\n/**\n * Prepends a per-turn `[YYYY-MM-DD HH:MM TZ]` prefix to inbound text so the model has\n * a stable \"now\" without changing the system prompt (prompt-cache friendly).\n */\nexport function createEnvelopeTimestampHandler(timezone?: string): ProcessHandler {\n return {\n name: 'envelopeTimestamp',\n process: async (ctx) => {\n const text = ctx.content?.trim();\n if (!text) {\n return ctx;\n }\n const timestamp = formatEnvelopeTimestamp(timezone);\n return { ...ctx, content: `[${timestamp}] ${ctx.content}` };\n },\n };\n}\n\n/**\n * Create standard process handlers\n */\nexport function standardProcessHandlers(timezone?: string): ProcessHandler[] {\n return [createEnvelopeTimestampHandler(timezone)];\n}\n\n// ============================================\n// Factory\n// ============================================\n\nexport interface CreatePipelineParams {\n channel: string;\n botId: string;\n agentInvoke: PipelineOptions['agentInvoke'];\n onError?: PipelineOptions['onError'];\n /** IANA timezone — matches userTimezone from agent config / USER.md */\n timezone?: string;\n}\n\n/**\n * Create message processing pipeline\n */\nexport function createPipeline(params: CreatePipelineParams): MessagePipeline {\n return new MessagePipeline({\n channel: params.channel,\n preflightHandlers: standardPreflightHandlers(params.botId),\n processHandlers: standardProcessHandlers(params.timezone),\n agentInvoke: params.agentInvoke,\n onError: params.onError,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","names":[],"sources":["../../../src/channels/pipeline.ts"],"sourcesContent":["/**\n * Message Processing Pipeline\n * \n * Three-stage pipeline:\n * - Preflight: Filter empty messages, self-messages, detect commands\n * - Process: Transform format, extract metadata\n * - Delivery: Send to Agent\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { createLogger, runWithLogContext, updateAsyncLogContext } from '../utils/logger.js';\nimport type { AgentResponse } from './plugin-types.js';\nimport { formatEnvelopeTimestamp } from './envelope-timestamp.js';\n\n// Re-export for convenience\nexport type { AgentResponse } from './plugin-types.js';\n\nconst log = createLogger('Pipeline');\n\nfunction pipelineLogRequestId(ctx: PipelineMessageContext): string {\n const raw = ctx.metadata?.requestId;\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return raw.trim();\n }\n return randomUUID();\n}\n\nfunction pipelineLogSessionId(ctx: PipelineMessageContext): string {\n const sk = ctx.metadata?.sessionKey;\n if (typeof sk === 'string' && sk.trim().length > 0) {\n return sk.trim();\n }\n return `${ctx.channel}:${ctx.chatId}`;\n}\n\n// ============================================\n// Types\n// ============================================\n\nexport interface PipelineMessageContext {\n /** Channel identifier */\n channel: string;\n /** Account ID */\n accountId: string;\n /** Chat ID */\n chatId: string;\n /** Sender ID */\n senderId: string;\n /** Message content */\n content: string;\n /** Original message metadata */\n metadata: Record<string, unknown>;\n /** Is group chat */\n isGroup: boolean;\n /** Is direct message */\n isDm: boolean;\n /** Thread ID (optional) */\n threadId?: string;\n /** Message ID (optional) */\n messageId?: string;\n}\n\nexport interface PipelineMediaRef {\n type: 'photo' | 'video' | 'audio' | 'document' | 'voice';\n fileId: string;\n mimeType?: string;\n fileName?: string;\n url?: string;\n}\n\n// ============================================\n// Handler Interfaces\n// ============================================\n\nexport interface PreflightHandler {\n /** Handler name */\n name: string;\n /** Preflight - return null to skip message */\n preflight?(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null>;\n}\n\nexport interface ProcessHandler {\n /** Handler name */\n name: string;\n /** Process message - transform and extract */\n process(ctx: PipelineMessageContext): Promise<PipelineMessageContext>;\n}\n\nexport interface DeliveryHandler {\n /** Handler name */\n name: string;\n /** Deliver message to Agent */\n deliver(ctx: PipelineMessageContext, response: AgentResponse): Promise<void>;\n}\n\n// ============================================\n// Pipeline Options\n// ============================================\n\nexport interface PipelineOptions {\n /** Channel name */\n channel: string;\n /** Preflight handlers */\n preflightHandlers?: PreflightHandler[];\n /** Process handlers */\n processHandlers?: ProcessHandler[];\n /** Delivery handlers */\n deliveryHandlers?: DeliveryHandler[];\n /** Agent callback */\n agentInvoke?: (ctx: PipelineMessageContext) => Promise<AgentResponse>;\n /** Error handler */\n onError?: (err: unknown, ctx: PipelineMessageContext) => void;\n}\n\n// ============================================\n// Pipeline Implementation\n// ============================================\n\nexport class MessagePipeline {\n private channel: string;\n private preflightHandlers: PreflightHandler[];\n private processHandlers: ProcessHandler[];\n private deliveryHandlers: DeliveryHandler[];\n private agentInvoke?: PipelineOptions['agentInvoke'];\n private onError?: PipelineOptions['onError'];\n\n constructor(options: PipelineOptions) {\n this.channel = options.channel;\n this.preflightHandlers = options.preflightHandlers ?? [];\n this.processHandlers = options.processHandlers ?? [];\n this.deliveryHandlers = options.deliveryHandlers ?? [];\n this.agentInvoke = options.agentInvoke;\n this.onError = options.onError;\n }\n\n /**\n * Handle inbound message\n */\n async handleMessage(ctx: PipelineMessageContext): Promise<void> {\n const channel = this.channel;\n const requestId = pipelineLogRequestId(ctx);\n\n await runWithLogContext(\n {\n requestId,\n sessionId: pipelineLogSessionId(ctx),\n },\n async () => {\n // 1. Preflight stage\n let processedCtx = await this.runPreflight(ctx);\n if (!processedCtx) {\n log.debug({ channel, chatId: ctx.chatId }, 'Message filtered in preflight');\n return;\n }\n\n // 2. Process stage\n try {\n processedCtx = await this.runProcess(processedCtx);\n } catch (err) {\n log.error({ channel, err, phase: 'channel.inbound' }, `Process handler error: ${err instanceof Error ? err.message : String(err)}`);\n this.onError?.(err, processedCtx);\n return;\n }\n\n const resolvedSk = processedCtx.metadata?.sessionKey;\n if (typeof resolvedSk === 'string' && resolvedSk.trim().length > 0) {\n updateAsyncLogContext({ sessionId: resolvedSk.trim() });\n }\n\n // 3. Deliver to Agent\n if (!this.agentInvoke) {\n log.warn({ channel }, 'No agentInvoke configured');\n return;\n }\n\n let response: AgentResponse;\n try {\n response = await this.agentInvoke(processedCtx);\n } catch (err) {\n log.error({ channel, err }, 'Agent invocation error');\n this.onError?.(err, processedCtx);\n return;\n }\n\n // 4. Delivery stage\n try {\n await this.runDelivery(processedCtx, response);\n } catch (err) {\n log.error({ channel, err }, 'Delivery handler error');\n this.onError?.(err, processedCtx);\n }\n },\n );\n }\n\n private async runPreflight(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null> {\n for (const handler of this.preflightHandlers) {\n try {\n const result = await handler.preflight?.(ctx);\n if (!result) {\n log.debug({ channel: this.channel, handler: handler.name }, 'Preflight filtered message');\n return null;\n }\n ctx = result;\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Preflight handler error');\n return null;\n }\n }\n return ctx;\n }\n\n private async runProcess(ctx: PipelineMessageContext): Promise<PipelineMessageContext> {\n for (const handler of this.processHandlers) {\n try {\n ctx = await handler.process(ctx);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Process handler error');\n throw err;\n }\n }\n return ctx;\n }\n\n private async runDelivery(ctx: PipelineMessageContext, response: AgentResponse): Promise<void> {\n for (const handler of this.deliveryHandlers) {\n try {\n await handler.deliver(ctx, response);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Delivery handler error');\n throw err;\n }\n }\n }\n}\n\n// ============================================\n// Standard Handlers\n// ============================================\n\n/**\n * Create filter-self handler\n */\nexport function createFilterSelfHandler(currentBotId: string): PreflightHandler {\n return {\n name: 'filterSelf',\n preflight: async (ctx) => {\n if (ctx.senderId === currentBotId) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-empty handler\n */\nexport function createFilterEmptyHandler(): PreflightHandler {\n return {\n name: 'filterEmpty',\n preflight: async (ctx) => {\n const content = ctx.content?.trim() ?? '';\n if (!content && (!ctx.metadata.media || (ctx.metadata.media as PipelineMediaRef[]).length === 0)) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-commands handler\n */\nexport function createFilterCommandsHandler(commands: string[]): PreflightHandler {\n const commandSet = new Set(commands.map(c => c.toLowerCase()));\n return {\n name: 'filterCommands',\n preflight: async (ctx) => {\n const firstWord = ctx.content?.split(/\\s/)[0]?.toLowerCase() ?? '';\n if (firstWord.startsWith('/') && commandSet.has(firstWord.slice(1))) {\n ctx.metadata.isCommand = true;\n ctx.metadata.command = firstWord.slice(1);\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create standard preflight handlers\n */\nexport function standardPreflightHandlers(botId: string): PreflightHandler[] {\n return [\n createFilterSelfHandler(botId),\n createFilterEmptyHandler(),\n createFilterCommandsHandler(['start', 'help', 'status', 'stop']),\n ];\n}\n\n/**\n * Prepends a per-turn `[YYYY-MM-DD HH:MM TZ]` prefix to inbound text so the model has\n * a stable \"now\" without changing the system prompt (prompt-cache friendly).\n */\nexport function createEnvelopeTimestampHandler(timezone?: string): ProcessHandler {\n return {\n name: 'envelopeTimestamp',\n process: async (ctx) => {\n const text = ctx.content?.trim();\n if (!text) {\n return ctx;\n }\n const timestamp = formatEnvelopeTimestamp(timezone);\n return { ...ctx, content: `[${timestamp}] ${ctx.content}` };\n },\n };\n}\n\n/**\n * Create standard process handlers\n */\nexport function standardProcessHandlers(timezone?: string): ProcessHandler[] {\n return [createEnvelopeTimestampHandler(timezone)];\n}\n\n// ============================================\n// Factory\n// ============================================\n\nexport interface CreatePipelineParams {\n channel: string;\n botId: string;\n agentInvoke: PipelineOptions['agentInvoke'];\n onError?: PipelineOptions['onError'];\n /** IANA timezone — matches userTimezone from config or global user profile */\n timezone?: string;\n}\n\n/**\n * Create message processing pipeline\n */\nexport function createPipeline(params: CreatePipelineParams): MessagePipeline {\n return new MessagePipeline({\n channel: params.channel,\n preflightHandlers: standardPreflightHandlers(params.botId),\n processHandlers: standardProcessHandlers(params.timezone),\n agentInvoke: params.agentInvoke,\n onError: params.onError,\n });\n}\n"],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { Config } from '../config/schema.js';
5
5
  import type { MessageBus } from '../infra/bus/index.js';
6
+ import type { Migration } from '../migrations/types.js';
6
7
  import type { ChannelId, ChannelMeta, ChannelCapabilities } from './plugins/types.core.js';
7
8
  import type { ChannelPairingAdapter, ChannelAllowlistAdapter, ChannelThreadingAdapter, ChannelLifecycleAdapter, ChannelHeartbeatAdapter, ChannelConfiguredBindingProvider, ChannelMessagingAdapter, ChannelDirectoryAdapter, ChannelResolverAdapter, ChannelAuthAdapter, ChannelElevatedAdapter, ChannelExecApprovalAdapter, ChannelAgentPromptAdapter, ChannelSetupWizard, ChannelCronDeliveryAdapter, ChannelCliLoginAdapter, ChannelConfigSurfaceAdapter, ChannelOnboardAdapter, ChannelRuntimeActionAdapter } from './plugins/types.adapters.js';
8
9
  export type { ChannelId, ChatType, ChannelMeta, ChannelCapabilities } from './plugins/types.core.js';
@@ -131,6 +132,8 @@ export interface ChannelPlugin<ResolvedAccount = any> {
131
132
  heartbeat?: ChannelHeartbeatAdapter;
132
133
  agentPrompt?: ChannelAgentPromptAdapter;
133
134
  agentTools?: ChannelAgentTool[];
135
+ /** Optional safe/manual migrations contributed by this channel plugin. */
136
+ migrations?: readonly Migration[];
134
137
  /** Optional doctor health-check contributed by this channel plugin. */
135
138
  doctor?: ChannelDoctorAdapter;
136
139
  }
@@ -14,7 +14,6 @@ init_paths();
14
14
  const PROFILE_FILE_NAMES = [
15
15
  WORKSPACE_FILES.SOUL,
16
16
  WORKSPACE_FILES.IDENTITY,
17
- WORKSPACE_FILES.USER,
18
17
  WORKSPACE_FILES.TOOLS,
19
18
  WORKSPACE_FILES.AGENTS,
20
19
  WORKSPACE_FILES.HEARTBEAT,
@@ -1 +1 @@
1
- {"version":3,"file":"agent-edit.js","names":[],"sources":["../../../src/chat-commands/agent-edit.ts"],"sourcesContent":["/**\n * /agent-edit — open the current chat as an agent profile editing session.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport type { CommandContext, CommandDefinition } from './types.js';\nimport { commandRegistry } from './registry.js';\nimport { resolveAgentIdFromSessionKey } from '../routing/agent-session-key.js';\nimport { normalizeAgentId, resolveAgentProfileDir } from '../agent/agent-scope.js';\nimport { WORKSPACE_FILES } from '../config/paths.js';\n\nconst PROFILE_FILE_NAMES = [\n WORKSPACE_FILES.SOUL,\n WORKSPACE_FILES.IDENTITY,\n WORKSPACE_FILES.USER,\n WORKSPACE_FILES.TOOLS,\n WORKSPACE_FILES.AGENTS,\n WORKSPACE_FILES.HEARTBEAT,\n WORKSPACE_FILES.MEMORY,\n] as const;\n\nconst DEFAULT_PREVIEW_CHARS = 900;\nconst MAX_PREVIEW_CHARS = 4_000;\n\nfunction parseArgs(args: string): { fileName?: string; previewChars: number } {\n const trimmed = args.trim();\n if (!trimmed) {\n return { previewChars: DEFAULT_PREVIEW_CHARS };\n }\n\n const parts = trimmed.split(/\\s+/);\n let fileName: string | undefined;\n let previewChars = DEFAULT_PREVIEW_CHARS;\n\n for (const part of parts) {\n const limitMatch = /^--limit=(\\d+)$/.exec(part);\n if (limitMatch) {\n previewChars = Math.min(Number(limitMatch[1]), MAX_PREVIEW_CHARS);\n continue;\n }\n if (!fileName) {\n fileName = part;\n }\n }\n\n return { fileName, previewChars };\n}\n\nfunction normalizeProfileFileName(input: string | undefined): string | undefined {\n if (!input) {\n return undefined;\n }\n const basename = input.trim().replace(/\\\\/g, '/').split('/').pop();\n const matched = PROFILE_FILE_NAMES.find((name) => name.toLowerCase() === basename?.toLowerCase());\n return matched;\n}\n\nasync function readPreview(path: string, limit: number): Promise<{ content: string; missing: boolean }> {\n try {\n const content = await readFile(path, 'utf-8');\n const trimmed = content.trimEnd();\n if (trimmed.length <= limit) {\n return { content: trimmed, missing: false };\n }\n return {\n content: `${trimmed.slice(0, limit)}\\n\\n… truncated, ask me to read the full file before editing …`,\n missing: false,\n };\n } catch {\n return { content: '', missing: true };\n }\n}\n\nfunction buildEditInstructions(agentId: string, fileNames: readonly string[]): string {\n const files = fileNames.map((name) => `\\`${name}\\``).join(', ');\n return [\n `You are editing agent \\`${agentId}\\`.`,\n '',\n 'Tell me what to change, or say things like:',\n '- “Refine `SOUL.md` to sound warmer and more concise.”',\n '- “Update `IDENTITY.md` so this agent is focused on data analysis.”',\n '- “Read `SOUL.md` first, propose changes, then write them back.”',\n '',\n `Editable profile files: ${files}.`,\n 'I can read and update these by bare filename with `read_file`, `edit_file`, and `write_file`.',\n ].join('\\n');\n}\n\nconst agentEditCommand: CommandDefinition = {\n id: 'agent.edit',\n name: 'agent-edit',\n aliases: ['agentedit'],\n description: 'Show editable profile files for the current agent and prepare this chat for profile edits.',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/agent-edit', '/agent-edit SOUL.md', '/agent-edit IDENTITY.md --limit=2000'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n\n const { fileName: rawFileName, previewChars } = parseArgs(args);\n const fileName = normalizeProfileFileName(rawFileName);\n if (rawFileName && !fileName) {\n return {\n content: `⚠️ Unsupported profile file: \\`${rawFileName}\\`. Use one of: ${PROFILE_FILE_NAMES.map((name) => `\\`${name}\\``).join(', ')}.`,\n success: false,\n };\n }\n\n const agentId = normalizeAgentId(resolveAgentIdFromSessionKey(ctx.sessionKey));\n const profileDir = resolveAgentProfileDir(ctx.config, agentId);\n const namesToShow = fileName ? [fileName] : [WORKSPACE_FILES.SOUL, WORKSPACE_FILES.IDENTITY];\n\n const sections: string[] = [];\n for (const name of namesToShow) {\n const preview = await readPreview(join(profileDir, name), previewChars);\n if (preview.missing) {\n sections.push(`## ${name}\\n_missing_`);\n } else {\n sections.push(`## ${name}\\n\\`\\`\\`markdown\\n${preview.content}\\n\\`\\`\\``);\n }\n }\n\n return {\n content: [\n '🛠️ Agent editor mode',\n '',\n buildEditInstructions(agentId, PROFILE_FILE_NAMES),\n '',\n `Profile directory: \\`${profileDir}\\``,\n '',\n ...sections,\n ].join('\\n'),\n success: true,\n };\n },\n};\n\nexport function registerAgentEditCommand(): void {\n commandRegistry.register(agentEditCommand);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-edit.js","names":[],"sources":["../../../src/chat-commands/agent-edit.ts"],"sourcesContent":["/**\n * /agent-edit — open the current chat as an agent profile editing session.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport type { CommandContext, CommandDefinition } from './types.js';\nimport { commandRegistry } from './registry.js';\nimport { resolveAgentIdFromSessionKey } from '../routing/agent-session-key.js';\nimport { normalizeAgentId, resolveAgentProfileDir } from '../agent/agent-scope.js';\nimport { WORKSPACE_FILES } from '../config/paths.js';\n\nconst PROFILE_FILE_NAMES = [\n WORKSPACE_FILES.SOUL,\n WORKSPACE_FILES.IDENTITY,\n WORKSPACE_FILES.TOOLS,\n WORKSPACE_FILES.AGENTS,\n WORKSPACE_FILES.HEARTBEAT,\n WORKSPACE_FILES.MEMORY,\n] as const;\n\nconst DEFAULT_PREVIEW_CHARS = 900;\nconst MAX_PREVIEW_CHARS = 4_000;\n\nfunction parseArgs(args: string): { fileName?: string; previewChars: number } {\n const trimmed = args.trim();\n if (!trimmed) {\n return { previewChars: DEFAULT_PREVIEW_CHARS };\n }\n\n const parts = trimmed.split(/\\s+/);\n let fileName: string | undefined;\n let previewChars = DEFAULT_PREVIEW_CHARS;\n\n for (const part of parts) {\n const limitMatch = /^--limit=(\\d+)$/.exec(part);\n if (limitMatch) {\n previewChars = Math.min(Number(limitMatch[1]), MAX_PREVIEW_CHARS);\n continue;\n }\n if (!fileName) {\n fileName = part;\n }\n }\n\n return { fileName, previewChars };\n}\n\nfunction normalizeProfileFileName(input: string | undefined): string | undefined {\n if (!input) {\n return undefined;\n }\n const basename = input.trim().replace(/\\\\/g, '/').split('/').pop();\n const matched = PROFILE_FILE_NAMES.find((name) => name.toLowerCase() === basename?.toLowerCase());\n return matched;\n}\n\nasync function readPreview(path: string, limit: number): Promise<{ content: string; missing: boolean }> {\n try {\n const content = await readFile(path, 'utf-8');\n const trimmed = content.trimEnd();\n if (trimmed.length <= limit) {\n return { content: trimmed, missing: false };\n }\n return {\n content: `${trimmed.slice(0, limit)}\\n\\n… truncated, ask me to read the full file before editing …`,\n missing: false,\n };\n } catch {\n return { content: '', missing: true };\n }\n}\n\nfunction buildEditInstructions(agentId: string, fileNames: readonly string[]): string {\n const files = fileNames.map((name) => `\\`${name}\\``).join(', ');\n return [\n `You are editing agent \\`${agentId}\\`.`,\n '',\n 'Tell me what to change, or say things like:',\n '- “Refine `SOUL.md` to sound warmer and more concise.”',\n '- “Update `IDENTITY.md` so this agent is focused on data analysis.”',\n '- “Read `SOUL.md` first, propose changes, then write them back.”',\n '',\n `Editable profile files: ${files}.`,\n 'I can read and update these by bare filename with `read_file`, `edit_file`, and `write_file`.',\n ].join('\\n');\n}\n\nconst agentEditCommand: CommandDefinition = {\n id: 'agent.edit',\n name: 'agent-edit',\n aliases: ['agentedit'],\n description: 'Show editable profile files for the current agent and prepare this chat for profile edits.',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/agent-edit', '/agent-edit SOUL.md', '/agent-edit IDENTITY.md --limit=2000'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n\n const { fileName: rawFileName, previewChars } = parseArgs(args);\n const fileName = normalizeProfileFileName(rawFileName);\n if (rawFileName && !fileName) {\n return {\n content: `⚠️ Unsupported profile file: \\`${rawFileName}\\`. Use one of: ${PROFILE_FILE_NAMES.map((name) => `\\`${name}\\``).join(', ')}.`,\n success: false,\n };\n }\n\n const agentId = normalizeAgentId(resolveAgentIdFromSessionKey(ctx.sessionKey));\n const profileDir = resolveAgentProfileDir(ctx.config, agentId);\n const namesToShow = fileName ? [fileName] : [WORKSPACE_FILES.SOUL, WORKSPACE_FILES.IDENTITY];\n\n const sections: string[] = [];\n for (const name of namesToShow) {\n const preview = await readPreview(join(profileDir, name), previewChars);\n if (preview.missing) {\n sections.push(`## ${name}\\n_missing_`);\n } else {\n sections.push(`## ${name}\\n\\`\\`\\`markdown\\n${preview.content}\\n\\`\\`\\``);\n }\n }\n\n return {\n content: [\n '🛠️ Agent editor mode',\n '',\n buildEditInstructions(agentId, PROFILE_FILE_NAMES),\n '',\n `Profile directory: \\`${profileDir}\\``,\n '',\n ...sections,\n ].join('\\n'),\n success: true,\n };\n },\n};\n\nexport function registerAgentEditCommand(): void {\n commandRegistry.register(agentEditCommand);\n}\n"],"mappings":"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"}
@@ -66,10 +66,10 @@ const switchCommand = {
66
66
  "group"
67
67
  ],
68
68
  acceptsArgs: true,
69
- examples: ["/switch openai/gpt-5.5", "/switch minimax/minimax-m2.7"],
69
+ examples: ["/switch deepseek/deepseek-chat", "/switch minimax/minimax-m2.7"],
70
70
  handler: async (ctx, args) => {
71
71
  if (!args.trim()) return {
72
- content: joinBlocks("❌ Missing model ref.", "**Usage:** `/switch provider/model-id`", "Run `/models` — each line shows a display name and a `provider/model` ref in backticks. Copy that ref.", "**Example:** `/switch openai/gpt-5.5`"),
72
+ content: joinBlocks("❌ Missing model ref.", "**Usage:** `/switch provider/model-id`", "Run `/models` — each line shows a display name and a `provider/model` ref in backticks. Copy that ref.", "**Example:** `/switch deepseek/deepseek-chat`"),
73
73
  success: false
74
74
  };
75
75
  await ctx.setTyping(true);
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","names":[],"sources":["../../../../src/chat-commands/builtins/model.ts"],"sourcesContent":["/**\n * Model Commands\n *\n * Built-in commands for model management:\n * - /models - List models with display names and `provider/model` refs for /switch\n * - /switch - Switch model using the ref from /models\n * - /usage - Show token usage statistics\n */\n\nimport type { CommandDefinition, CommandContext, UIComponent } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport { bulletList, code, hint, joinBlocks, kvList, section } from '../format-output.js';\n\nconst modelsCommand: CommandDefinition = {\n id: 'model.list',\n name: 'models',\n aliases: ['model'],\n description: 'List models with display names and `provider/model` refs for /switch',\n category: 'model',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n\n const models = await ctx.listModels();\n const currentModel = ctx.getCurrentModel();\n\n if (models.length === 0) {\n return {\n content: '🤖 No models available. Please check your configuration.',\n success: true,\n };\n }\n\n // Group by provider\n const byProvider = new Map<string, typeof models>();\n for (const m of models) {\n if (!byProvider.has(m.provider)) {\n byProvider.set(m.provider, []);\n }\n byProvider.get(m.provider)!.push(m);\n }\n\n /** `m.id` from listModels is always `serviceId/modelId` (canonical `/switch` ref). */\n const providerBlocks = Array.from(byProvider.entries()).map(([provider, providerModels]) => {\n const items = providerModels.slice(0, 5).map((m) => {\n const marker = m.id === currentModel ? '▶️ ' : '';\n return `${marker}**${m.name}** — ${code(m.id)}`;\n });\n if (providerModels.length > 5) {\n items.push(hint(`… and ${providerModels.length - 5} more in this provider`));\n }\n return joinBlocks(section(provider), bulletList(items));\n });\n\n const content = joinBlocks(\n '🤖 Available models (use the `provider/model` ref with `/switch`):',\n ...providerBlocks,\n hint('Copy the ref in backticks after each name, then: `/switch provider/model-id`.'),\n );\n\n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'model-picker',\n providers: Array.from(byProvider.entries()).map(([id, models]) => ({\n id,\n name: id,\n models: models.map((m) => ({\n id: m.id,\n name: m.name,\n provider: m.provider,\n })),\n })),\n currentModel,\n };\n\n return {\n content,\n success: true,\n components: [component],\n };\n }\n\n return {\n content,\n success: true,\n };\n },\n};\n\nconst switchCommand: CommandDefinition = {\n id: 'model.switch',\n name: 'switch',\n description: 'Switch model — pass the `provider/model` ref shown by /models',\n category: 'model',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/switch openai/gpt-5.5', '/switch minimax/minimax-m2.7'],\n handler: async (ctx: CommandContext, args: string) => {\n if (!args.trim()) {\n return {\n content: joinBlocks(\n '❌ Missing model ref.',\n '**Usage:** `/switch provider/model-id`',\n 'Run `/models` — each line shows a display name and a `provider/model` ref in backticks. Copy that ref.',\n '**Example:** `/switch openai/gpt-5.5`',\n ),\n success: false,\n };\n }\n\n await ctx.setTyping(true);\n\n const modelId = args.trim();\n const success = await ctx.switchModel(modelId);\n\n if (success) {\n const modelName = modelId.split('/').pop() || modelId;\n return {\n content: joinBlocks(\n `✅ Switched to ${code(modelId)} (${modelName}).`,\n 'This model will be used for your next message.',\n ),\n success: true,\n };\n }\n\n return {\n content: joinBlocks(\n `❌ Could not switch to ${code(modelId)}.`,\n 'Use the exact `provider/model` ref from `/models` (not only the display name).',\n '**Example:** `/switch anthropic/claude-sonnet-4-20250514`',\n ),\n success: false,\n };\n },\n};\n\nconst usageCommand: CommandDefinition = {\n id: 'model.usage',\n name: 'usage',\n description: 'Show token usage statistics for current session',\n category: 'model',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n\n const stats = await ctx.getUsage();\n const modelName = ctx.getCurrentModel().split('/').pop() || 'Unknown';\n\n const content = joinBlocks(\n section('📊 Session Token Usage'),\n kvList([\n { key: 'Model', value: modelName },\n { key: 'Prompt', value: `${stats.promptTokens.toLocaleString()} tokens` },\n { key: 'Completion', value: `${stats.completionTokens.toLocaleString()} tokens` },\n { key: 'Total', value: `${stats.totalTokens.toLocaleString()} tokens` },\n { key: 'Messages', value: String(stats.messageCount) },\n ]),\n );\n\n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'usage-display',\n stats,\n modelName,\n };\n\n return {\n content,\n success: true,\n components: [component],\n };\n }\n\n return {\n content,\n success: true,\n };\n },\n};\n\n// Register all model commands\nexport function registerModelCommands(): void {\n commandRegistry.register(modelsCommand);\n commandRegistry.register(switchCommand);\n commandRegistry.register(usageCommand);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model.js","names":[],"sources":["../../../../src/chat-commands/builtins/model.ts"],"sourcesContent":["/**\n * Model Commands\n *\n * Built-in commands for model management:\n * - /models - List models with display names and `provider/model` refs for /switch\n * - /switch - Switch model using the ref from /models\n * - /usage - Show token usage statistics\n */\n\nimport type { CommandDefinition, CommandContext, UIComponent } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport { bulletList, code, hint, joinBlocks, kvList, section } from '../format-output.js';\n\nconst modelsCommand: CommandDefinition = {\n id: 'model.list',\n name: 'models',\n aliases: ['model'],\n description: 'List models with display names and `provider/model` refs for /switch',\n category: 'model',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n\n const models = await ctx.listModels();\n const currentModel = ctx.getCurrentModel();\n\n if (models.length === 0) {\n return {\n content: '🤖 No models available. Please check your configuration.',\n success: true,\n };\n }\n\n // Group by provider\n const byProvider = new Map<string, typeof models>();\n for (const m of models) {\n if (!byProvider.has(m.provider)) {\n byProvider.set(m.provider, []);\n }\n byProvider.get(m.provider)!.push(m);\n }\n\n /** `m.id` from listModels is always `serviceId/modelId` (canonical `/switch` ref). */\n const providerBlocks = Array.from(byProvider.entries()).map(([provider, providerModels]) => {\n const items = providerModels.slice(0, 5).map((m) => {\n const marker = m.id === currentModel ? '▶️ ' : '';\n return `${marker}**${m.name}** — ${code(m.id)}`;\n });\n if (providerModels.length > 5) {\n items.push(hint(`… and ${providerModels.length - 5} more in this provider`));\n }\n return joinBlocks(section(provider), bulletList(items));\n });\n\n const content = joinBlocks(\n '🤖 Available models (use the `provider/model` ref with `/switch`):',\n ...providerBlocks,\n hint('Copy the ref in backticks after each name, then: `/switch provider/model-id`.'),\n );\n\n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'model-picker',\n providers: Array.from(byProvider.entries()).map(([id, models]) => ({\n id,\n name: id,\n models: models.map((m) => ({\n id: m.id,\n name: m.name,\n provider: m.provider,\n })),\n })),\n currentModel,\n };\n\n return {\n content,\n success: true,\n components: [component],\n };\n }\n\n return {\n content,\n success: true,\n };\n },\n};\n\nconst switchCommand: CommandDefinition = {\n id: 'model.switch',\n name: 'switch',\n description: 'Switch model — pass the `provider/model` ref shown by /models',\n category: 'model',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/switch deepseek/deepseek-chat', '/switch minimax/minimax-m2.7'],\n handler: async (ctx: CommandContext, args: string) => {\n if (!args.trim()) {\n return {\n content: joinBlocks(\n '❌ Missing model ref.',\n '**Usage:** `/switch provider/model-id`',\n 'Run `/models` — each line shows a display name and a `provider/model` ref in backticks. Copy that ref.',\n '**Example:** `/switch deepseek/deepseek-chat`',\n ),\n success: false,\n };\n }\n\n await ctx.setTyping(true);\n\n const modelId = args.trim();\n const success = await ctx.switchModel(modelId);\n\n if (success) {\n const modelName = modelId.split('/').pop() || modelId;\n return {\n content: joinBlocks(\n `✅ Switched to ${code(modelId)} (${modelName}).`,\n 'This model will be used for your next message.',\n ),\n success: true,\n };\n }\n\n return {\n content: joinBlocks(\n `❌ Could not switch to ${code(modelId)}.`,\n 'Use the exact `provider/model` ref from `/models` (not only the display name).',\n '**Example:** `/switch anthropic/claude-sonnet-4-20250514`',\n ),\n success: false,\n };\n },\n};\n\nconst usageCommand: CommandDefinition = {\n id: 'model.usage',\n name: 'usage',\n description: 'Show token usage statistics for current session',\n category: 'model',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n\n const stats = await ctx.getUsage();\n const modelName = ctx.getCurrentModel().split('/').pop() || 'Unknown';\n\n const content = joinBlocks(\n section('📊 Session Token Usage'),\n kvList([\n { key: 'Model', value: modelName },\n { key: 'Prompt', value: `${stats.promptTokens.toLocaleString()} tokens` },\n { key: 'Completion', value: `${stats.completionTokens.toLocaleString()} tokens` },\n { key: 'Total', value: `${stats.totalTokens.toLocaleString()} tokens` },\n { key: 'Messages', value: String(stats.messageCount) },\n ]),\n );\n\n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'usage-display',\n stats,\n modelName,\n };\n\n return {\n content,\n success: true,\n components: [component],\n };\n }\n\n return {\n content,\n success: true,\n };\n },\n};\n\n// Register all model commands\nexport function registerModelCommands(): void {\n commandRegistry.register(modelsCommand);\n commandRegistry.register(switchCommand);\n commandRegistry.register(usageCommand);\n}\n"],"mappings":"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"}