@xopcai/xopc 0.0.111 → 0.0.113

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 (443) hide show
  1. package/dist/browser-ext/icons/icon-128.png +0 -0
  2. package/dist/browser-ext/icons/icon-16.png +0 -0
  3. package/dist/browser-ext/icons/icon-32.png +0 -0
  4. package/dist/browser-ext/icons/icon-48.png +0 -0
  5. package/dist/browser-ext/manifest.json +1 -1
  6. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  7. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  8. package/dist/extensions/telegram/src/adapters/streaming-adapter.js +1 -1
  9. package/dist/extensions/telegram/src/inbound-processor.js +1 -1
  10. package/dist/extensions/telegram/src/plugin.js +1 -1
  11. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  12. package/dist/extensions/telegram/src/update-offset-store.js +1 -1
  13. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  14. package/dist/extensions/telegram/xopc.extension.json +1 -1
  15. package/dist/extensions/weixin/src/api/api.js +2 -2
  16. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  17. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  18. package/dist/extensions/weixin/src/delivery-to.d.ts +3 -3
  19. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  20. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  21. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  22. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  23. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  24. package/dist/extensions/weixin/src/plugin.js +1 -1
  25. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  26. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  27. package/dist/gateway/static/root/apple-touch-icon.png +0 -0
  28. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-zlx9l5Yp.js → agent-avatar-dicebear-CBlklyNr.js} +1 -1
  29. package/dist/gateway/static/root/assets/agent-browser-settings-page-rGbKc2Hh.js +6 -0
  30. package/dist/gateway/static/root/assets/{agent-defaults-tabbed-page-Bz7JWjRM.js → agent-defaults-tabbed-page-CtwVbjOA.js} +1 -1
  31. package/dist/gateway/static/root/assets/agents-CCBmGcwc.js +71 -0
  32. package/dist/gateway/static/root/assets/{app-management-settings-panel-DaQwdeCq.js → app-management-settings-panel-28E_WfMe.js} +1 -1
  33. package/dist/gateway/static/root/assets/{appearance-settings-D2zv6r3P.js → appearance-settings-Do1z0XgJ.js} +1 -1
  34. package/dist/gateway/static/root/assets/{apps-page-BIppe_jj.js → apps-page-xwCsJS15.js} +1 -1
  35. package/dist/gateway/static/root/assets/{archive-plugin-DEmu8eH7.js → archive-plugin-BAFlsov_.js} +1 -1
  36. package/dist/gateway/static/root/assets/{attachment-load-B98lJoN5.js → attachment-load-V3qGPC3x.js} +1 -1
  37. package/dist/gateway/static/root/assets/{binary-plugins-CL9QKmyo.js → binary-plugins-DT8NvA6W.js} +1 -1
  38. package/dist/gateway/static/root/assets/{block-editor-BOAhMAhT.js → block-editor-CdLMEcHd.js} +1 -1
  39. package/dist/gateway/static/root/assets/{channels-settings-Br8iRnXp.js → channels-settings-C044DcYk.js} +1 -1
  40. package/dist/gateway/static/root/assets/{config-api-0o1WYdz2.js → config-api-Bf5mjpu7.js} +1 -1
  41. package/dist/gateway/static/root/assets/config-reload-event-Bjh2zoKp.js +1 -0
  42. package/dist/gateway/static/root/assets/connectors-settings-8VEWUOTj.js +2 -0
  43. package/dist/gateway/static/root/assets/cron-api--pAB_rps.js +1 -0
  44. package/dist/gateway/static/root/assets/cron-page-DqmNGNOy.js +3 -0
  45. package/dist/gateway/static/root/assets/dreaming-settings-6UZE1FSK.js +1 -0
  46. package/dist/gateway/static/root/assets/{extension-debug-page-Cjx6pZVz.js → extension-debug-page-BGhE7msg.js} +1 -1
  47. package/dist/gateway/static/root/assets/{extension-page-BSPNL90V.js → extension-page-CvpI45WG.js} +1 -1
  48. package/dist/gateway/static/root/assets/extension-settings-page-DAmR5FUe.js +1 -0
  49. package/dist/gateway/static/root/assets/{fetch-Bs7in_4F.js → fetch-NZmjSXLl.js} +1 -1
  50. package/dist/gateway/static/root/assets/format-cron-label-BsotZDO5.js +2 -0
  51. package/dist/gateway/static/root/assets/{format-interval-label-CftJ5uXu.js → format-interval-label-qhth3BMV.js} +1 -1
  52. package/dist/gateway/static/root/assets/{gateway-config-api-Clfck-hW.js → gateway-config-api-CaXBt-9R.js} +1 -1
  53. package/dist/gateway/static/root/assets/gateway-settings-DphpbLBc.js +1 -0
  54. package/dist/gateway/static/root/assets/{gateway-startup-retry-BkjVJc38.js → gateway-startup-retry-Be1omjh3.js} +1 -1
  55. package/dist/gateway/static/root/assets/{goal-detail-page-kqJue0sE.js → goal-detail-page-bv0VRDaf.js} +1 -1
  56. package/dist/gateway/static/root/assets/goals-page-1Ywnt4Cb.js +2 -0
  57. package/dist/gateway/static/root/assets/{goals-settings-CJnzNSDo.js → goals-settings-ceEBjnfc.js} +1 -1
  58. package/dist/gateway/static/root/assets/{heartbeat-config-api-Cp7CPuIn.js → heartbeat-config-api-BzO6G4fD.js} +1 -1
  59. package/dist/gateway/static/root/assets/{heartbeat-settings-ifSI1Wbh.js → heartbeat-settings-DCST9-du.js} +1 -1
  60. package/dist/gateway/static/root/assets/index-Cxm6xsps.js +122 -0
  61. package/dist/gateway/static/root/assets/index-b_R4tKqt.css +1 -0
  62. package/dist/gateway/static/root/assets/interval-presets-DJmshV8n.js +1 -0
  63. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings--MI1rYl7.js → keyboard-shortcuts-settings-9AUUlQ5Q.js} +1 -1
  64. package/dist/gateway/static/root/assets/{logs-page-BwrVNlyW.js → logs-page-B_wrxhee.js} +1 -1
  65. package/dist/gateway/static/root/assets/{markdown-split-B_oc9lJe.js → markdown-split-CYYELqAr.js} +1 -1
  66. package/dist/gateway/static/root/assets/{media-plugins-DLPvB5H3.js → media-plugins-BEcMR2M8.js} +1 -1
  67. package/dist/gateway/static/root/assets/messages-CkT-lcgP.js +3 -0
  68. package/dist/gateway/static/root/assets/models-hub-panel-D0JDSbNu.js +2 -0
  69. package/dist/gateway/static/root/assets/note-detail-page-DpFV-EO6.js +2 -0
  70. package/dist/gateway/static/root/assets/{note-image-lightbox-rSqAgOi9.js → note-image-lightbox-DNAoPcmm.js} +1 -1
  71. package/dist/gateway/static/root/assets/{notes-page-C9ACGEty.js → notes-page-CdkjTch8.js} +1 -1
  72. package/dist/gateway/static/root/assets/{preview-open-alternatives-Bw_N2oZi.js → preview-open-alternatives-BPLz0AIv.js} +1 -1
  73. package/dist/gateway/static/root/assets/{remote-access-hub-BhcWIKuM.js → remote-access-hub-DSYVAi96.js} +1 -1
  74. package/dist/gateway/static/root/assets/{save-bar-controls-CNP2jmw0.js → save-bar-controls-CrWwWRVK.js} +1 -1
  75. package/dist/gateway/static/root/assets/schedule-field-CF2sadvN.js +1 -0
  76. package/dist/gateway/static/root/assets/{schema-form-CDPzua9q.js → schema-form-Di5cKjYE.js} +1 -1
  77. package/dist/gateway/static/root/assets/{sessions-page-DzkpSDZr.js → sessions-page-CenkcVjF.js} +1 -1
  78. package/dist/gateway/static/root/assets/settings-advanced-gate-D9sva86V.js +1 -0
  79. package/dist/gateway/static/root/assets/{settings-collapsible-section-C9bff85B.js → settings-collapsible-section-Chh282jW.js} +1 -1
  80. package/dist/gateway/static/root/assets/settings-form-section-Dq-83cQd.js +1 -0
  81. package/dist/gateway/static/root/assets/{settings-page-DWK4Zo7s.js → settings-page-DL35U0x0.js} +1 -1
  82. package/dist/gateway/static/root/assets/setup-status-panel-Ctwbp9Er.js +1 -0
  83. package/dist/gateway/static/root/assets/{share-preview-page-DnutoY_S.js → share-preview-page-0FXYnSUA.js} +1 -1
  84. package/dist/gateway/static/root/assets/{shares-settings-6oRgJMpn.js → shares-settings-DIM3JNsr.js} +1 -1
  85. package/dist/gateway/static/root/assets/skill-reload-api-DAlNaNzL.js +1 -0
  86. package/dist/gateway/static/root/assets/skills-page-CLrHVzAt.js +2 -0
  87. package/dist/gateway/static/root/assets/{system-settings-panel-qP1JDtDd.js → system-settings-panel-W1pTcojB.js} +1 -1
  88. package/dist/gateway/static/root/assets/use-agent-defaults-form-BVRvkK7m.js +1 -0
  89. package/dist/gateway/static/root/assets/{use-channel-catalog-C4mg1hjD.js → use-channel-catalog-CgJibfMM.js} +1 -1
  90. package/dist/gateway/static/root/assets/{use-settings-tab-guard-F6C0mpJt.js → use-settings-tab-guard-Bw_qkRQE.js} +1 -1
  91. package/dist/gateway/static/root/assets/{voice-api-key-field-BnDDb6qO.js → voice-api-key-field-vYAO71Gn.js} +1 -1
  92. package/dist/gateway/static/root/assets/{voice-player-store-B2EwIhtY.js → voice-player-store-C7lziaAx.js} +1 -1
  93. package/dist/gateway/static/root/assets/{workflow-page.utils-CRB7WX9l.js → workflow-page.utils-sgHjOXOm.js} +1 -1
  94. package/dist/gateway/static/root/assets/{workflows-page-2Fptr0je.js → workflows-page-DYyOTmFx.js} +1 -1
  95. package/dist/gateway/static/root/favicon-16x16.png +0 -0
  96. package/dist/gateway/static/root/favicon-32x32.png +0 -0
  97. package/dist/gateway/static/root/favicon-icon.svg +1 -0
  98. package/dist/gateway/static/root/favicon.ico +0 -0
  99. package/dist/gateway/static/root/favicon.png +0 -0
  100. package/dist/gateway/static/root/favicon.svg +24 -15
  101. package/dist/gateway/static/root/index.html +11 -10
  102. package/dist/gateway/static/root/logo-dark.svg +23 -15
  103. package/dist/gateway/static/root/logo.svg +23 -16
  104. package/dist/package.js +1 -1
  105. package/dist/src/agent/agent-manager.d.ts +28 -2
  106. package/dist/src/agent/agent-manager.js +82 -11
  107. package/dist/src/agent/agent-manager.js.map +1 -1
  108. package/dist/src/agent/agent-scope.js +1 -1
  109. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  110. package/dist/src/agent/context/workspace-seed.js +2 -2
  111. package/dist/src/agent/context/workspace-state.js +1 -1
  112. package/dist/src/agent/context/workspace.js +1 -1
  113. package/dist/src/agent/goals/goal-locale.js +2 -2
  114. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  115. package/dist/src/agent/goals/post-turn.js +3 -3
  116. package/dist/src/agent/image/load-image-media.js +2 -2
  117. package/dist/src/agent/ipc/inbox.js +3 -3
  118. package/dist/src/agent/ipc/socket.js +1 -1
  119. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  120. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  121. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  122. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  123. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  124. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  125. package/dist/src/agent/memory/dreaming/events.js +1 -1
  126. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  127. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  128. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  129. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  130. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  131. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  132. package/dist/src/agent/memory/memory-flush.js +1 -1
  133. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  134. package/dist/src/agent/messaging/command-handler.d.ts +3 -0
  135. package/dist/src/agent/messaging/command-handler.js +5 -0
  136. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  137. package/dist/src/agent/models/manager.js +1 -1
  138. package/dist/src/agent/prompt/memory/index.js +1 -1
  139. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  140. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  141. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  142. package/dist/src/agent/sandbox/path-policy.js +2 -2
  143. package/dist/src/agent/service/process-direct-streaming.js +9 -4
  144. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  145. package/dist/src/agent/service/reconcile-dreaming-cron.js +8 -5
  146. package/dist/src/agent/service/reconcile-dreaming-cron.js.map +1 -1
  147. package/dist/src/agent/service.d.ts +2 -1
  148. package/dist/src/agent/service.js +10 -5
  149. package/dist/src/agent/service.js.map +1 -1
  150. package/dist/src/agent/service.types.d.ts +4 -0
  151. package/dist/src/agent/session/session-config-service.d.ts +8 -0
  152. package/dist/src/agent/session/session-config-service.js +20 -0
  153. package/dist/src/agent/session/session-config-service.js.map +1 -1
  154. package/dist/src/agent/session/session-inspector.js +1 -1
  155. package/dist/src/agent/skills/config.js +2 -2
  156. package/dist/src/agent/skills/filesystem-watcher.d.ts +19 -0
  157. package/dist/src/agent/skills/filesystem-watcher.js +182 -0
  158. package/dist/src/agent/skills/filesystem-watcher.js.map +1 -0
  159. package/dist/src/agent/skills/hub-hash.js +2 -2
  160. package/dist/src/agent/skills/hub-lock.js +1 -1
  161. package/dist/src/agent/skills/hub-pull.js +30 -36
  162. package/dist/src/agent/skills/hub-pull.js.map +1 -1
  163. package/dist/src/agent/skills/index.js +82 -38
  164. package/dist/src/agent/skills/index.js.map +1 -1
  165. package/dist/src/agent/skills/installer.js +2 -2
  166. package/dist/src/agent/skills/managed-store.d.ts +19 -0
  167. package/dist/src/agent/skills/managed-store.js +97 -38
  168. package/dist/src/agent/skills/managed-store.js.map +1 -1
  169. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  170. package/dist/src/agent/skills/skill-manager.d.ts +16 -6
  171. package/dist/src/agent/skills/skill-manager.js +79 -24
  172. package/dist/src/agent/skills/skill-manager.js.map +1 -1
  173. package/dist/src/agent/skills/skill-view-path.js +1 -1
  174. package/dist/src/agent/skills/test-framework.js +1 -1
  175. package/dist/src/agent/skills/types.d.ts +11 -0
  176. package/dist/src/agent/skills/watcher.js +1 -1
  177. package/dist/src/agent/starter-agents.js +1 -1
  178. package/dist/src/agent/tools/cronjob-tool.d.ts +6 -3
  179. package/dist/src/agent/tools/cronjob-tool.js +58 -23
  180. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  181. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  182. package/dist/src/agent/tools/factory.js +1 -1
  183. package/dist/src/agent/tools/find.js +1 -1
  184. package/dist/src/agent/tools/grep.js +1 -1
  185. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  186. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  187. package/dist/src/agent/tools/workflow-tool.js +1 -1
  188. package/dist/src/agent/workflow/catalog.js +1 -1
  189. package/dist/src/agent/workspace-runtime/registry.d.ts +5 -0
  190. package/dist/src/agent/workspace-runtime/registry.js +7 -0
  191. package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
  192. package/dist/src/ai-assist/text-assist.d.ts +42 -0
  193. package/dist/src/ai-assist/text-assist.js +252 -0
  194. package/dist/src/ai-assist/text-assist.js.map +1 -0
  195. package/dist/src/auth/credentials.js +2 -2
  196. package/dist/src/auth/profiles/store.js +2 -2
  197. package/dist/src/auth/sync-provider-auth.js +1 -1
  198. package/dist/src/browser/cache-dir-policy.js +1 -1
  199. package/dist/src/browser/cdp-local-launcher.js +2 -2
  200. package/dist/src/browser/providers/browser-ext-install.js +4 -4
  201. package/dist/src/browser/providers/cloakbrowser.js +5 -5
  202. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  203. package/dist/src/browser/stealth.js +1 -1
  204. package/dist/src/channels/outbound/persist-store.js +1 -1
  205. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  206. package/dist/src/channels/pairing/pairing-store.js +2 -2
  207. package/dist/src/channels/plugins/types.adapters.d.ts +2 -2
  208. package/dist/src/chat-commands/agent-edit.js +2 -2
  209. package/dist/src/chat-commands/builtins/config.js +3 -3
  210. package/dist/src/chat-commands/builtins/system.js +11 -4
  211. package/dist/src/chat-commands/builtins/system.js.map +1 -1
  212. package/dist/src/chat-commands/context.d.ts +3 -0
  213. package/dist/src/chat-commands/context.js +7 -0
  214. package/dist/src/chat-commands/context.js.map +1 -1
  215. package/dist/src/chat-commands/types.d.ts +2 -0
  216. package/dist/src/cli/commands/config.js +1 -1
  217. package/dist/src/cli/commands/cron.js +55 -16
  218. package/dist/src/cli/commands/cron.js.map +1 -1
  219. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  220. package/dist/src/cli/commands/doctor/checks/cron-health.js +2 -83
  221. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
  222. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  223. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  224. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  225. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  226. package/dist/src/cli/commands/extension-create.js +1 -1
  227. package/dist/src/cli/commands/extension-dev.js +2 -2
  228. package/dist/src/cli/commands/extension-marketplace.js +2 -2
  229. package/dist/src/cli/commands/extension-pack.js +2 -2
  230. package/dist/src/cli/commands/extension.js +3 -3
  231. package/dist/src/cli/commands/gateway/logs.js +1 -1
  232. package/dist/src/cli/commands/image.js +1 -1
  233. package/dist/src/cli/commands/init.js +2 -2
  234. package/dist/src/cli/commands/models.js +1 -1
  235. package/dist/src/cli/commands/skills-test.js +1 -1
  236. package/dist/src/cli/templates.js +1 -1
  237. package/dist/src/cli/utils/gateway-client.js +1 -1
  238. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  239. package/dist/src/config/agent-profile.js +1 -1
  240. package/dist/src/config/agent-typed-models.js +1 -1
  241. package/dist/src/config/gateway-bind.js +1 -1
  242. package/dist/src/config/index.js +7 -7
  243. package/dist/src/config/loader.js +4 -4
  244. package/dist/src/config/models-json.js +2 -2
  245. package/dist/src/config/paths-state.js +1 -1
  246. package/dist/src/config/paths.js +1 -1
  247. package/dist/src/config/profile.js +1 -1
  248. package/dist/src/config/reload.js +1 -1
  249. package/dist/src/config/schema.d.ts +0 -2
  250. package/dist/src/config/schema.js +0 -3
  251. package/dist/src/config/schema.js.map +1 -1
  252. package/dist/src/config/web-patch.d.ts +0 -1
  253. package/dist/src/config/web-patch.js +0 -2
  254. package/dist/src/config/web-patch.js.map +1 -1
  255. package/dist/src/config/workspace-path.js +1 -1
  256. package/dist/src/cron/executor.js +102 -71
  257. package/dist/src/cron/executor.js.map +1 -1
  258. package/dist/src/cron/index.d.ts +1 -1
  259. package/dist/src/cron/index.js +3 -3
  260. package/dist/src/cron/run-log-store.d.ts +1 -0
  261. package/dist/src/cron/run-log-store.js +4 -2
  262. package/dist/src/cron/run-log-store.js.map +1 -1
  263. package/dist/src/cron/schedule.d.ts +10 -0
  264. package/dist/src/cron/schedule.js +85 -0
  265. package/dist/src/cron/schedule.js.map +1 -0
  266. package/dist/src/cron/service.d.ts +34 -77
  267. package/dist/src/cron/service.js +327 -264
  268. package/dist/src/cron/service.js.map +1 -1
  269. package/dist/src/cron/types.d.ts +80 -50
  270. package/dist/src/cron/validation.d.ts +358 -90
  271. package/dist/src/cron/validation.js +152 -132
  272. package/dist/src/cron/validation.js.map +1 -1
  273. package/dist/src/cron/workflow-run-completion.js +1 -1
  274. package/dist/src/daemon/constants.js +1 -1
  275. package/dist/src/daemon/install-plan.js +2 -2
  276. package/dist/src/daemon/launchd.js +2 -2
  277. package/dist/src/daemon/schtasks.js +2 -2
  278. package/dist/src/daemon/systemd.js +2 -2
  279. package/dist/src/extensions/bundle-mcp.js +1 -1
  280. package/dist/src/extensions/discover-extensions.js +2 -2
  281. package/dist/src/extensions/health.js +1 -1
  282. package/dist/src/extensions/install.js +2 -2
  283. package/dist/src/extensions/loader.js +2 -2
  284. package/dist/src/extensions/lockfile.js +3 -3
  285. package/dist/src/extensions/security.js +1 -1
  286. package/dist/src/extensions/update.js +2 -2
  287. package/dist/src/gateway/agents-admin.js +2 -2
  288. package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
  289. package/dist/src/gateway/chat-stream/mapper.js +6 -2
  290. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  291. package/dist/src/gateway/chat-stream/protocol.d.ts +2 -0
  292. package/dist/src/gateway/file-path-classifier.js +2 -2
  293. package/dist/src/gateway/hono/lib/config-payload.d.ts +0 -1
  294. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  295. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  296. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  297. package/dist/src/gateway/hono/oauth-async.js +1 -1
  298. package/dist/src/gateway/hono/routes/agents.js +1 -1
  299. package/dist/src/gateway/hono/routes/ai-assist.d.ts +3 -0
  300. package/dist/src/gateway/hono/routes/ai-assist.js +66 -0
  301. package/dist/src/gateway/hono/routes/ai-assist.js.map +1 -0
  302. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  303. package/dist/src/gateway/hono/routes/commands-skills.js +7 -0
  304. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  305. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  306. package/dist/src/gateway/hono/routes/cron.js +24 -5
  307. package/dist/src/gateway/hono/routes/cron.js.map +1 -1
  308. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  309. package/dist/src/gateway/hono/routes/home.js +5 -3
  310. package/dist/src/gateway/hono/routes/home.js.map +1 -1
  311. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  312. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  313. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  314. package/dist/src/gateway/hono/routes/models.js +1 -1
  315. package/dist/src/gateway/hono/routes/shares.js +1 -1
  316. package/dist/src/gateway/hono/routes/workflows.js +2 -2
  317. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  318. package/dist/src/gateway/lock.js +3 -3
  319. package/dist/src/gateway/ports.js +1 -1
  320. package/dist/src/gateway/service/agent-runner.js +2 -2
  321. package/dist/src/gateway/service/config-coordinator.js +1 -1
  322. package/dist/src/gateway/service/marketplace-service.d.ts +22 -10
  323. package/dist/src/gateway/service/marketplace-service.js +46 -5
  324. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  325. package/dist/src/gateway/service.js +11 -5
  326. package/dist/src/gateway/service.js.map +1 -1
  327. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  328. package/dist/src/goals/goal-notifications.js +1 -1
  329. package/dist/src/goals/goal-workflow-judge.js +3 -3
  330. package/dist/src/i18n/goals-bundle.js +1 -1
  331. package/dist/src/i18n/index.js +1 -1
  332. package/dist/src/i18n/share-tool-bundle.js +2 -2
  333. package/dist/src/infra/brew.js +1 -1
  334. package/dist/src/infra/package-json.js +1 -1
  335. package/dist/src/infra/package-update-steps.js +1 -1
  336. package/dist/src/infra/path-env.js +2 -2
  337. package/dist/src/infra/restart.js +1 -1
  338. package/dist/src/infra/stable-node-path.js +1 -1
  339. package/dist/src/infra/update-check.js +1 -1
  340. package/dist/src/infra/update-global.js +1 -1
  341. package/dist/src/infra/update-lock.js +2 -2
  342. package/dist/src/infra/update-restart.js +1 -1
  343. package/dist/src/infra/update-runner.js +1 -1
  344. package/dist/src/infra/update-startup.js +1 -1
  345. package/dist/src/infra/write-file-atomic.js +2 -2
  346. package/dist/src/media/store.js +1 -1
  347. package/dist/src/notes/agent-context.js +1 -1
  348. package/dist/src/notes/service.js +1 -1
  349. package/dist/src/notes/store.js +2 -2
  350. package/dist/src/project-trust/trust-store.js +2 -2
  351. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  352. package/dist/src/providers/index.js +1 -1
  353. package/dist/src/providers/model-registry.js +4 -4
  354. package/dist/src/session/init-session-turn.js +2 -2
  355. package/dist/src/session/resolve-session.js +3 -3
  356. package/dist/src/session/session-title.js +2 -2
  357. package/dist/src/session/store-reader.d.ts +5 -0
  358. package/dist/src/session/store-reader.js +1 -0
  359. package/dist/src/session/store.js +1 -1
  360. package/dist/src/share/share-auto.js +2 -2
  361. package/dist/src/share/share-store.js +3 -3
  362. package/dist/src/share/share-thumbnail.js +2 -2
  363. package/dist/src/share/share-zip.js +1 -1
  364. package/dist/src/share/site-share-store.js +3 -3
  365. package/dist/src/share/site-static-serve.js +1 -1
  366. package/dist/src/storage/sqlite/connection.js +1 -1
  367. package/dist/src/storage/sqlite/cron-job-repository.d.ts +6 -0
  368. package/dist/src/storage/sqlite/cron-job-repository.js +70 -0
  369. package/dist/src/storage/sqlite/cron-job-repository.js.map +1 -0
  370. package/dist/src/storage/sqlite/cron-run-repository.js +0 -1
  371. package/dist/src/storage/sqlite/cron-run-repository.js.map +1 -1
  372. package/dist/src/storage/sqlite/index.d.ts +1 -0
  373. package/dist/src/storage/sqlite/index.js +2 -1
  374. package/dist/src/storage/sqlite/memory-index-repository.js +2 -2
  375. package/dist/src/storage/sqlite/migrations/009_cron_jobs.sql +24 -0
  376. package/dist/src/storage/sqlite/migrations/discover.js +1 -1
  377. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  378. package/dist/src/storage/sqlite/migrations/runner.js +4 -4
  379. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  380. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  381. package/dist/src/storage/sqlite/schema.js +2 -2
  382. package/dist/src/storage/sqlite/schema.sql +25 -0
  383. package/dist/src/tui/clipboard-image.js +3 -3
  384. package/dist/src/tui/extension-host/load-extensions.js +1 -1
  385. package/dist/src/tui/theme-manager.js +1 -1
  386. package/dist/src/tui/tui-command-formatters.js +4 -4
  387. package/dist/src/tui/tui-commands.js +2 -2
  388. package/dist/src/tui/tui-keybindings-file.js +1 -1
  389. package/dist/src/tui/tui-scoped-models.js +2 -2
  390. package/dist/src/tui/tui-settings.js +1 -1
  391. package/dist/src/tui/tui-startup-resources.js +1 -1
  392. package/dist/src/tui/tui.js +6 -6
  393. package/dist/src/tunnel/frpc-binary.js +4 -4
  394. package/dist/src/tunnel/frpc-config.js +1 -1
  395. package/dist/src/tunnel/frpc-extract.js +1 -1
  396. package/dist/src/tunnel/tunnel-state.js +1 -1
  397. package/dist/src/utils/logger/audit.js +1 -1
  398. package/dist/src/utils/logger/log-store.js +1 -1
  399. package/dist/src/utils/logger/rotation.js +1 -1
  400. package/dist/src/utils/logger/streams.js +1 -1
  401. package/dist/src/voice/tts/audio.js +1 -1
  402. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  403. package/dist/src/workflows/draft/workflow-draft-service.js +1 -1
  404. package/dist/src/workflows/engine/projector.js +23 -14
  405. package/dist/src/workflows/engine/projector.js.map +1 -1
  406. package/dist/src/workflows/engine/workflow-engine.d.ts +1 -0
  407. package/dist/src/workflows/engine/workflow-engine.js +36 -0
  408. package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
  409. package/dist/src/workflows/index.js +1 -1
  410. package/dist/src/workflows/service/workflow-run-service.js +2 -2
  411. package/dist/src/workflows/service/workflow-session-bridge.js +2 -2
  412. package/dist/src/workflows/store/event-store.js +1 -1
  413. package/dist/src/workflows/store/run-store.js +1 -1
  414. package/package.json +2 -3
  415. package/dist/gateway/static/root/assets/agent-browser-settings-page-B-0zGjt8.js +0 -6
  416. package/dist/gateway/static/root/assets/agents-DwDnJlXT.js +0 -71
  417. package/dist/gateway/static/root/assets/connectors-settings-DDvHo9in.js +0 -2
  418. package/dist/gateway/static/root/assets/cron-api-Dj_UZKST.js +0 -1
  419. package/dist/gateway/static/root/assets/cron-page-Dv7GII9G.js +0 -1
  420. package/dist/gateway/static/root/assets/cron-schedule-picker-DH10KgD6.js +0 -1
  421. package/dist/gateway/static/root/assets/dreaming-settings-L2rgZDQl.js +0 -1
  422. package/dist/gateway/static/root/assets/extension-settings-page-m_UQYkVc.js +0 -1
  423. package/dist/gateway/static/root/assets/gateway-settings-CS177xni.js +0 -1
  424. package/dist/gateway/static/root/assets/goals-page-C0FujUB5.js +0 -2
  425. package/dist/gateway/static/root/assets/index-COkF54xv.js +0 -121
  426. package/dist/gateway/static/root/assets/index-DIaSNGEO.css +0 -1
  427. package/dist/gateway/static/root/assets/interval-presets-B29Ss_2y.js +0 -2
  428. package/dist/gateway/static/root/assets/messages-Csef1aW7.js +0 -3
  429. package/dist/gateway/static/root/assets/models-hub-panel-DHGiTIvf.js +0 -2
  430. package/dist/gateway/static/root/assets/note-detail-page-BM7zlDiT.js +0 -2
  431. package/dist/gateway/static/root/assets/schedule-field-BPAWui3P.js +0 -1
  432. package/dist/gateway/static/root/assets/schedule-summary-B-hdSRdR.js +0 -1
  433. package/dist/gateway/static/root/assets/settings-advanced-gate-BYS-rpzn.js +0 -1
  434. package/dist/gateway/static/root/assets/settings-form-section-BsrK7TBF.js +0 -1
  435. package/dist/gateway/static/root/assets/setup-status-panel-DJiksNu4.js +0 -1
  436. package/dist/gateway/static/root/assets/skill-reload-api-CU9i0nJV.js +0 -1
  437. package/dist/gateway/static/root/assets/skills-page-ERbdWENm.js +0 -2
  438. package/dist/gateway/static/root/assets/use-agent-defaults-form-D3FGVLZb.js +0 -1
  439. package/dist/gateway/static/root/favicon-16.png +0 -0
  440. package/dist/gateway/static/root/favicon-32.png +0 -0
  441. package/dist/src/cron/persistence.d.ts +0 -62
  442. package/dist/src/cron/persistence.js +0 -188
  443. package/dist/src/cron/persistence.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { init_agent_scope, normalizeAgentId } from "../agent/agent-scope.js";
2
- import { buildSessionKey, init_session_key } from "../routing/session-key.js";
3
2
  import { createLogger } from "../utils/logger/index.js";
4
3
  import { init_logger } from "../utils/logger.js";
4
+ import { buildSessionKey, init_session_key } from "../routing/session-key.js";
5
5
  import { shouldSilence, stripHeartbeatToken } from "../heartbeat/tokens.js";
6
- import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/registry.js";
6
+ import { getChannelPlugin } from "../channels/plugins/registry.js";
7
7
  import { getCronPayloadText } from "./job-content.js";
8
8
  import { buildWorkflowRunCronSummary, buildWorkflowRunDeliveryText, isWorkflowRunCronSuccess, resolveWorkflowCronWaitMs, waitForWorkflowRunView } from "./workflow-run-completion.js";
9
9
  //#region src/cron/executor.ts
@@ -23,13 +23,22 @@ function errorBackoffMs(consecutiveErrors) {
23
23
  return ERROR_BACKOFF_MS[Math.max(0, idx)];
24
24
  }
25
25
  function resolveIsolatedCronJobModel(job) {
26
- const fromJob = job.model?.trim();
27
- if (fromJob) return fromJob;
28
26
  if (job.payload?.kind === "agentTurn") {
29
27
  const m = job.payload.model?.trim();
30
28
  if (m) return m;
31
29
  }
32
30
  }
31
+ async function postCronWebhook(url, payload) {
32
+ const res = await fetch(url, {
33
+ method: "POST",
34
+ headers: { "content-type": "application/json" },
35
+ body: JSON.stringify(payload)
36
+ });
37
+ if (!res.ok) {
38
+ const text = await res.text().catch(() => "");
39
+ throw new Error(`Webhook delivery failed: HTTP ${res.status}${text ? ` ${text.slice(0, 200)}` : ""}`);
40
+ }
41
+ }
33
42
  var DefaultJobExecutor = class {
34
43
  history = /* @__PURE__ */ new Map();
35
44
  runningJobs = /* @__PURE__ */ new Map();
@@ -84,9 +93,13 @@ var DefaultJobExecutor = class {
84
93
  jobId: job.id,
85
94
  status: "running",
86
95
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
87
- retryCount: 0
96
+ retryCount: 0,
97
+ sessionId: job.sessionKey,
98
+ sessionKey: job.sessionKey,
99
+ sessionType: job.sessionKey ? "cron" : void 0
88
100
  };
89
101
  this.addToHistory(job.id, execution);
102
+ if (this.runLogStore) await this.runLogStore.upsert(execution);
90
103
  this.runningJobs.set(job.id, new AbortController());
91
104
  log.info({
92
105
  jobId: job.id,
@@ -123,7 +136,7 @@ var DefaultJobExecutor = class {
123
136
  errorMessage: result.error,
124
137
  phase: "cron.execute"
125
138
  }, `Job failed: ${result.error ?? "unknown"}`);
126
- if (result.status === "ok" && this.heartbeatService) try {
139
+ if (result.status === "ok" && job.wakeMode === "next-heartbeat" && this.heartbeatService) try {
127
140
  this.heartbeatService.requestNow({ reason: `cron:${job.id}` });
128
141
  } catch (e) {
129
142
  log.warn({
@@ -150,14 +163,14 @@ var DefaultJobExecutor = class {
150
163
  } finally {
151
164
  this.runningJobs.delete(job.id);
152
165
  }
153
- if (this.runLogStore) await this.runLogStore.appendCompleted(execution);
166
+ if (this.runLogStore) await this.runLogStore.upsert(execution);
154
167
  }
155
168
  /**
156
169
  * Perform the actual job work - integrate with AgentService
157
170
  */
158
171
  async performJob(job, signal) {
159
- const timeout = job.timeout || 6e4;
160
- const sessionTarget = job.sessionTarget || "main";
172
+ const timeout = job.payload.kind === "agentTurn" && job.payload.timeoutSeconds ? job.payload.timeoutSeconds * 1e3 : 18e4;
173
+ const sessionTarget = job.sessionTarget;
161
174
  if (signal.aborted) return {
162
175
  status: "skipped",
163
176
  error: "Job was aborted before execution"
@@ -189,7 +202,7 @@ var DefaultJobExecutor = class {
189
202
  };
190
203
  const item = this.goalRunner.enqueue(job.payload.goalId, {
191
204
  message: job.payload.message,
192
- maxRetries: job.payload.maxRetries ?? job.maxRetries,
205
+ maxRetries: job.payload.maxRetries ?? 0,
193
206
  source: "cron"
194
207
  });
195
208
  return {
@@ -258,8 +271,8 @@ var DefaultJobExecutor = class {
258
271
  });
259
272
  }
260
273
  async waitForWorkflowRunOutcome(params) {
261
- const waitMs = resolveWorkflowCronWaitMs(params.job.timeout);
262
- const maxWorkflowRetries = Math.max(0, params.job.maxRetries ?? 0);
274
+ const waitMs = resolveWorkflowCronWaitMs(18e4);
275
+ const maxWorkflowRetries = params.job.payload.kind === "workflowRun" ? Math.max(0, Math.floor(params.job.payload.maxRetries ?? 0)) : 0;
263
276
  let runId = params.initialRunId;
264
277
  let attempt = 0;
265
278
  while (attempt <= maxWorkflowRetries) {
@@ -336,11 +349,27 @@ var DefaultJobExecutor = class {
336
349
  };
337
350
  }
338
351
  async deliverWorkflowRunOutcome(job, view, outcome) {
339
- if (!this.messageBus) return;
340
352
  const delivery = job.delivery;
341
353
  if (!delivery || delivery.mode === "none" || delivery.channel === "local" || !delivery.to?.trim()) return;
342
354
  try {
343
355
  const text = buildWorkflowRunDeliveryText(view);
356
+ if (delivery.mode === "webhook") {
357
+ await postCronWebhook(delivery.to, {
358
+ jobId: job.id,
359
+ jobName: job.name,
360
+ payloadKind: job.payload.kind,
361
+ workflowRunId: view.run.id,
362
+ status: view.run.status,
363
+ text,
364
+ run: view.run
365
+ });
366
+ log.info({
367
+ jobId: job.id,
368
+ workflowRunId: view.run.id
369
+ }, "Delivered workflow run webhook from cron");
370
+ return;
371
+ }
372
+ if (!this.messageBus || !delivery.channel) return;
344
373
  const outbound = await this.buildCronOutboundMessage(delivery.channel, delivery.to, text);
345
374
  await this.messageBus.publishOutbound(outbound);
346
375
  log.info({
@@ -368,65 +397,42 @@ var DefaultJobExecutor = class {
368
397
  status: "skipped",
369
398
  error: "Main session job requires non-empty message"
370
399
  };
371
- let channel;
372
- let to;
373
- let actualMessage;
374
- if (job.delivery?.channel === "local") {
375
- channel = "local";
376
- to = "";
377
- actualMessage = text;
378
- } else if (job.delivery?.channel && job.delivery?.to) {
379
- channel = job.delivery.channel;
380
- to = job.delivery.to;
381
- actualMessage = text;
382
- } else {
383
- const parts = text.split(":");
384
- const hasAtLeastThreeParts = parts.length >= 3;
385
- const registeredChannelIds = listChannelPlugins().map((p) => p.id);
386
- const firstPartIsChannel = [...new Set([
387
- ...registeredChannelIds,
388
- "cli",
389
- "gateway",
390
- "local"
391
- ])].includes(parts[0]);
392
- if (hasAtLeastThreeParts && firstPartIsChannel) {
393
- channel = parts[0];
394
- to = parts[1];
395
- actualMessage = parts.slice(2).join(":");
396
- log.info({
397
- jobId: job.id,
398
- channel,
399
- to,
400
- parsedFrom: "message",
401
- originalLength: text.length
402
- }, "Parsed delivery from payload text");
403
- } else {
404
- channel = "cli";
405
- to = "cron";
406
- actualMessage = text;
407
- log.debug({
408
- jobId: job.id,
409
- partsCount: parts.length,
410
- firstPart: parts[0],
411
- hasDelivery: !!job.delivery
412
- }, "Using default delivery - message format not recognized");
413
- }
414
- }
400
+ const delivery = job.delivery;
401
+ const channel = delivery?.channel ?? "local";
402
+ const to = delivery?.to ?? "";
403
+ const actualMessage = text;
415
404
  const timeoutPromise = new Promise((_, reject) => {
416
405
  setTimeout(() => reject(/* @__PURE__ */ new Error(`Job timed out after ${timeout}ms`)), timeout);
417
406
  });
418
407
  const executePromise = (async () => {
419
408
  if (signal.aborted) throw new Error("Job was aborted");
420
- if (channel === "local") {
409
+ if (!delivery || delivery.mode === "none" || channel === "local") {
421
410
  log.info({
422
411
  jobId: job.id,
423
412
  messageLength: actualMessage.length
424
- }, "Cron main session: local channel — no outbound publish");
413
+ }, "Cron main session: no outbound publish");
414
+ return {
415
+ status: "ok",
416
+ summary: actualMessage.slice(0, 200)
417
+ };
418
+ }
419
+ if (delivery.mode === "webhook") {
420
+ if (!to.trim()) throw new Error("Webhook delivery requires delivery.to");
421
+ await postCronWebhook(to, {
422
+ jobId: job.id,
423
+ jobName: job.name,
424
+ payloadKind: job.payload.kind,
425
+ text: actualMessage
426
+ });
425
427
  return {
426
428
  status: "ok",
427
429
  summary: actualMessage.slice(0, 200)
428
430
  };
429
431
  }
432
+ if (!this.messageBus) return {
433
+ status: "error",
434
+ error: "MessageBus not available"
435
+ };
430
436
  const outbound = await this.buildCronOutboundMessage(channel, to, actualMessage);
431
437
  await this.messageBus.publishOutbound(outbound);
432
438
  log.info({
@@ -451,14 +457,15 @@ var DefaultJobExecutor = class {
451
457
  status: "skipped",
452
458
  error: "Isolated job requires non-empty message"
453
459
  };
460
+ const explicitSessionKey = job.sessionTarget.startsWith("session:") ? job.sessionTarget.slice(8).trim() : job.sessionTarget === "current" ? job.sessionKey?.trim() : job.sessionTarget === "isolated" ? job.sessionKey?.trim() : void 0;
454
461
  const aid = job.agentId?.trim();
455
462
  const fallbackAgentId = this.getDefaultCronAgentId?.() ?? "main";
456
- const sessionKey = buildSessionKey({
463
+ const sessionKey = explicitSessionKey || buildSessionKey({
457
464
  agentId: normalizeAgentId(aid || fallbackAgentId),
458
465
  source: "cron",
459
466
  accountId: "default",
460
467
  peerKind: "dm",
461
- peerId: job.id
468
+ peerId: `${job.id}-${crypto.randomUUID().slice(0, 8)}`
462
469
  });
463
470
  const timeoutPromise = new Promise((_, reject) => {
464
471
  setTimeout(() => reject(/* @__PURE__ */ new Error(`Job timed out after ${timeout}ms`)), timeout);
@@ -468,15 +475,12 @@ var DefaultJobExecutor = class {
468
475
  await this.agentService.sessionConfig.applyCronJobWorkingDirectory(sessionKey, job.workingDirectory);
469
476
  const jobModel = resolveIsolatedCronJobModel(job);
470
477
  if (jobModel) {
471
- if (!await this.agentService.switchModelForSession(sessionKey, jobModel)) {
472
- log.warn({
473
- jobId: job.id,
474
- sessionKey,
475
- model: jobModel
476
- }, "Cron job model invalid; using agent default");
477
- await this.agentService.resetSessionModelToAgentDefault(sessionKey);
478
- }
479
- } else await this.agentService.resetSessionModelToAgentDefault(sessionKey);
478
+ if (!await this.agentService.sessionConfig.applyCronJobModelOverride(sessionKey, jobModel)) log.warn({
479
+ jobId: job.id,
480
+ sessionKey,
481
+ model: jobModel
482
+ }, "Cron job model invalid; using agent default");
483
+ } else await this.agentService.sessionConfig.applyCronJobModelOverride(sessionKey, void 0);
480
484
  const response = await this.agentService.turnDispatcher.processDirect(message, sessionKey);
481
485
  const model = this.agentService.getModelForSession(sessionKey);
482
486
  log.info({
@@ -487,7 +491,7 @@ var DefaultJobExecutor = class {
487
491
  }, "Agent execution completed");
488
492
  const delivery = job.delivery;
489
493
  const outboundChannel = delivery?.channel;
490
- if (delivery && delivery.mode !== "none" && outboundChannel !== "local" && delivery.to) {
494
+ if (delivery && delivery.mode !== "none" && delivery.to) {
491
495
  if (shouldSilence(response, 300) || response.trim() === "NO_REPLY") return {
492
496
  status: "ok",
493
497
  summary: response.slice(0, 200),
@@ -498,6 +502,33 @@ var DefaultJobExecutor = class {
498
502
  };
499
503
  const { stripped } = stripHeartbeatToken(response);
500
504
  const outboundText = stripped || response.trim();
505
+ if (delivery.mode === "webhook") {
506
+ await postCronWebhook(delivery.to, {
507
+ jobId: job.id,
508
+ jobName: job.name,
509
+ payloadKind: job.payload.kind,
510
+ sessionKey,
511
+ model,
512
+ text: outboundText
513
+ });
514
+ log.info({ jobId: job.id }, "Delivered agent response webhook");
515
+ return {
516
+ status: "ok",
517
+ summary: response.slice(0, 200),
518
+ sessionId: sessionKey,
519
+ sessionKey,
520
+ sessionType: "cron",
521
+ model
522
+ };
523
+ }
524
+ if (outboundChannel === "local") return {
525
+ status: "ok",
526
+ summary: response.slice(0, 200),
527
+ sessionId: sessionKey,
528
+ sessionKey,
529
+ sessionType: "cron",
530
+ model
531
+ };
501
532
  const targetChannel = outboundChannel || "cli";
502
533
  const outbound = await this.buildCronOutboundMessage(targetChannel, delivery.to, outboundText);
503
534
  await this.messageBus.publishOutbound(outbound);
@@ -1 +1 @@
1
- {"version":3,"file":"executor.js","names":[],"sources":["../../../src/cron/executor.ts"],"sourcesContent":["// Cron job executor with timeout, retry logic and agent integration\nimport type { WorkflowRunView } from '../workflows/domain/index.js';\nimport type {\n CronRunOutcome,\n CronWorkflowRunStarter,\n HeartbeatWakeSink,\n JobData,\n JobExecution,\n JobExecutor,\n JobExecutorDeps,\n} from './types.js';\nimport type { OutboundMessage } from '../channels/transport-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport {\n getChannelPlugin,\n listChannelPlugins,\n} from '../channels/plugins/registry.js';\nimport { getCronPayloadText } from './job-content.js';\nimport type { SessionStore } from '../session/store.js';\nimport type { CronRunLogStore } from './run-log-store.js';\nimport {\n DEFAULT_ACK_MAX_CHARS,\n NO_REPLY,\n shouldSilence,\n stripHeartbeatToken,\n} from '../heartbeat/tokens.js';\nimport { DEFAULT_AGENT_ID, normalizeAgentId } from '../agent/agent-scope.js';\nimport { buildSessionKey } from '../routing/session-key.js';\nimport {\n buildWorkflowRunCronSummary,\n buildWorkflowRunDeliveryText,\n isWorkflowRunCronSuccess,\n resolveWorkflowCronWaitMs,\n waitForWorkflowRunView,\n} from './workflow-run-completion.js';\n\nconst log = createLogger('CronExecutor');\n\n// Error backoff schedule in ms\nconst ERROR_BACKOFF_MS = [\n 30_000, // 1st error → 30s\n 60_000, // 2nd error → 1min\n 5 * 60_000, // 3rd error → 5min\n 15 * 60_000, // 4th error → 15min\n 60 * 60_000, // 5th+ error → 60min\n];\n\nfunction errorBackoffMs(consecutiveErrors: number): number {\n const idx = Math.min(consecutiveErrors - 1, ERROR_BACKOFF_MS.length - 1);\n return ERROR_BACKOFF_MS[Math.max(0, idx)];\n}\n\nfunction resolveIsolatedCronJobModel(job: JobData): string | undefined {\n const fromJob = job.model?.trim();\n if (fromJob) return fromJob;\n if (job.payload?.kind === 'agentTurn') {\n const m = job.payload.model?.trim();\n if (m) return m;\n }\n return undefined;\n}\n\nexport class DefaultJobExecutor implements JobExecutor {\n private history: Map<string, JobExecution[]> = new Map();\n private runningJobs = new Map<string, AbortController>();\n private agentService: any = null;\n private messageBus: any = null;\n private heartbeatService: HeartbeatWakeSink | null = null;\n private sessionStore: SessionStore | undefined;\n private runLogStore: CronRunLogStore | null = null;\n private getDefaultCronAgentId: (() => string) | null = null;\n private workflowRunService: CronWorkflowRunStarter | null = null;\n private goalRunner: JobExecutorDeps['goalRunner'] | null = null;\n\n setRunLogStore(store: CronRunLogStore | null): void {\n this.runLogStore = store;\n }\n\n setDeps(deps: JobExecutorDeps): void {\n this.agentService = deps.agentService;\n this.messageBus = deps.messageBus;\n this.heartbeatService = deps.heartbeatService ?? null;\n if (deps.sessionStore !== undefined) {\n this.sessionStore = deps.sessionStore;\n }\n this.getDefaultCronAgentId = deps.getDefaultCronAgentId ?? null;\n this.workflowRunService = deps.workflowRunService ?? null;\n this.goalRunner = deps.goalRunner ?? null;\n }\n\n private async buildCronOutboundMessage(\n channel: string,\n to: string,\n content: string,\n ): Promise<OutboundMessage> {\n const plugin = getChannelPlugin(channel);\n if (plugin?.cronDelivery) {\n const { chatId, accountId, metadata } = await plugin.cronDelivery.normalizeDeliveryTarget(\n to,\n this.sessionStore,\n );\n const meta =\n accountId || metadata\n ? { ...(metadata ?? {}), ...(accountId ? { accountId } : {}) }\n : undefined;\n return {\n channel,\n chat_id: chatId,\n content,\n type: 'message',\n ...(meta && Object.keys(meta).length > 0 ? { metadata: meta } : {}),\n };\n }\n return {\n channel,\n chat_id: to,\n content,\n type: 'message',\n };\n }\n\n async execute(job: JobData, signal: AbortSignal, deps?: JobExecutorDeps): Promise<void> {\n // Set deps if provided\n if (deps) {\n this.setDeps(deps);\n }\n\n const executionId = crypto.randomUUID();\n const execution: JobExecution = {\n id: executionId,\n jobId: job.id,\n status: 'running',\n startedAt: new Date().toISOString(),\n retryCount: 0,\n };\n\n // Record execution start\n this.addToHistory(job.id, execution);\n this.runningJobs.set(job.id, new AbortController());\n\n log.info(\n { jobId: job.id, executionId, preview: getCronPayloadText(job).slice(0, 100) },\n 'Job executing'\n );\n\n let result: CronRunOutcome;\n\n try {\n // Check for cancellation\n if (signal.aborted) {\n throw new Error('Job was cancelled before execution');\n }\n\n // Execute the job\n result = await this.performJob(job, signal);\n\n // Mark as success/failed\n execution.status = result.status === 'ok' ? 'success' : result.status === 'skipped' ? 'cancelled' : 'failed';\n execution.endedAt = new Date().toISOString();\n execution.duration = Date.now() - new Date(execution.startedAt).getTime();\n execution.summary = result.summary;\n execution.error = result.error;\n execution.sessionId = result.sessionId;\n execution.sessionKey = result.sessionKey;\n execution.sessionType = result.sessionType;\n execution.model = result.model;\n execution.workflowRunId = result.workflowRunId;\n\n if (result.status === 'ok') {\n log.info(\n { jobId: job.id, executionId, duration: execution.duration },\n 'Job completed'\n );\n } else if (result.status === 'skipped') {\n log.warn({ jobId: job.id, executionId, reason: result.error }, 'Job skipped');\n } else {\n log.error(\n { jobId: job.id, executionId, errorMessage: result.error, phase: 'cron.execute' },\n `Job failed: ${result.error ?? 'unknown'}`,\n );\n }\n\n if (result.status === 'ok' && this.heartbeatService) {\n try {\n this.heartbeatService.requestNow({ reason: `cron:${job.id}` });\n } catch (e) {\n log.warn({ jobId: job.id, err: e }, 'Heartbeat wake after cron failed');\n }\n }\n } catch (error) {\n execution.status = 'failed';\n execution.endedAt = new Date().toISOString();\n execution.duration = Date.now() - new Date(execution.startedAt).getTime();\n execution.error = error instanceof Error ? error.message : String(error);\n\n log.error(\n {\n err: error,\n errorMessage: execution.error,\n jobId: job.id,\n executionId,\n phase: 'cron.execute',\n },\n `Job execution error: ${execution.error}`,\n );\n\n result = {\n status: 'error',\n error: execution.error,\n };\n } finally {\n this.runningJobs.delete(job.id);\n }\n\n if (this.runLogStore) {\n await this.runLogStore.appendCompleted(execution);\n }\n\n return;\n }\n\n /**\n * Perform the actual job work - integrate with AgentService\n */\n protected async performJob(job: JobData, signal: AbortSignal): Promise<CronRunOutcome> {\n const timeout = job.timeout || 60000;\n const sessionTarget = job.sessionTarget || 'main';\n\n // Check for abort before starting\n if (signal.aborted) {\n return { status: 'skipped', error: 'Job was aborted before execution' };\n }\n\n try {\n if (job.payload.kind === 'goalContinue') {\n return await this.executeGoalContinue(job);\n }\n\n if (job.payload.kind === 'workflowRun') {\n return await this.executeWorkflowRun(job, signal);\n }\n\n // If no agent service, fall back to basic execution\n if (!this.agentService || !this.messageBus) {\n log.warn({ jobId: job.id }, 'No agent service configured, using basic execution');\n return this.basicExecute(job, signal, timeout);\n }\n\n if (sessionTarget === 'main') {\n return await this.executeMainSession(job, signal, timeout);\n } else {\n return await this.executeIsolated(job, signal, timeout);\n }\n } catch (error) {\n return {\n status: 'error',\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n private async executeGoalContinue(job: JobData): Promise<CronRunOutcome> {\n if (job.payload.kind !== 'goalContinue') {\n return { status: 'error', error: 'Cron job payload is not goalContinue' };\n }\n if (!this.goalRunner) {\n return { status: 'error', error: 'Goal runner is not configured for cron' };\n }\n const item = this.goalRunner.enqueue(job.payload.goalId, {\n message: job.payload.message,\n maxRetries: job.payload.maxRetries ?? job.maxRetries,\n source: 'cron',\n });\n return {\n status: 'ok',\n summary: `Queued goal ${job.payload.goalId} (${item.status})`,\n sessionId: item.sessionKey,\n sessionKey: item.sessionKey,\n sessionType: 'goal',\n };\n }\n\n private async executeWorkflowRun(job: JobData, signal: AbortSignal): Promise<CronRunOutcome> {\n if (signal.aborted) {\n return { status: 'skipped', error: 'Job was aborted before workflow run start' };\n }\n\n if (job.payload.kind !== 'workflowRun') {\n return { status: 'error', error: 'Cron job payload is not workflowRun' };\n }\n\n if (!this.workflowRunService) {\n return { status: 'error', error: 'Workflow run service is not configured for cron' };\n }\n\n const fallbackAgentId = this.getDefaultCronAgentId?.() ?? DEFAULT_AGENT_ID;\n const agentId = normalizeAgentId(job.payload.agentId || job.agentId || fallbackAgentId);\n const fireId = job.payload.source?.fireId || crypto.randomUUID();\n const source = {\n kind: 'cron' as const,\n scheduleId: job.payload.source?.scheduleId || job.id,\n fireId,\n scheduledAtMs: job.payload.source?.scheduledAtMs ?? Date.now(),\n };\n const result = await this.workflowRunService.startWorkflowRun({\n agentId,\n definitionId: job.payload.definitionId,\n input: job.payload.input,\n inputEnvelope: job.payload.inputEnvelope,\n goal: job.payload.goal,\n source,\n idempotencyKey: `cron:${job.id}:${fireId}`,\n });\n\n if (result.ok === false) {\n return {\n status: 'error',\n error: result.message,\n };\n }\n\n log.info(\n {\n jobId: job.id,\n workflowRunId: result.runId,\n definitionId: job.payload.definitionId,\n sessionKey: result.sessionKey,\n },\n 'Workflow run started from cron',\n );\n\n const waitForCompletion = job.payload.waitForCompletion !== false;\n if (!waitForCompletion || !this.workflowRunService.readWorkflowRunView) {\n return {\n status: 'ok',\n summary: `Started workflow run ${result.runId}`,\n sessionId: result.sessionKey,\n sessionKey: result.sessionKey,\n sessionType: 'workflow',\n workflowRunId: result.runId,\n };\n }\n\n return this.waitForWorkflowRunOutcome({\n job,\n agentId,\n sessionKey: result.sessionKey,\n initialRunId: result.runId,\n signal,\n });\n }\n\n private async waitForWorkflowRunOutcome(params: {\n job: JobData;\n agentId: string;\n sessionKey: string;\n initialRunId: string;\n signal: AbortSignal;\n }): Promise<CronRunOutcome> {\n const waitMs = resolveWorkflowCronWaitMs(params.job.timeout);\n const maxWorkflowRetries = Math.max(0, params.job.maxRetries ?? 0);\n let runId = params.initialRunId;\n let attempt = 0;\n\n while (attempt <= maxWorkflowRetries) {\n const waitResult = await waitForWorkflowRunView({\n readView: (id) => this.workflowRunService!.readWorkflowRunView!(params.agentId, id),\n runId,\n signal: params.signal,\n timeoutMs: waitMs,\n });\n\n if (waitResult.kind === 'aborted') {\n return { status: 'skipped', error: 'Job was aborted while waiting for workflow run' };\n }\n\n if (waitResult.kind === 'timeout') {\n return {\n status: 'error',\n error: `Workflow run ${runId} did not finish within ${waitMs}ms`,\n summary: waitResult.lastView ? buildWorkflowRunCronSummary(waitResult.lastView) : undefined,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n const view = waitResult.view;\n const summary = buildWorkflowRunCronSummary(view);\n const succeeded = isWorkflowRunCronSuccess(view);\n\n if (succeeded) {\n const baseOutcome: CronRunOutcome = {\n status: 'ok',\n summary,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n await this.deliverWorkflowRunOutcome(params.job, view, baseOutcome);\n return baseOutcome;\n }\n\n const canRetry =\n attempt < maxWorkflowRetries &&\n view.run.status === 'failed' &&\n Boolean(this.workflowRunService?.retryWorkflowRun);\n\n if (!canRetry) {\n return {\n status: 'error',\n summary,\n error: summary,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n const retryResult = await this.workflowRunService!.retryWorkflowRun!({\n agentId: params.agentId,\n runId,\n });\n if (retryResult.ok === false) {\n return {\n status: 'error',\n error: retryResult.message,\n summary,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n runId = retryResult.runId;\n attempt += 1;\n log.info(\n { jobId: params.job.id, attempt, workflowRunId: runId },\n 'Retrying failed workflow run from cron',\n );\n }\n\n return {\n status: 'error',\n error: 'Workflow run retries exhausted',\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n private async deliverWorkflowRunOutcome(\n job: JobData,\n view: WorkflowRunView,\n outcome: CronRunOutcome,\n ): Promise<void> {\n if (!this.messageBus) return;\n const delivery = job.delivery;\n if (!delivery || delivery.mode === 'none' || delivery.channel === 'local' || !delivery.to?.trim()) {\n return;\n }\n\n try {\n const text = buildWorkflowRunDeliveryText(view);\n const outbound = await this.buildCronOutboundMessage(delivery.channel!, delivery.to!, text);\n await this.messageBus.publishOutbound(outbound);\n log.info(\n { jobId: job.id, channel: delivery.channel, to: outbound.chat_id, workflowRunId: view.run.id },\n 'Delivered workflow run result from cron',\n );\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ jobId: job.id, err, workflowRunId: view.run.id }, `Workflow cron delivery failed: ${em}`);\n if (outcome.status === 'ok') {\n outcome.summary = `${outcome.summary ?? ''} (delivery failed: ${em})`.trim();\n }\n }\n }\n\n /**\n * Execute in main session - sends system event\n */\n private async executeMainSession(\n job: JobData,\n signal: AbortSignal,\n timeout: number\n ): Promise<CronRunOutcome> {\n const text = getCronPayloadText(job);\n\n if (!text || !text.trim()) {\n return { status: 'skipped', error: 'Main session job requires non-empty message' };\n }\n\n // Parse delivery from job config, or parse routing from payload text: \"channel:chat_id:content\"\n let channel: string;\n let to: string;\n let actualMessage: string;\n\n if (job.delivery?.channel === 'local') {\n channel = 'local';\n to = '';\n actualMessage = text;\n } else if (job.delivery?.channel && job.delivery?.to) {\n // Use explicit delivery config\n channel = job.delivery.channel;\n to = job.delivery.to;\n actualMessage = text;\n } else {\n // Parse from payload text: \"channel:chat_id:content\"\n const parts = text.split(':');\n const hasAtLeastThreeParts = parts.length >= 3;\n \n // Check if first part looks like a known channel\n const registeredChannelIds = listChannelPlugins().map((p) => p.id);\n const knownChannels = [...new Set([...registeredChannelIds, 'cli', 'gateway', 'local'])];\n const firstPartIsChannel = knownChannels.includes(parts[0]);\n \n if (hasAtLeastThreeParts && firstPartIsChannel) {\n channel = parts[0];\n to = parts[1];\n actualMessage = parts.slice(2).join(':');\n log.info(\n { jobId: job.id, channel, to, parsedFrom: 'message', originalLength: text.length },\n 'Parsed delivery from payload text'\n );\n } else {\n // Fallback to defaults\n channel = 'cli';\n to = 'cron';\n actualMessage = text;\n log.debug(\n { jobId: job.id, partsCount: parts.length, firstPart: parts[0], hasDelivery: !!job.delivery },\n 'Using default delivery - message format not recognized'\n );\n }\n }\n\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error(`Job timed out after ${timeout}ms`)), timeout);\n });\n\n // Create execution promise\n const executePromise = (async () => {\n // Check for abort\n if (signal.aborted) {\n throw new Error('Job was aborted');\n }\n\n if (channel === 'local') {\n log.info(\n { jobId: job.id, messageLength: actualMessage.length },\n 'Cron main session: local channel — no outbound publish'\n );\n return {\n status: 'ok' as const,\n summary: actualMessage.slice(0, 200),\n };\n }\n\n const outbound = await this.buildCronOutboundMessage(channel, to, actualMessage);\n\n await this.messageBus.publishOutbound(outbound);\n\n log.info(\n { jobId: job.id, channel, to: outbound.chat_id, messageLength: actualMessage.length },\n 'Sent message to main session'\n );\n\n return {\n status: 'ok' as const,\n summary: actualMessage.slice(0, 200),\n };\n })();\n\n // Race against timeout\n return await Promise.race([executePromise, timeoutPromise]);\n }\n\n /**\n * Execute in isolated mode - runs agent independently\n */\n private async executeIsolated(\n job: JobData,\n signal: AbortSignal,\n timeout: number\n ): Promise<CronRunOutcome> {\n const message = getCronPayloadText(job);\n\n if (!message || !message.trim()) {\n return { status: 'skipped', error: 'Isolated job requires non-empty message' };\n }\n\n const aid = job.agentId?.trim();\n const fallbackAgentId = this.getDefaultCronAgentId?.() ?? DEFAULT_AGENT_ID;\n const sessionKey = buildSessionKey({\n agentId: normalizeAgentId(aid || fallbackAgentId),\n source: 'cron',\n accountId: 'default',\n peerKind: 'dm',\n peerId: job.id,\n });\n\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error(`Job timed out after ${timeout}ms`)), timeout);\n });\n\n // Create execution promise\n const executePromise = (async () => {\n // Check for abort\n if (signal.aborted) {\n throw new Error('Job was aborted');\n }\n\n await this.agentService.sessionConfig.applyCronJobWorkingDirectory(sessionKey, job.workingDirectory);\n\n const jobModel = resolveIsolatedCronJobModel(job);\n if (jobModel) {\n const ok = await this.agentService.switchModelForSession(sessionKey, jobModel);\n if (!ok) {\n log.warn({ jobId: job.id, sessionKey, model: jobModel }, 'Cron job model invalid; using agent default');\n await this.agentService.resetSessionModelToAgentDefault(sessionKey);\n }\n } else {\n await this.agentService.resetSessionModelToAgentDefault(sessionKey);\n }\n\n const response = await this.agentService.turnDispatcher.processDirect(message, sessionKey);\n\n const model = this.agentService.getModelForSession(sessionKey);\n\n log.info(\n { jobId: job.id, sessionKey, responseLength: response.length, model },\n 'Agent execution completed'\n );\n\n // Handle delivery (`local` channel or `mode: none` = no outbound; transcript in SessionStore)\n const delivery = job.delivery;\n const outboundChannel = delivery?.channel;\n const shouldPublish =\n delivery &&\n delivery.mode !== 'none' &&\n outboundChannel !== 'local' &&\n delivery.to;\n\n if (shouldPublish) {\n if (shouldSilence(response, DEFAULT_ACK_MAX_CHARS) || response.trim() === NO_REPLY) {\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n }\n const { stripped } = stripHeartbeatToken(response);\n const outboundText = stripped || response.trim();\n\n const targetChannel = outboundChannel || 'cli';\n const outbound = await this.buildCronOutboundMessage(targetChannel, delivery.to, outboundText);\n\n await this.messageBus.publishOutbound(outbound);\n\n log.info(\n { jobId: job.id, channel: targetChannel, to: outbound.chat_id },\n 'Delivered agent response'\n );\n\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n }\n\n // No outbound delivery: transcript is in SessionStore under `sessionKey`.\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n })();\n\n // Race against timeout\n try {\n return await Promise.race([executePromise, timeoutPromise]);\n } finally {\n const { retireSessionMcpRuntimeForSessionKey } = await import('../agent/mcp/bundle-mcp-tools.js');\n await retireSessionMcpRuntimeForSessionKey({\n sessionKey,\n reason: 'cron-isolated-end',\n }).catch(() => {});\n }\n }\n\n /**\n * Basic execution without agent service (fallback)\n */\n private async basicExecute(\n job: JobData,\n signal: AbortSignal,\n timeout: number\n ): Promise<CronRunOutcome> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error(`Job timed out after ${timeout}ms`));\n }, timeout);\n\n // Listen for abort signal\n const abortHandler = () => {\n clearTimeout(timeoutId);\n reject(new Error('Job was aborted'));\n };\n signal.addEventListener('abort', abortHandler);\n\n // Simulate basic work\n setTimeout(() => {\n clearTimeout(timeoutId);\n signal.removeEventListener('abort', abortHandler);\n\n if (signal.aborted) {\n reject(new Error('Job was aborted'));\n } else {\n resolve({\n status: 'ok',\n summary: `Executed: ${getCronPayloadText(job).slice(0, 100)}`,\n });\n }\n }, 100);\n });\n }\n\n /**\n * Cancel a running job\n */\n cancelJob(jobId: string): boolean {\n const controller = this.runningJobs.get(jobId);\n if (controller) {\n controller.abort();\n this.runningJobs.delete(jobId);\n return true;\n }\n return false;\n }\n\n /**\n * Get execution history for a job\n */\n getHistory(jobId: string, limit = 10): JobExecution[] {\n const history = this.history.get(jobId) || [];\n return history.slice(-limit);\n }\n\n /**\n * Get currently running executions\n */\n getRunningExecutions(): JobExecution[] {\n const result: JobExecution[] = [];\n for (const [jobId] of this.runningJobs) {\n const history = this.history.get(jobId);\n if (history) {\n const running = history.find((e) => e.status === 'running');\n if (running) result.push(running);\n }\n }\n return result;\n }\n\n /**\n * Check if a job is currently running\n */\n isRunning(jobId: string): boolean {\n return this.runningJobs.has(jobId);\n }\n\n /**\n * Get consecutive error count for a job\n */\n getConsecutiveErrors(jobId: string): number {\n const history = this.history.get(jobId) || [];\n // Find last execution\n for (let i = history.length - 1; i >= 0; i--) {\n if (history[i].status !== 'running') {\n if (history[i].status === 'failed' || history[i].status === 'cancelled') {\n // Count consecutive errors before this\n let count = 0;\n for (let j = i - 1; j >= 0; j--) {\n if (history[j].status === 'failed' || history[j].status === 'cancelled') {\n count++;\n } else {\n break;\n }\n }\n return count + 1;\n }\n return 0;\n }\n }\n return 0;\n }\n\n /**\n * Calculate backoff delay for a job\n */\n calculateBackoff(jobId: string): number {\n const errors = this.getConsecutiveErrors(jobId);\n return errorBackoffMs(errors);\n }\n\n /**\n * Clear old history entries\n */\n cleanupHistory(maxAgeDays = 7): void {\n const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;\n\n for (const [jobId, executions] of this.history) {\n this.history.set(\n jobId,\n executions.filter((e) => new Date(e.startedAt).getTime() > cutoff)\n );\n }\n }\n\n private addToHistory(jobId: string, execution: JobExecution): void {\n const existing = this.history.get(jobId) || [];\n existing.push(execution);\n // Keep last 100 executions per job\n if (existing.length > 100) {\n existing.shift();\n }\n this.history.set(jobId, existing);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"executor.js","names":[],"sources":["../../../src/cron/executor.ts"],"sourcesContent":["// Cron job executor with timeout, retry logic and agent integration\nimport type { WorkflowRunView } from '../workflows/domain/index.js';\nimport type {\n CronRunOutcome,\n CronWorkflowRunStarter,\n HeartbeatWakeSink,\n JobData,\n JobExecution,\n JobExecutor,\n JobExecutorDeps,\n} from './types.js';\nimport type { OutboundMessage } from '../channels/transport-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport {\n getChannelPlugin,\n} from '../channels/plugins/registry.js';\nimport { getCronPayloadText } from './job-content.js';\nimport type { CronRunLogStore } from './run-log-store.js';\nimport {\n DEFAULT_ACK_MAX_CHARS,\n NO_REPLY,\n shouldSilence,\n stripHeartbeatToken,\n} from '../heartbeat/tokens.js';\nimport { DEFAULT_AGENT_ID, normalizeAgentId } from '../agent/agent-scope.js';\nimport { buildSessionKey } from '../routing/session-key.js';\nimport {\n buildWorkflowRunCronSummary,\n buildWorkflowRunDeliveryText,\n isWorkflowRunCronSuccess,\n resolveWorkflowCronWaitMs,\n waitForWorkflowRunView,\n} from './workflow-run-completion.js';\n\nconst log = createLogger('CronExecutor');\n\n// Error backoff schedule in ms\nconst ERROR_BACKOFF_MS = [\n 30_000, // 1st error → 30s\n 60_000, // 2nd error → 1min\n 5 * 60_000, // 3rd error → 5min\n 15 * 60_000, // 4th error → 15min\n 60 * 60_000, // 5th+ error → 60min\n];\n\nfunction errorBackoffMs(consecutiveErrors: number): number {\n const idx = Math.min(consecutiveErrors - 1, ERROR_BACKOFF_MS.length - 1);\n return ERROR_BACKOFF_MS[Math.max(0, idx)];\n}\n\nfunction resolveIsolatedCronJobModel(job: JobData): string | undefined {\n if (job.payload?.kind === 'agentTurn') {\n const m = job.payload.model?.trim();\n if (m) return m;\n }\n return undefined;\n}\n\nasync function postCronWebhook(url: string, payload: unknown): Promise<void> {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Webhook delivery failed: HTTP ${res.status}${text ? ` ${text.slice(0, 200)}` : ''}`);\n }\n}\n\nexport class DefaultJobExecutor implements JobExecutor {\n private history: Map<string, JobExecution[]> = new Map();\n private runningJobs = new Map<string, AbortController>();\n private agentService: any = null;\n private messageBus: any = null;\n private heartbeatService: HeartbeatWakeSink | null = null;\n private sessionStore: JobExecutorDeps['sessionStore'] | undefined;\n private runLogStore: CronRunLogStore | null = null;\n private getDefaultCronAgentId: (() => string) | null = null;\n private workflowRunService: CronWorkflowRunStarter | null = null;\n private goalRunner: JobExecutorDeps['goalRunner'] | null = null;\n\n setRunLogStore(store: CronRunLogStore | null): void {\n this.runLogStore = store;\n }\n\n setDeps(deps: JobExecutorDeps): void {\n this.agentService = deps.agentService;\n this.messageBus = deps.messageBus;\n this.heartbeatService = deps.heartbeatService ?? null;\n if (deps.sessionStore !== undefined) {\n this.sessionStore = deps.sessionStore;\n }\n this.getDefaultCronAgentId = deps.getDefaultCronAgentId ?? null;\n this.workflowRunService = deps.workflowRunService ?? null;\n this.goalRunner = deps.goalRunner ?? null;\n }\n\n private async buildCronOutboundMessage(\n channel: string,\n to: string,\n content: string,\n ): Promise<OutboundMessage> {\n const plugin = getChannelPlugin(channel);\n if (plugin?.cronDelivery) {\n const { chatId, accountId, metadata } = await plugin.cronDelivery.normalizeDeliveryTarget(\n to,\n this.sessionStore,\n );\n const meta =\n accountId || metadata\n ? { ...(metadata ?? {}), ...(accountId ? { accountId } : {}) }\n : undefined;\n return {\n channel,\n chat_id: chatId,\n content,\n type: 'message',\n ...(meta && Object.keys(meta).length > 0 ? { metadata: meta } : {}),\n };\n }\n return {\n channel,\n chat_id: to,\n content,\n type: 'message',\n };\n }\n\n async execute(job: JobData, signal: AbortSignal, deps?: JobExecutorDeps): Promise<void> {\n // Set deps if provided\n if (deps) {\n this.setDeps(deps);\n }\n\n const executionId = crypto.randomUUID();\n const execution: JobExecution = {\n id: executionId,\n jobId: job.id,\n status: 'running',\n startedAt: new Date().toISOString(),\n retryCount: 0,\n sessionId: job.sessionKey,\n sessionKey: job.sessionKey,\n sessionType: job.sessionKey ? 'cron' : undefined,\n };\n\n // Record execution start\n this.addToHistory(job.id, execution);\n if (this.runLogStore) {\n await this.runLogStore.upsert(execution);\n }\n this.runningJobs.set(job.id, new AbortController());\n\n log.info(\n { jobId: job.id, executionId, preview: getCronPayloadText(job).slice(0, 100) },\n 'Job executing'\n );\n\n let result: CronRunOutcome;\n\n try {\n // Check for cancellation\n if (signal.aborted) {\n throw new Error('Job was cancelled before execution');\n }\n\n // Execute the job\n result = await this.performJob(job, signal);\n\n // Mark as success/failed\n execution.status = result.status === 'ok' ? 'success' : result.status === 'skipped' ? 'cancelled' : 'failed';\n execution.endedAt = new Date().toISOString();\n execution.duration = Date.now() - new Date(execution.startedAt).getTime();\n execution.summary = result.summary;\n execution.error = result.error;\n execution.sessionId = result.sessionId;\n execution.sessionKey = result.sessionKey;\n execution.sessionType = result.sessionType;\n execution.model = result.model;\n execution.workflowRunId = result.workflowRunId;\n\n if (result.status === 'ok') {\n log.info(\n { jobId: job.id, executionId, duration: execution.duration },\n 'Job completed'\n );\n } else if (result.status === 'skipped') {\n log.warn({ jobId: job.id, executionId, reason: result.error }, 'Job skipped');\n } else {\n log.error(\n { jobId: job.id, executionId, errorMessage: result.error, phase: 'cron.execute' },\n `Job failed: ${result.error ?? 'unknown'}`,\n );\n }\n\n if (result.status === 'ok' && job.wakeMode === 'next-heartbeat' && this.heartbeatService) {\n try {\n this.heartbeatService.requestNow({ reason: `cron:${job.id}` });\n } catch (e) {\n log.warn({ jobId: job.id, err: e }, 'Heartbeat wake after cron failed');\n }\n }\n } catch (error) {\n execution.status = 'failed';\n execution.endedAt = new Date().toISOString();\n execution.duration = Date.now() - new Date(execution.startedAt).getTime();\n execution.error = error instanceof Error ? error.message : String(error);\n\n log.error(\n {\n err: error,\n errorMessage: execution.error,\n jobId: job.id,\n executionId,\n phase: 'cron.execute',\n },\n `Job execution error: ${execution.error}`,\n );\n\n result = {\n status: 'error',\n error: execution.error,\n };\n } finally {\n this.runningJobs.delete(job.id);\n }\n\n if (this.runLogStore) {\n await this.runLogStore.upsert(execution);\n }\n\n return;\n }\n\n /**\n * Perform the actual job work - integrate with AgentService\n */\n protected async performJob(job: JobData, signal: AbortSignal): Promise<CronRunOutcome> {\n const timeout =\n job.payload.kind === 'agentTurn' && job.payload.timeoutSeconds\n ? job.payload.timeoutSeconds * 1000\n : 180_000;\n const sessionTarget = job.sessionTarget;\n\n // Check for abort before starting\n if (signal.aborted) {\n return { status: 'skipped', error: 'Job was aborted before execution' };\n }\n\n try {\n if (job.payload.kind === 'goalContinue') {\n return await this.executeGoalContinue(job);\n }\n\n if (job.payload.kind === 'workflowRun') {\n return await this.executeWorkflowRun(job, signal);\n }\n\n // If no agent service, fall back to basic execution\n if (!this.agentService || !this.messageBus) {\n log.warn({ jobId: job.id }, 'No agent service configured, using basic execution');\n return this.basicExecute(job, signal, timeout);\n }\n\n if (sessionTarget === 'main') {\n return await this.executeMainSession(job, signal, timeout);\n } else {\n return await this.executeIsolated(job, signal, timeout);\n }\n } catch (error) {\n return {\n status: 'error',\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n private async executeGoalContinue(job: JobData): Promise<CronRunOutcome> {\n if (job.payload.kind !== 'goalContinue') {\n return { status: 'error', error: 'Cron job payload is not goalContinue' };\n }\n if (!this.goalRunner) {\n return { status: 'error', error: 'Goal runner is not configured for cron' };\n }\n const item = this.goalRunner.enqueue(job.payload.goalId, {\n message: job.payload.message,\n maxRetries: job.payload.maxRetries ?? 0,\n source: 'cron',\n });\n return {\n status: 'ok',\n summary: `Queued goal ${job.payload.goalId} (${item.status})`,\n sessionId: item.sessionKey,\n sessionKey: item.sessionKey,\n sessionType: 'goal',\n };\n }\n\n private async executeWorkflowRun(job: JobData, signal: AbortSignal): Promise<CronRunOutcome> {\n if (signal.aborted) {\n return { status: 'skipped', error: 'Job was aborted before workflow run start' };\n }\n\n if (job.payload.kind !== 'workflowRun') {\n return { status: 'error', error: 'Cron job payload is not workflowRun' };\n }\n\n if (!this.workflowRunService) {\n return { status: 'error', error: 'Workflow run service is not configured for cron' };\n }\n\n const fallbackAgentId = this.getDefaultCronAgentId?.() ?? DEFAULT_AGENT_ID;\n const agentId = normalizeAgentId(job.payload.agentId || job.agentId || fallbackAgentId);\n const fireId = job.payload.source?.fireId || crypto.randomUUID();\n const source = {\n kind: 'cron' as const,\n scheduleId: job.payload.source?.scheduleId || job.id,\n fireId,\n scheduledAtMs: job.payload.source?.scheduledAtMs ?? Date.now(),\n };\n const result = await this.workflowRunService.startWorkflowRun({\n agentId,\n definitionId: job.payload.definitionId,\n input: job.payload.input,\n inputEnvelope: job.payload.inputEnvelope,\n goal: job.payload.goal,\n source,\n idempotencyKey: `cron:${job.id}:${fireId}`,\n });\n\n if (result.ok === false) {\n return {\n status: 'error',\n error: result.message,\n };\n }\n\n log.info(\n {\n jobId: job.id,\n workflowRunId: result.runId,\n definitionId: job.payload.definitionId,\n sessionKey: result.sessionKey,\n },\n 'Workflow run started from cron',\n );\n\n const waitForCompletion = job.payload.waitForCompletion !== false;\n if (!waitForCompletion || !this.workflowRunService.readWorkflowRunView) {\n return {\n status: 'ok',\n summary: `Started workflow run ${result.runId}`,\n sessionId: result.sessionKey,\n sessionKey: result.sessionKey,\n sessionType: 'workflow',\n workflowRunId: result.runId,\n };\n }\n\n return this.waitForWorkflowRunOutcome({\n job,\n agentId,\n sessionKey: result.sessionKey,\n initialRunId: result.runId,\n signal,\n });\n }\n\n private async waitForWorkflowRunOutcome(params: {\n job: JobData;\n agentId: string;\n sessionKey: string;\n initialRunId: string;\n signal: AbortSignal;\n }): Promise<CronRunOutcome> {\n const waitMs = resolveWorkflowCronWaitMs(180_000);\n const maxWorkflowRetries =\n params.job.payload.kind === 'workflowRun'\n ? Math.max(0, Math.floor(params.job.payload.maxRetries ?? 0))\n : 0;\n let runId = params.initialRunId;\n let attempt = 0;\n\n while (attempt <= maxWorkflowRetries) {\n const waitResult = await waitForWorkflowRunView({\n readView: (id) => this.workflowRunService!.readWorkflowRunView!(params.agentId, id),\n runId,\n signal: params.signal,\n timeoutMs: waitMs,\n });\n\n if (waitResult.kind === 'aborted') {\n return { status: 'skipped', error: 'Job was aborted while waiting for workflow run' };\n }\n\n if (waitResult.kind === 'timeout') {\n return {\n status: 'error',\n error: `Workflow run ${runId} did not finish within ${waitMs}ms`,\n summary: waitResult.lastView ? buildWorkflowRunCronSummary(waitResult.lastView) : undefined,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n const view = waitResult.view;\n const summary = buildWorkflowRunCronSummary(view);\n const succeeded = isWorkflowRunCronSuccess(view);\n\n if (succeeded) {\n const baseOutcome: CronRunOutcome = {\n status: 'ok',\n summary,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n await this.deliverWorkflowRunOutcome(params.job, view, baseOutcome);\n return baseOutcome;\n }\n\n const canRetry =\n attempt < maxWorkflowRetries &&\n view.run.status === 'failed' &&\n Boolean(this.workflowRunService?.retryWorkflowRun);\n\n if (!canRetry) {\n return {\n status: 'error',\n summary,\n error: summary,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n const retryResult = await this.workflowRunService!.retryWorkflowRun!({\n agentId: params.agentId,\n runId,\n });\n if (retryResult.ok === false) {\n return {\n status: 'error',\n error: retryResult.message,\n summary,\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n runId = retryResult.runId;\n attempt += 1;\n log.info(\n { jobId: params.job.id, attempt, workflowRunId: runId },\n 'Retrying failed workflow run from cron',\n );\n }\n\n return {\n status: 'error',\n error: 'Workflow run retries exhausted',\n sessionId: params.sessionKey,\n sessionKey: params.sessionKey,\n sessionType: 'workflow',\n workflowRunId: runId,\n };\n }\n\n private async deliverWorkflowRunOutcome(\n job: JobData,\n view: WorkflowRunView,\n outcome: CronRunOutcome,\n ): Promise<void> {\n const delivery = job.delivery;\n if (!delivery || delivery.mode === 'none' || delivery.channel === 'local' || !delivery.to?.trim()) {\n return;\n }\n\n try {\n const text = buildWorkflowRunDeliveryText(view);\n if (delivery.mode === 'webhook') {\n await postCronWebhook(delivery.to, {\n jobId: job.id,\n jobName: job.name,\n payloadKind: job.payload.kind,\n workflowRunId: view.run.id,\n status: view.run.status,\n text,\n run: view.run,\n });\n log.info({ jobId: job.id, workflowRunId: view.run.id }, 'Delivered workflow run webhook from cron');\n return;\n }\n if (!this.messageBus || !delivery.channel) return;\n const outbound = await this.buildCronOutboundMessage(delivery.channel!, delivery.to!, text);\n await this.messageBus.publishOutbound(outbound);\n log.info(\n { jobId: job.id, channel: delivery.channel, to: outbound.chat_id, workflowRunId: view.run.id },\n 'Delivered workflow run result from cron',\n );\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ jobId: job.id, err, workflowRunId: view.run.id }, `Workflow cron delivery failed: ${em}`);\n if (outcome.status === 'ok') {\n outcome.summary = `${outcome.summary ?? ''} (delivery failed: ${em})`.trim();\n }\n }\n }\n\n /**\n * Execute in main session - sends system event\n */\n private async executeMainSession(\n job: JobData,\n signal: AbortSignal,\n timeout: number\n ): Promise<CronRunOutcome> {\n const text = getCronPayloadText(job);\n\n if (!text || !text.trim()) {\n return { status: 'skipped', error: 'Main session job requires non-empty message' };\n }\n\n const delivery = job.delivery;\n const channel = delivery?.channel ?? 'local';\n const to = delivery?.to ?? '';\n const actualMessage = text;\n\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error(`Job timed out after ${timeout}ms`)), timeout);\n });\n\n // Create execution promise\n const executePromise = (async () => {\n // Check for abort\n if (signal.aborted) {\n throw new Error('Job was aborted');\n }\n\n if (!delivery || delivery.mode === 'none' || channel === 'local') {\n log.info(\n { jobId: job.id, messageLength: actualMessage.length },\n 'Cron main session: no outbound publish'\n );\n return {\n status: 'ok' as const,\n summary: actualMessage.slice(0, 200),\n };\n }\n\n if (delivery.mode === 'webhook') {\n if (!to.trim()) {\n throw new Error('Webhook delivery requires delivery.to');\n }\n await postCronWebhook(to, {\n jobId: job.id,\n jobName: job.name,\n payloadKind: job.payload.kind,\n text: actualMessage,\n });\n return {\n status: 'ok' as const,\n summary: actualMessage.slice(0, 200),\n };\n }\n\n if (!this.messageBus) {\n return { status: 'error' as const, error: 'MessageBus not available' };\n }\n\n const outbound = await this.buildCronOutboundMessage(channel, to, actualMessage);\n\n await this.messageBus.publishOutbound(outbound);\n\n log.info(\n { jobId: job.id, channel, to: outbound.chat_id, messageLength: actualMessage.length },\n 'Sent message to main session'\n );\n\n return {\n status: 'ok' as const,\n summary: actualMessage.slice(0, 200),\n };\n })();\n\n // Race against timeout\n return await Promise.race([executePromise, timeoutPromise]);\n }\n\n /**\n * Execute in isolated mode - runs agent independently\n */\n private async executeIsolated(\n job: JobData,\n signal: AbortSignal,\n timeout: number\n ): Promise<CronRunOutcome> {\n const message = getCronPayloadText(job);\n\n if (!message || !message.trim()) {\n return { status: 'skipped', error: 'Isolated job requires non-empty message' };\n }\n\n const explicitSessionKey =\n job.sessionTarget.startsWith('session:')\n ? job.sessionTarget.slice('session:'.length).trim()\n : job.sessionTarget === 'current'\n ? job.sessionKey?.trim()\n : job.sessionTarget === 'isolated'\n ? job.sessionKey?.trim()\n : undefined;\n const aid = job.agentId?.trim();\n const fallbackAgentId = this.getDefaultCronAgentId?.() ?? DEFAULT_AGENT_ID;\n const sessionKey = explicitSessionKey || buildSessionKey({\n agentId: normalizeAgentId(aid || fallbackAgentId),\n source: 'cron',\n accountId: 'default',\n peerKind: 'dm',\n peerId: `${job.id}-${crypto.randomUUID().slice(0, 8)}`,\n });\n\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error(`Job timed out after ${timeout}ms`)), timeout);\n });\n\n // Create execution promise\n const executePromise = (async () => {\n // Check for abort\n if (signal.aborted) {\n throw new Error('Job was aborted');\n }\n\n await this.agentService.sessionConfig.applyCronJobWorkingDirectory(sessionKey, job.workingDirectory);\n\n const jobModel = resolveIsolatedCronJobModel(job);\n if (jobModel) {\n const ok = await this.agentService.sessionConfig.applyCronJobModelOverride(sessionKey, jobModel);\n if (!ok) {\n log.warn({ jobId: job.id, sessionKey, model: jobModel }, 'Cron job model invalid; using agent default');\n }\n } else {\n await this.agentService.sessionConfig.applyCronJobModelOverride(sessionKey, undefined);\n }\n\n const response = await this.agentService.turnDispatcher.processDirect(message, sessionKey);\n\n const model = this.agentService.getModelForSession(sessionKey);\n\n log.info(\n { jobId: job.id, sessionKey, responseLength: response.length, model },\n 'Agent execution completed'\n );\n\n // Handle delivery (`local` channel or `mode: none` = no outbound; transcript in SessionStore)\n const delivery = job.delivery;\n const outboundChannel = delivery?.channel;\n const shouldPublish =\n delivery &&\n delivery.mode !== 'none' &&\n delivery.to;\n\n if (shouldPublish) {\n if (shouldSilence(response, DEFAULT_ACK_MAX_CHARS) || response.trim() === NO_REPLY) {\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n }\n const { stripped } = stripHeartbeatToken(response);\n const outboundText = stripped || response.trim();\n\n if (delivery.mode === 'webhook') {\n await postCronWebhook(delivery.to, {\n jobId: job.id,\n jobName: job.name,\n payloadKind: job.payload.kind,\n sessionKey,\n model,\n text: outboundText,\n });\n log.info({ jobId: job.id }, 'Delivered agent response webhook');\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n }\n if (outboundChannel === 'local') {\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n }\n const targetChannel = outboundChannel || 'cli';\n const outbound = await this.buildCronOutboundMessage(targetChannel, delivery.to, outboundText);\n\n await this.messageBus.publishOutbound(outbound);\n\n log.info(\n { jobId: job.id, channel: targetChannel, to: outbound.chat_id },\n 'Delivered agent response'\n );\n\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n }\n\n // No outbound delivery: transcript is in SessionStore under `sessionKey`.\n return {\n status: 'ok' as const,\n summary: response.slice(0, 200),\n sessionId: sessionKey,\n sessionKey,\n sessionType: 'cron',\n model,\n };\n })();\n\n // Race against timeout\n try {\n return await Promise.race([executePromise, timeoutPromise]);\n } finally {\n const { retireSessionMcpRuntimeForSessionKey } = await import('../agent/mcp/bundle-mcp-tools.js');\n await retireSessionMcpRuntimeForSessionKey({\n sessionKey,\n reason: 'cron-isolated-end',\n }).catch(() => {});\n }\n }\n\n /**\n * Basic execution without agent service (fallback)\n */\n private async basicExecute(\n job: JobData,\n signal: AbortSignal,\n timeout: number\n ): Promise<CronRunOutcome> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error(`Job timed out after ${timeout}ms`));\n }, timeout);\n\n // Listen for abort signal\n const abortHandler = () => {\n clearTimeout(timeoutId);\n reject(new Error('Job was aborted'));\n };\n signal.addEventListener('abort', abortHandler);\n\n // Simulate basic work\n setTimeout(() => {\n clearTimeout(timeoutId);\n signal.removeEventListener('abort', abortHandler);\n\n if (signal.aborted) {\n reject(new Error('Job was aborted'));\n } else {\n resolve({\n status: 'ok',\n summary: `Executed: ${getCronPayloadText(job).slice(0, 100)}`,\n });\n }\n }, 100);\n });\n }\n\n /**\n * Cancel a running job\n */\n cancelJob(jobId: string): boolean {\n const controller = this.runningJobs.get(jobId);\n if (controller) {\n controller.abort();\n this.runningJobs.delete(jobId);\n return true;\n }\n return false;\n }\n\n /**\n * Get execution history for a job\n */\n getHistory(jobId: string, limit = 10): JobExecution[] {\n const history = this.history.get(jobId) || [];\n return history.slice(-limit);\n }\n\n /**\n * Get currently running executions\n */\n getRunningExecutions(): JobExecution[] {\n const result: JobExecution[] = [];\n for (const [jobId] of this.runningJobs) {\n const history = this.history.get(jobId);\n if (history) {\n const running = history.find((e) => e.status === 'running');\n if (running) result.push(running);\n }\n }\n return result;\n }\n\n /**\n * Check if a job is currently running\n */\n isRunning(jobId: string): boolean {\n return this.runningJobs.has(jobId);\n }\n\n /**\n * Get consecutive error count for a job\n */\n getConsecutiveErrors(jobId: string): number {\n const history = this.history.get(jobId) || [];\n // Find last execution\n for (let i = history.length - 1; i >= 0; i--) {\n if (history[i].status !== 'running') {\n if (history[i].status === 'failed' || history[i].status === 'cancelled') {\n // Count consecutive errors before this\n let count = 0;\n for (let j = i - 1; j >= 0; j--) {\n if (history[j].status === 'failed' || history[j].status === 'cancelled') {\n count++;\n } else {\n break;\n }\n }\n return count + 1;\n }\n return 0;\n }\n }\n return 0;\n }\n\n /**\n * Calculate backoff delay for a job\n */\n calculateBackoff(jobId: string): number {\n const errors = this.getConsecutiveErrors(jobId);\n return errorBackoffMs(errors);\n }\n\n /**\n * Clear old history entries\n */\n cleanupHistory(maxAgeDays = 7): void {\n const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;\n\n for (const [jobId, executions] of this.history) {\n this.history.set(\n jobId,\n executions.filter((e) => new Date(e.startedAt).getTime() > cutoff)\n );\n }\n }\n\n private addToHistory(jobId: string, execution: JobExecution): void {\n const existing = this.history.get(jobId) || [];\n existing.push(execution);\n // Keep last 100 executions per job\n if (existing.length > 100) {\n existing.shift();\n }\n this.history.set(jobId, existing);\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  export { CronService } from './service.js';
2
- export { CronPersistence } from './persistence.js';
3
2
  export { CronRunLogStore } from './run-log-store.js';
4
3
  export { DefaultJobExecutor } from './executor.js';
5
4
  export { AddJobRequestSchema, UpdateJobRequestSchema, JobDataSchema } from './validation.js';
6
5
  export * from './types.js';
7
6
  export { getCronPayloadText } from './job-content.js';
7
+ export { describeSchedule } from './schedule.js';
@@ -1,7 +1,7 @@
1
+ import { AddJobRequestSchema, JobDataSchema, UpdateJobRequestSchema } from "./validation.js";
2
+ import { describeSchedule } from "./schedule.js";
1
3
  import { getCronPayloadText } from "./job-content.js";
2
4
  import { DefaultJobExecutor } from "./executor.js";
3
- import { AddJobRequestSchema, JobDataSchema, UpdateJobRequestSchema } from "./validation.js";
4
- import { CronPersistence } from "./persistence.js";
5
5
  import { CronRunLogStore } from "./run-log-store.js";
6
6
  import { CronService } from "./service.js";
7
- export { AddJobRequestSchema, CronPersistence, CronRunLogStore, CronService, DefaultJobExecutor, JobDataSchema, UpdateJobRequestSchema, getCronPayloadText };
7
+ export { AddJobRequestSchema, CronRunLogStore, CronService, DefaultJobExecutor, JobDataSchema, UpdateJobRequestSchema, describeSchedule, getCronPayloadText };
@@ -4,6 +4,7 @@ export declare class CronRunLogStore {
4
4
  /** @param dbPath Optional SQLite path for tests (opens a dedicated DB). */
5
5
  constructor(dbPath?: string);
6
6
  private requireDatabase;
7
+ upsert(execution: JobExecution): Promise<void>;
7
8
  appendCompleted(execution: JobExecution): Promise<void>;
8
9
  readJobHistory(jobId: string, limit: number): Promise<JobExecution[]>;
9
10
  readAllRuns(limit: number, jobNames: Map<string, string | undefined>): Promise<CronRunHistoryRow[]>;
@@ -15,8 +15,7 @@ var CronRunLogStore = class {
15
15
  requireDatabase() {
16
16
  requireXopcDatabase(this.dbPath ? { path: this.dbPath } : void 0);
17
17
  }
18
- async appendCompleted(execution) {
19
- if (execution.status === "running") return;
18
+ async upsert(execution) {
20
19
  try {
21
20
  this.requireDatabase();
22
21
  appendCronRun(execution);
@@ -27,6 +26,9 @@ var CronRunLogStore = class {
27
26
  }, "Failed to persist cron run");
28
27
  }
29
28
  }
29
+ async appendCompleted(execution) {
30
+ await this.upsert(execution);
31
+ }
30
32
  async readJobHistory(jobId, limit) {
31
33
  this.requireDatabase();
32
34
  return readCronJobHistory(jobId, limit);
@@ -1 +1 @@
1
- {"version":3,"file":"run-log-store.js","names":[],"sources":["../../../src/cron/run-log-store.ts"],"sourcesContent":["// Durable cron run history in SQLite (`cron_runs` table).\nimport { createLogger } from '../utils/logger.js';\nimport {\n appendCronRun,\n deleteCronRunsForJob,\n requireXopcDatabase,\n readAllCronRuns,\n readCronJobHistory,\n} from '../storage/sqlite/index.js';\nimport type { CronRunHistoryRow, JobExecution } from './types.js';\n\nconst log = createLogger('CronRunLog');\n\nexport class CronRunLogStore {\n private readonly dbPath?: string;\n\n /** @param dbPath Optional SQLite path for tests (opens a dedicated DB). */\n constructor(dbPath?: string) {\n this.dbPath = dbPath;\n }\n\n private requireDatabase(): void {\n requireXopcDatabase(this.dbPath ? { path: this.dbPath } : undefined);\n }\n\n async appendCompleted(execution: JobExecution): Promise<void> {\n if (execution.status === 'running') {\n return;\n }\n try {\n this.requireDatabase();\n appendCronRun(execution);\n } catch (err) {\n log.error({ jobId: execution.jobId, err: err as Error }, 'Failed to persist cron run');\n }\n }\n\n async readJobHistory(jobId: string, limit: number): Promise<JobExecution[]> {\n this.requireDatabase();\n return readCronJobHistory(jobId, limit);\n }\n\n async readAllRuns(limit: number, jobNames: Map<string, string | undefined>): Promise<CronRunHistoryRow[]> {\n this.requireDatabase();\n const rows = readAllCronRuns(limit);\n return rows.map((execution) => ({\n ...execution,\n jobName: jobNames.get(execution.jobId),\n }));\n }\n\n async deleteJobRuns(jobId: string): Promise<void> {\n try {\n this.requireDatabase();\n deleteCronRunsForJob(jobId);\n } catch (err) {\n log.warn({ jobId, err: err as Error }, 'Failed to delete cron runs');\n }\n }\n}\n"],"mappings":";;;;;;aACkD;AAUlD,MAAM,MAAM,aAAa,aAAa;AAEtC,IAAa,kBAAb,MAA6B;CAC3B;;CAGA,YAAY,QAAiB;AAC3B,OAAK,SAAS;;CAGhB,kBAAgC;AAC9B,sBAAoB,KAAK,SAAS,EAAE,MAAM,KAAK,QAAQ,GAAG,KAAA,EAAU;;CAGtE,MAAM,gBAAgB,WAAwC;AAC5D,MAAI,UAAU,WAAW,UACvB;AAEF,MAAI;AACF,QAAK,iBAAiB;AACtB,iBAAc,UAAU;WACjB,KAAK;AACZ,OAAI,MAAM;IAAE,OAAO,UAAU;IAAY;IAAc,EAAE,6BAA6B;;;CAI1F,MAAM,eAAe,OAAe,OAAwC;AAC1E,OAAK,iBAAiB;AACtB,SAAO,mBAAmB,OAAO,MAAM;;CAGzC,MAAM,YAAY,OAAe,UAAyE;AACxG,OAAK,iBAAiB;AAEtB,SADa,gBAAgB,MAClB,CAAC,KAAK,eAAe;GAC9B,GAAG;GACH,SAAS,SAAS,IAAI,UAAU,MAAM;GACvC,EAAE;;CAGL,MAAM,cAAc,OAA8B;AAChD,MAAI;AACF,QAAK,iBAAiB;AACtB,wBAAqB,MAAM;WACpB,KAAK;AACZ,OAAI,KAAK;IAAE;IAAY;IAAc,EAAE,6BAA6B"}
1
+ {"version":3,"file":"run-log-store.js","names":[],"sources":["../../../src/cron/run-log-store.ts"],"sourcesContent":["// Durable cron run history in SQLite (`cron_runs` table).\nimport { createLogger } from '../utils/logger.js';\nimport {\n appendCronRun,\n deleteCronRunsForJob,\n requireXopcDatabase,\n readAllCronRuns,\n readCronJobHistory,\n} from '../storage/sqlite/index.js';\nimport type { CronRunHistoryRow, JobExecution } from './types.js';\n\nconst log = createLogger('CronRunLog');\n\nexport class CronRunLogStore {\n private readonly dbPath?: string;\n\n /** @param dbPath Optional SQLite path for tests (opens a dedicated DB). */\n constructor(dbPath?: string) {\n this.dbPath = dbPath;\n }\n\n private requireDatabase(): void {\n requireXopcDatabase(this.dbPath ? { path: this.dbPath } : undefined);\n }\n\n async upsert(execution: JobExecution): Promise<void> {\n try {\n this.requireDatabase();\n appendCronRun(execution);\n } catch (err) {\n log.error({ jobId: execution.jobId, err: err as Error }, 'Failed to persist cron run');\n }\n }\n\n async appendCompleted(execution: JobExecution): Promise<void> {\n await this.upsert(execution);\n }\n\n async readJobHistory(jobId: string, limit: number): Promise<JobExecution[]> {\n this.requireDatabase();\n return readCronJobHistory(jobId, limit);\n }\n\n async readAllRuns(limit: number, jobNames: Map<string, string | undefined>): Promise<CronRunHistoryRow[]> {\n this.requireDatabase();\n const rows = readAllCronRuns(limit);\n return rows.map((execution) => ({\n ...execution,\n jobName: jobNames.get(execution.jobId),\n }));\n }\n\n async deleteJobRuns(jobId: string): Promise<void> {\n try {\n this.requireDatabase();\n deleteCronRunsForJob(jobId);\n } catch (err) {\n log.warn({ jobId, err: err as Error }, 'Failed to delete cron runs');\n }\n }\n}\n"],"mappings":";;;;;;aACkD;AAUlD,MAAM,MAAM,aAAa,aAAa;AAEtC,IAAa,kBAAb,MAA6B;CAC3B;;CAGA,YAAY,QAAiB;AAC3B,OAAK,SAAS;;CAGhB,kBAAgC;AAC9B,sBAAoB,KAAK,SAAS,EAAE,MAAM,KAAK,QAAQ,GAAG,KAAA,EAAU;;CAGtE,MAAM,OAAO,WAAwC;AACnD,MAAI;AACF,QAAK,iBAAiB;AACtB,iBAAc,UAAU;WACjB,KAAK;AACZ,OAAI,MAAM;IAAE,OAAO,UAAU;IAAY;IAAc,EAAE,6BAA6B;;;CAI1F,MAAM,gBAAgB,WAAwC;AAC5D,QAAM,KAAK,OAAO,UAAU;;CAG9B,MAAM,eAAe,OAAe,OAAwC;AAC1E,OAAK,iBAAiB;AACtB,SAAO,mBAAmB,OAAO,MAAM;;CAGzC,MAAM,YAAY,OAAe,UAAyE;AACxG,OAAK,iBAAiB;AAEtB,SADa,gBAAgB,MAClB,CAAC,KAAK,eAAe;GAC9B,GAAG;GACH,SAAS,SAAS,IAAI,UAAU,MAAM;GACvC,EAAE;;CAGL,MAAM,cAAc,OAA8B;AAChD,MAAI;AACF,QAAK,iBAAiB;AACtB,wBAAqB,MAAM;WACpB,KAAK;AACZ,OAAI,KAAK;IAAE;IAAY;IAAc,EAAE,6BAA6B"}
@@ -0,0 +1,10 @@
1
+ import type { CronSchedule, JobData } from './types.js';
2
+ export declare const DEFAULT_TOP_OF_HOUR_STAGGER_MS: number;
3
+ export declare function normalizeCronStaggerMs(value: unknown): number | undefined;
4
+ export declare function isRecurringTopOfHourCronExpr(expr: string): boolean;
5
+ export declare function resolveDefaultCronStaggerMs(expr: string): number | undefined;
6
+ export declare function resolveCronStaggerMs(schedule: CronSchedule): number;
7
+ export declare function computeNextRunAtMs(job: Pick<JobData, 'id' | 'enabled' | 'schedule'>, nowMs?: number): number | undefined;
8
+ export declare function computePreviousRunAtMs(job: Pick<JobData, 'id' | 'schedule'>, nowMs?: number): number | undefined;
9
+ export declare function describeSchedule(schedule: CronSchedule): string;
10
+ export declare function timerDelayUntil(targetMs: number, nowMs?: number): number;