@gaodefa/daocore 2026.5.29 → 2026.5.30
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.
- package/dist/abort-DI3P6TIb.js +277 -0
- package/dist/abort.runtime-B6Z7lDEa.js +2 -0
- package/dist/abort.runtime.js +1 -1
- package/dist/account-inspect-BFz7X0TX.js +173 -0
- package/dist/accounts-BoYYrY7x.js +107 -0
- package/dist/accounts-Busq29zW.js +107 -0
- package/dist/accounts-CuvH2tuN.js +119 -0
- package/dist/accounts-DkIcqvf7.js +2 -0
- package/dist/acp-runtime-BA8lzXzf.js +26 -0
- package/dist/acp-spawn-DPz2aX3A.js +2 -0
- package/dist/acp-spawn-DvVCsWYV.js +1275 -0
- package/dist/acp-stateful-target-driver-DcC76aZW.js +89 -0
- package/dist/action-kill-D-dVisIX.js +33 -0
- package/dist/action-runtime-BEiqwxDD.js +469 -0
- package/dist/action-runtime-api-1LzcGjcX.js +2 -0
- package/dist/action-send-c3CnDuPg.js +39 -0
- package/dist/action-spawn-Dj4kwpWs.js +47 -0
- package/dist/actions-qLccmQBc.js +161 -0
- package/dist/actions.runtime-B5vYAgNi.js +5 -0
- package/dist/agent-command-D6Ds198G.js +1367 -0
- package/dist/agent-components.runtime-xquK0B3L.js +10 -0
- package/dist/agent-components.runtime.js +1 -1
- package/dist/agent-harness-runtime-Dq1fCBOM.js +180 -0
- package/dist/agent-harness-task-runtime-DvSEqFkD.js +140 -0
- package/dist/agent-lm5ZYOFy.js +3 -0
- package/dist/agent-runner-execution-Dbh2pDhQ.js +1713 -0
- package/dist/agent-runner-utils-Di3r2T1T.js +266 -0
- package/dist/agent-runner.runtime-BW90EcGW.js +3455 -0
- package/dist/agent-runner.runtime.js +1 -1
- package/dist/agent-runtime-BgIT5Ytc.js +229 -0
- package/dist/agent-via-gateway-DMMS8-hK.js +463 -0
- package/dist/agent-ylolD8-V.js +2 -0
- package/dist/api-BB4vXLtW.js +2 -0
- package/dist/api-CUhM3KNB.js +134 -0
- package/dist/api-CewRHSbT.js +6 -0
- package/dist/api-CfKjNJlW.js +2 -0
- package/dist/api-Rsnqui1u.js +3 -0
- package/dist/api-k7AvxwHr.js +639 -0
- package/dist/apply-DQ4TQNMA.js +54 -0
- package/dist/apply-S_75Py4Y.js +41 -0
- package/dist/approval-handler.runtime-Cthxrstu.js +130 -0
- package/dist/assistant-Cp9pGaBw.js +291 -0
- package/dist/attachment-normalize-5R0bLLOj.js +225 -0
- package/dist/attempt-execution-DuyyZYsU.js +558 -0
- package/dist/attempt-execution.runtime-DF2GPgNh.js +3 -0
- package/dist/attempt-execution.runtime.js +1 -1
- package/dist/attempt-execution.shared-CwdukA3t.js +38 -0
- package/dist/attempt.prompt-helpers-CUqKtIa5.js +475 -0
- package/dist/attempt.tool-run-context-BbN4_BYO.js +2094 -0
- package/dist/binding-routing-C6Ex2Blu.js +113 -0
- package/dist/binding-targets-BqREncSL.js +121 -0
- package/dist/bot-DGMEkaIJ.js +7894 -0
- package/dist/bot-deps-BltYjeMX.js +2 -0
- package/dist/bot-deps-XvGCor9f.js +747 -0
- package/dist/bot-message-context.runtime-kK-xvLV7.js +7 -0
- package/dist/bot-message-context.runtime.js +1 -1
- package/dist/bot-message-context.session.runtime-DpOIJUae.js +12 -0
- package/dist/bot-message-context.session.runtime.js +1 -1
- package/dist/bot-native-commands.delivery.runtime-D49p5Lc1.js +4 -0
- package/dist/bot-native-commands.delivery.runtime.js +1 -1
- package/dist/bot-native-commands.runtime-8XBdSpkQ.js +13 -0
- package/dist/bot-native-commands.runtime.js +1 -1
- package/dist/bridge-server-y_XGduPx.js +113 -0
- package/dist/browser-cli-B2jmmTT3.js +230 -0
- package/dist/browser-cli-actions-input-JQt5ZQcn.js +473 -0
- package/dist/browser-cli-actions-observe-CrfKh6AX.js +81 -0
- package/dist/browser-cli-debug-DQACWlvC.js +137 -0
- package/dist/browser-cli-inspect-DDnxx0Xw.js +104 -0
- package/dist/browser-cli-lEuOnZzs.js +2 -0
- package/dist/browser-cli-manage-DogmwtYt.js +443 -0
- package/dist/browser-cli-resize-BHuV71VZ.js +26 -0
- package/dist/browser-cli-shared-CidShgoF.js +50 -0
- package/dist/browser-cli-state-i8cCGbiQ.js +337 -0
- package/dist/browser-control-auth-CKfXdXWj.js +2 -0
- package/dist/browser-profiles-ATBTvUGh.js +2 -0
- package/dist/browser-runtime-KmCT6FuL.js +384 -0
- package/dist/build-DOOT6f62.js +257 -0
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.js +2 -2
- package/dist/bundled/session-memory/handler.js +1 -1
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/capability-cli-DwvrwB-g.js +1782 -0
- package/dist/channel-B-wgK1bK.js +481 -0
- package/dist/channel-B1e_k1P0.js +867 -0
- package/dist/channel-BRDfa5tc.js +1777 -0
- package/dist/channel-BVEO1O4p.js +376 -0
- package/dist/channel-BYlO5l1d.js +508 -0
- package/dist/channel-Bl0sPoNx.js +653 -0
- package/dist/channel-C84xtDBy.js +1134 -0
- package/dist/channel-CLgKq4Ds.js +1556 -0
- package/dist/channel-CZT0qxLh.js +1249 -0
- package/dist/channel-CffBHWkb.js +955 -0
- package/dist/channel-D34ZJvVB.js +2126 -0
- package/dist/channel-DcdXLo_W.js +238 -0
- package/dist/channel-DnK5TrXV.js +362 -0
- package/dist/channel-Dzj4RHY2.js +740 -0
- package/dist/channel-OhZzLZCj.js +562 -0
- package/dist/channel-actions.runtime-Cdvhqc9s.js +265 -0
- package/dist/channel-actions.runtime.js +1 -1
- package/dist/channel-core-Dz9I_R4j.js +5 -0
- package/dist/channel-inbound-COGoV_aS.js +80 -0
- package/dist/channel-mP6HCIMr.js +1496 -0
- package/dist/channel-plugin-runtime-C0R46KeG.js +998 -0
- package/dist/channel-quGdgWel.js +808 -0
- package/dist/channel-runtime-FvPQoIF9.js +408 -0
- package/dist/channel.runtime-2vUz3gij.js +109 -0
- package/dist/channel.runtime-BZLDSNuQ.js +254 -0
- package/dist/channel.runtime-Bo5jO0Hj.js +4 -0
- package/dist/channel.runtime-CDgvrjM2.js +652 -0
- package/dist/channel.runtime-COf8SDFn.js +88 -0
- package/dist/channel.runtime-C_C1f8lu.js +733 -0
- package/dist/channel.runtime-CqPbsg8t.js +2528 -0
- package/dist/channel.runtime-DtV85Khx.js +1008 -0
- package/dist/channel.runtime-Spt1ukGW.js +21009 -0
- package/dist/channel.setup-Bc0LUQS1.js +1098 -0
- package/dist/channel.setup-ChTkj2hq.js +343 -0
- package/dist/channel.setup-CpMD9F8i.js +10 -0
- package/dist/chat-mUsjD5YN.js +2666 -0
- package/dist/chrome-u314zcbM.js +1503 -0
- package/dist/cli/run-main.js +5 -5
- package/dist/cli-CJMoqXrc.js +1341 -0
- package/dist/cli-compaction-qGYB7suI.js +347 -0
- package/dist/cli-metadata-BBJFpNU9.js +22 -0
- package/dist/cli-runner-C0kcts0I.js +540 -0
- package/dist/cli-runner-CTm8K6s8.js +2 -0
- package/dist/cli-runner.runtime-C8deL5FL.js +3 -0
- package/dist/cli-runner.runtime-DDAg1D0L.js +4 -0
- package/dist/cli-runner.runtime.js +1 -1
- package/dist/cli-startup-metadata.json +8 -8
- package/dist/client-CPxpgs4m.js +650 -0
- package/dist/client-adapter-CLlqOqfu.js +897 -0
- package/dist/client-factory-R7Rr2jwn.js +9 -0
- package/dist/command-auth-CkQP1buP.js +135 -0
- package/dist/command-handlers-CJEjFyMk.js +1609 -0
- package/dist/command-registry-BDgyOqDw.js +4 -0
- package/dist/command-registry-BVi5Jn1S.js +9 -0
- package/dist/command-registry-core-JCfsVRtz.js +110 -0
- package/dist/command-status.runtime-CM1JLzi6.js +90 -0
- package/dist/command-status.runtime.js +1 -1
- package/dist/commands-acp-C1-I78U6.js +74 -0
- package/dist/commands-compact.runtime-DOsNavwp.js +10 -0
- package/dist/commands-compact.runtime.js +1 -1
- package/dist/commands-handlers.runtime-BD1n2WGw.js +6154 -0
- package/dist/commands-handlers.runtime.js +1 -1
- package/dist/commands-status-DU_UTQz1.js +16 -0
- package/dist/commands-status-DejfTakp.js +3 -0
- package/dist/commands-status.runtime-DejfTakp.js +3 -0
- package/dist/commands-status.runtime.js +1 -1
- package/dist/commands-subagents-control.runtime-D7vo_2Y_.js +3 -0
- package/dist/commands-subagents-control.runtime-DGybTjJ6.js +2 -0
- package/dist/commands-subagents-control.runtime.js +1 -1
- package/dist/commands-system-prompt-BEBXAMea.js +2 -0
- package/dist/commands-system-prompt-gj3oQpQO.js +162 -0
- package/dist/commands.runtime-CgXX70IN.js +176 -0
- package/dist/commands.runtime.js +1 -1
- package/dist/commitments/runtime.js +1 -1
- package/dist/compact-C41XxMDt.js +480 -0
- package/dist/compact-DUVAF9EC.js +1141 -0
- package/dist/compact.runtime-COMRg6s3.js +12 -0
- package/dist/compact.runtime.js +1 -1
- package/dist/completion-cli-CmEYoB9a.js +315 -0
- package/dist/computer-use-BwJHu1QF.js +367 -0
- package/dist/config-ATBTvUGh.js +2 -0
- package/dist/config-DlNgjF8W.js +373 -0
- package/dist/config-mutations-tl_OqI6d.js +159 -0
- package/dist/context-engine-host-compat-BNPAIK9F.js +288 -0
- package/dist/context-engine-host-compat-C69CK7zS.js +2 -0
- package/dist/context-engine-lifecycle-iQ8f5a_-.js +1274 -0
- package/dist/control-auth-vVxyGKxY.js +114 -0
- package/dist/control-service-73nj79EW.js +145 -0
- package/dist/control-ui/assets/agents-DdWIz8Vk.js +1008 -0
- package/dist/control-ui/assets/channel-config-extras-C7ELhuDi.js +2 -0
- package/dist/control-ui/assets/channels-Bsoj-9BS.js +367 -0
- package/dist/control-ui/assets/cron-BRTEw1tT.js +1013 -0
- package/dist/control-ui/assets/debug-EKl_F8_L.js +97 -0
- package/dist/control-ui/assets/index-DSbSO7ys.js +7370 -0
- package/dist/control-ui/assets/instances-B1AoYwI5.js +57 -0
- package/dist/control-ui/assets/logs-BChOznzh.js +74 -0
- package/dist/control-ui/assets/nodes-5yebKh9k.js +436 -0
- package/dist/control-ui/assets/sessions-BlDYNThc.js +399 -0
- package/dist/control-ui/assets/skills-shared-CNFnf7KU.js +11 -0
- package/dist/control-ui/assets/skills-uxTDRmLx.js +314 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/control-ui/sw.js +1 -1
- package/dist/conversation-binding-runtime-CmoTKcY1.js +4 -0
- package/dist/conversation-runtime-BxOHxuin.js +31 -0
- package/dist/core-D0Y2H55f.js +282 -0
- package/dist/core-api-BKlJUtDt.js +5 -0
- package/dist/core-api-_fiftUHn.js +2 -0
- package/dist/crestodian/crestodian.js +1 -1
- package/dist/crestodian/rescue-message.js +1 -1
- package/dist/crestodian-BJifoQiR.js +55 -0
- package/dist/daocore-tools-BMdF8RwK.js +11727 -0
- package/dist/delivery-e2ci_bcX.js +1002 -0
- package/dist/dialogue-BXayGhnz.js +37 -0
- package/dist/dir-fetch-tool-Cara5mZh.js +565 -0
- package/dist/dir-list-tool-CQ8Bo5Ez.js +100 -0
- package/dist/direct-dm-CPk6Qm9D.js +64 -0
- package/dist/directive-handling.fast-lane-BQ9aXqk8.js +68 -0
- package/dist/directive-handling.impl--a5AGurq.js +818 -0
- package/dist/directive-handling.impl-C4JqMwle.js +2 -0
- package/dist/directive-handling.model-selection-pyTYMIXn.js +122 -0
- package/dist/directive-handling.persist.runtime-B7vWT1r9.js +263 -0
- package/dist/directive-handling.persist.runtime.js +1 -1
- package/dist/dispatch-3Ru_eGq1.js +1640 -0
- package/dist/dispatch-acp-transcript.runtime-CYMP-5oE.js +40 -0
- package/dist/dispatch-acp-transcript.runtime.js +1 -1
- package/dist/dispatch-acp.runtime-BVCiklXk.js +18 -0
- package/dist/dispatch-acp.runtime.js +1 -1
- package/dist/doctor-B8mR3Sgr.js +2 -0
- package/dist/doctor-BJ6DuerV.js +6 -0
- package/dist/doctor-config-flow-BdOGs_zn.js +1741 -0
- package/dist/doctor-core-checks-CDf0stz-.js +2 -0
- package/dist/doctor-core-checks-Cpdnu2XI.js +573 -0
- package/dist/doctor-health-0qv_acBu.js +65 -0
- package/dist/doctor-health-contributions-Db1FQgFw.js +696 -0
- package/dist/doctor-lint-C1g4Llgl.js +94 -0
- package/dist/doctor-state-integrity-C8iXFLJ7.js +1231 -0
- package/dist/doctor-update-CsPu6p3d.js +58 -0
- package/dist/dynamic-tools-YQ0z0YKH.js +486 -0
- package/dist/embedded-backend-S0cahIZw.js +579 -0
- package/dist/embedded-gateway-stub.runtime-Cjw12ALm.js +12 -0
- package/dist/embedded-gateway-stub.runtime.js +1 -1
- package/dist/exec-approvals-ArHHjoE-.js +149 -0
- package/dist/extensionAPI.js +1 -1
- package/dist/extensions/active-memory/index.js +1 -1
- package/dist/extensions/admin-http-rpc/index.js +1 -1
- package/dist/extensions/browser/browser-bridge.js +1 -1
- package/dist/extensions/browser/browser-config.js +4 -4
- package/dist/extensions/browser/browser-control-auth.js +2 -2
- package/dist/extensions/browser/browser-doctor.js +2 -2
- package/dist/extensions/browser/browser-maintenance.js +1 -1
- package/dist/extensions/browser/browser-profiles.js +2 -2
- package/dist/extensions/browser/browser-runtime-api.js +11 -11
- package/dist/extensions/browser/cli-metadata.js +1 -1
- package/dist/extensions/browser/index.js +1 -1
- package/dist/extensions/browser/plugin-registration.js +1 -1
- package/dist/extensions/browser/register.runtime.js +4 -4
- package/dist/extensions/browser/runtime-api.js +13 -13
- package/dist/extensions/canvas/index.js +1 -1
- package/dist/extensions/clickclack/api.js +2 -2
- package/dist/extensions/clickclack/channel-plugin-api.js +1 -1
- package/dist/extensions/clickclack/runtime-api.js +2 -2
- package/dist/extensions/device-pair/api.js +1 -1
- package/dist/extensions/device-pair/pair-command-approve.js +1 -1
- package/dist/extensions/file-transfer/index.js +4 -4
- package/dist/extensions/imessage/api.js +2 -2
- package/dist/extensions/imessage/channel-plugin-api.js +1 -1
- package/dist/extensions/imessage/message-tool-api.d.ts +1 -1
- package/dist/extensions/imessage/runtime-api.js +3 -3
- package/dist/extensions/irc/api.js +2 -2
- package/dist/extensions/irc/channel-plugin-api.js +1 -1
- package/dist/extensions/llm-task/index.js +1 -1
- package/dist/extensions/mattermost/api.js +1 -1
- package/dist/extensions/mattermost/channel-plugin-api.js +1 -1
- package/dist/extensions/mattermost/channel-plugin-runtime.js +1 -1
- package/dist/extensions/mattermost/policy-api.js +1 -1
- package/dist/extensions/mattermost/runtime-api.js +2 -2
- package/dist/extensions/mattermost/slash-route-api.js +1 -1
- package/dist/extensions/memory-core/cli-metadata.js +1 -1
- package/dist/extensions/migrate-claude/apply.js +1 -1
- package/dist/extensions/migrate-claude/index.js +1 -1
- package/dist/extensions/migrate-claude/plan.js +1 -1
- package/dist/extensions/migrate-claude/provider.js +1 -1
- package/dist/extensions/migrate-claude/targets.js +1 -1
- package/dist/extensions/migrate-hermes/apply.js +1 -1
- package/dist/extensions/migrate-hermes/index.js +1 -1
- package/dist/extensions/migrate-hermes/model.js +1 -1
- package/dist/extensions/migrate-hermes/plan.js +1 -1
- package/dist/extensions/migrate-hermes/provider.js +1 -1
- package/dist/extensions/migrate-hermes/secrets.js +1 -1
- package/dist/extensions/migrate-hermes/targets.js +1 -1
- package/dist/extensions/policy/api.js +1 -1
- package/dist/extensions/policy/index.js +2 -2
- package/dist/extensions/signal/api.js +6 -6
- package/dist/extensions/signal/channel-plugin-api.js +1 -1
- package/dist/extensions/signal/reaction-runtime-api.js +1 -1
- package/dist/extensions/signal/runtime-api.js +7 -7
- package/dist/extensions/skill-workshop/api.js +1 -1
- package/dist/extensions/skill-workshop/index.js +2 -2
- package/dist/extensions/telegram/account-inspect-api.js +1 -1
- package/dist/extensions/telegram/api.js +11 -11
- package/dist/extensions/telegram/channel-plugin-api.js +2 -2
- package/dist/extensions/telegram/contract-api.js +3 -3
- package/dist/extensions/telegram/runtime-api.js +7 -7
- package/dist/extensions/telegram/security-audit-contract-api.js +1 -1
- package/dist/extensions/telegram/setup-plugin-api.js +1 -1
- package/dist/extensions/telegram/test-api.js +2 -2
- package/dist/extensions/webhooks/api.js +1 -1
- package/dist/extensions/webhooks/index.js +1 -1
- package/dist/extensions/xai/index.js +4 -4
- package/dist/extensions/xai/realtime-transcription-provider.js +1 -1
- package/dist/extensions/xai/speech-provider.js +1 -1
- package/dist/extensions/xai/test-api.js +1 -1
- package/dist/extensions/xai/tts.js +1 -1
- package/dist/extensions/xai/web-search.js +1 -1
- package/dist/extensions/xai/xai-oauth.js +1 -1
- package/dist/file-fetch-tool-DQpAtjX4.js +124 -0
- package/dist/file-write-tool-CkB0oYwe.js +127 -0
- package/dist/format-DUJUpMFQ.js +1145 -0
- package/dist/gateway-cli-CQo_E4Ka.js +435 -0
- package/dist/gateway-method-runtime-1QPgVJH7.js +21 -0
- package/dist/get-reply-DfUcsC-O.js +4689 -0
- package/dist/get-reply-from-config.runtime-C95MG73O.js +2 -0
- package/dist/get-reply-from-config.runtime.js +1 -1
- package/dist/graph-users-DPJe0fPs.js +1419 -0
- package/dist/group-access-DefaNJ6L.js +112 -0
- package/dist/handle-action.guild-admin-C1sy1kGJ.js +288 -0
- package/dist/harness-BgPRdmNK.js +61 -0
- package/dist/health-DFR9A1YV.js +4 -0
- package/dist/heartbeat-runner-BiYVPs25.js +5 -0
- package/dist/heartbeat-runner.runtime-Bmaq8WUA.js +4 -0
- package/dist/heartbeat-runner.runtime.js +1 -1
- package/dist/hooks-CNJDuJUm.js +534 -0
- package/dist/inbound-direct-dm-runtime-DFt58p71.js +2 -0
- package/dist/inbound-reply-dispatch-kgSDM8ax.js +148 -0
- package/dist/index.js +1 -1
- package/dist/init-B0zO_U4r.js +59 -0
- package/dist/inline-buttons-B6zgQW3J.js +40 -0
- package/dist/internal-events-C3Q5t6hg.js +90 -0
- package/dist/isolated-agent-CaSU5GCJ.js +2 -0
- package/dist/isolated-agent-CnsD2TmP.js +1118 -0
- package/dist/lifecycle-i3WZDaIn.js +571 -0
- package/dist/list.probe-g0rUiVXW.js +449 -0
- package/dist/list.status-command-DG3PrmeO.js +789 -0
- package/dist/llm-slug-generator-CHlf69FU.js +78 -0
- package/dist/llm-slug-generator.js +1 -1
- package/dist/local-dispatch.runtime-CDeHUp_A.js +9 -0
- package/dist/local-dispatch.runtime.js +1 -1
- package/dist/manager.runtime-BlLMPUTq.js +2714 -0
- package/dist/manager.runtime.js +1 -1
- package/dist/markdown-to-line-r_mBHMdy.js +811 -0
- package/dist/mcp-http-C5UPIJGR.js +555 -0
- package/dist/mcp-http-CTy9gpfA.js +2 -0
- package/dist/media-understanding-provider-BT6TOuVa.js +339 -0
- package/dist/message-actions-BdcjjwFt.js +145 -0
- package/dist/message-handler-B29Tj2p2.js +1715 -0
- package/dist/message-handler-BhHb1FHI.js +384 -0
- package/dist/message-handler.preflight-ouZ_-3o8.js +1125 -0
- package/dist/message-handler.process-CrYiBx_F.js +1484 -0
- package/dist/model-CLbtocQX.js +74 -0
- package/dist/model-selection-BG_vU9Kl.js +272 -0
- package/dist/models-4KERn3ya.js +104 -0
- package/dist/models-BCa-ipxi.js +2 -0
- package/dist/models-cli-DtpGXEsf.js +256 -0
- package/dist/monitor-B-egsvmI.js +1657 -0
- package/dist/monitor-BJX9Yv7k.js +2 -0
- package/dist/monitor-BPrIy3pV.js +715 -0
- package/dist/monitor-BWknZZ7N.js +4377 -0
- package/dist/monitor-C3y_PtI1.js +60 -0
- package/dist/monitor-DCzfdqE4.js +1370 -0
- package/dist/monitor-P24epSwE.js +2788 -0
- package/dist/monitor-auth-CEG_xldE.js +179 -0
- package/dist/monitor-jEePJbbS.js +834 -0
- package/dist/monitor-polling.runtime-BzlHK11u.js +883 -0
- package/dist/monitor-polling.runtime.js +1 -1
- package/dist/monitor-webhook.runtime-vFEhk_Zj.js +387 -0
- package/dist/monitor-webhook.runtime.js +1 -1
- package/dist/monitor.account-BUIfgWXg.js +5233 -0
- package/dist/monitor.runtime-Cp7W4fhb.js +2 -0
- package/dist/monitor.runtime.js +1 -1
- package/dist/monitor.webhook-DzrbeR4T.js +180 -0
- package/dist/node-cli-sessions-CLbI14UA.js +1228 -0
- package/dist/openai-http-BM9BIA_g.js +824 -0
- package/dist/openresponses-http-CNoW9kk8.js +1173 -0
- package/dist/operations-DT4bsxV6.js +805 -0
- package/dist/outbound-adapter-CZRUOgY0.js +543 -0
- package/dist/outbound-session-route-DneMsGNx.js +45 -0
- package/dist/outbound.runtime-CHuoKJJI.js +2 -0
- package/dist/outbound.runtime.js +1 -1
- package/dist/pi-embedded-1-rsueO_.js +3796 -0
- package/dist/pi-embedded-BRfDncVO.js +4 -0
- package/dist/pi-embedded.runtime-DEYoa6bW.js +4 -0
- package/dist/pi-embedded.runtime.js +1 -1
- package/dist/pi-tools-C0P3NgP5.js +2413 -0
- package/dist/plan-BQUkjBMY.js +81 -0
- package/dist/plan-CRO9_2_k.js +112 -0
- package/dist/plugin-BJCmWhnh.js +12396 -0
- package/dist/plugin-app-cache-key-CXAg0F7A.js +46 -0
- package/dist/plugin-enabled-D93Z9LSo.js +233 -0
- package/dist/plugin-registration-BBrTcZnf.js +88 -0
- package/dist/plugin-sdk/.boundary-entry-shims.stamp +1 -1
- package/dist/plugin-sdk/acp-runtime-backend.js +1 -1
- package/dist/plugin-sdk/acp-runtime.js +2 -2
- package/dist/plugin-sdk/agent-harness-runtime.js +6 -6
- package/dist/plugin-sdk/agent-harness-task-runtime.js +1 -1
- package/dist/plugin-sdk/agent-harness.js +7 -7
- package/dist/plugin-sdk/agent-runtime.js +2 -2
- package/dist/plugin-sdk/channel-core.js +2 -2
- package/dist/plugin-sdk/channel-inbound.js +2 -2
- package/dist/plugin-sdk/channel-test-helpers.js +1 -1
- package/dist/plugin-sdk/command-auth.js +1 -1
- package/dist/plugin-sdk/command-status-runtime.js +1 -1
- package/dist/plugin-sdk/compat.js +1 -1
- package/dist/plugin-sdk/conversation-binding-runtime.js +2 -2
- package/dist/plugin-sdk/conversation-runtime.js +3 -3
- package/dist/plugin-sdk/core.js +2 -2
- package/dist/plugin-sdk/direct-dm.js +1 -1
- package/dist/plugin-sdk/gateway-method-runtime.js +1 -1
- package/dist/plugin-sdk/health.js +2 -2
- package/dist/plugin-sdk/inbound-reply-dispatch.js +1 -1
- package/dist/plugin-sdk/index.js +1 -1
- package/dist/plugin-sdk/mattermost.js +1 -1
- package/dist/plugin-sdk/plugin-test-contracts.js +2 -2
- package/dist/plugin-sdk/provider-test-contracts.js +4 -4
- package/dist/plugin-sdk/reply-runtime.js +4 -4
- package/dist/plugin-sdk/testing.js +2 -2
- package/dist/plugin-sdk/zalouser.js +1 -1
- package/dist/plugin-service-DnZEPBEA.js +1229 -0
- package/dist/plugins/runtime/index.js +4 -4
- package/dist/policy-CMv5QMPG.js +138 -0
- package/dist/policy-CeoP4t9J.js +680 -0
- package/dist/prepare.runtime-DZ55JiTt.js +732 -0
- package/dist/prepare.runtime.js +1 -1
- package/dist/preview-warnings-O08PZRxu.js +392 -0
- package/dist/probe-C6w3bP8X.js +682 -0
- package/dist/probe-Cs1_X1NA.js +47 -0
- package/dist/probe-Cuee_jyM.js +2204 -0
- package/dist/probe-DZxCnDzn.js +2 -0
- package/dist/program-C2dT8VHB.js +131 -0
- package/dist/provider-C2_vSzYh.js +32 -0
- package/dist/provider-DSfTDQbj.js +152 -0
- package/dist/provider-U8hrm9h-.js +32 -0
- package/dist/provider-_v910H8y.js +8735 -0
- package/dist/provider-dispatcher-C3kmmIEC.js +22 -0
- package/dist/provider-dispatcher.runtime.js +1 -1
- package/dist/provider-session.runtime-Mg0fx5hH.js +9 -0
- package/dist/provider-session.runtime.js +1 -1
- package/dist/provider.runtime-pSTXuHrq.js +2 -0
- package/dist/provider.runtime.js +1 -1
- package/dist/public-surface-loader-09t4TTue.js +114 -0
- package/dist/pw-ai-DjNqHV3_.js +3029 -0
- package/dist/pw-role-snapshot-DoGefdv7.js +333 -0
- package/dist/reaction-level-C8Mqxizo.js +19 -0
- package/dist/reaction-runtime-api-DOP6xOsm.js +116 -0
- package/dist/realtime-transcription-provider-BTGEXXna.js +205 -0
- package/dist/register-CbZPpqI3.js +2178 -0
- package/dist/register.agent-DrBFuPKh.js +156 -0
- package/dist/register.crestodian-DwTbmaK8.js +24 -0
- package/dist/register.maintenance-BIeoYBub.js +83 -0
- package/dist/register.runtime-B5V9_Dwz.js +54 -0
- package/dist/register.subclis-CJE9ISZY.js +31 -0
- package/dist/register.subclis-YQsKas8N.js +3 -0
- package/dist/register.subclis-core-CjwdGeGU.js +273 -0
- package/dist/repair-sequencing-C0jrL5pw.js +640 -0
- package/dist/reply-delivery-B28mTIpb.js +196 -0
- package/dist/reply-runtime-BqQX7HHw.js +11 -0
- package/dist/reply.runtime-C95MG73O.js +2 -0
- package/dist/reply.runtime.js +1 -1
- package/dist/request-BqoMvXxH.js +54 -0
- package/dist/resolve-allowlist-hbKKd_kq.js +220 -0
- package/dist/result-fallback-classifier-BrORPoxj.js +79 -0
- package/dist/route-HHRgSzVy.js +469 -0
- package/dist/route-resolution-C7bafjVc.js +274 -0
- package/dist/routes-86XGeo7a.js +2 -0
- package/dist/routes-BhVGr9ie.js +3602 -0
- package/dist/run-Bb5QP5JV.js +1162 -0
- package/dist/run-attempt-CLi0wV7j.js +7704 -0
- package/dist/run-command-Be6GAxiW.js +2 -0
- package/dist/run-command-Ds4hr3iU.js +23 -0
- package/dist/run-embedded.runtime-wX9ORojX.js +4 -0
- package/dist/run-embedded.runtime.js +1 -1
- package/dist/run-execution-cli.runtime-BmNZVB2N.js +4 -0
- package/dist/run-execution-cli.runtime.js +1 -1
- package/dist/run-executor.runtime.js +1 -1
- package/dist/run-subagent-registry.runtime-Uq5sjftu.js +2 -0
- package/dist/run-subagent-registry.runtime.js +1 -1
- package/dist/runtime-BktLQ75j.js +1287 -0
- package/dist/runtime-C9XdR62_.js +438 -0
- package/dist/runtime-CTvWDPx9.js +6179 -0
- package/dist/runtime-api-BojB3MUw.js +21 -0
- package/dist/runtime-api-C92MlAwp.js +13 -0
- package/dist/runtime-api-CFNZE9Xt.js +13 -0
- package/dist/runtime-api-Cimpgm5_.js +3 -0
- package/dist/runtime-api-D8qG_lUm.js +17 -0
- package/dist/runtime-api-DMDMnQEG.js +4 -0
- package/dist/runtime-api-DupQdIlE.js +24 -0
- package/dist/runtime-api.actions-B8i3elqu.js +3 -0
- package/dist/runtime-api.monitor-BntFzf_M.js +6 -0
- package/dist/runtime-api.send-CPlmi1jc.js +4 -0
- package/dist/runtime-api.threads-D-H_r_Nl.js +2 -0
- package/dist/runtime-channel-BGhTxnXZ.js +150 -0
- package/dist/runtime-channel-CPFxNapZ.js +2 -0
- package/dist/runtime-embedded-pi.runtime-BjnMTurB.js +2 -0
- package/dist/runtime-embedded-pi.runtime.js +1 -1
- package/dist/sanitize-outbound-BMUv1NeS.js +127 -0
- package/dist/sdk-setup-tools-Do3DkQi_.js +8 -0
- package/dist/secrets-B8YVPHhk.js +113 -0
- package/dist/security-audit-7Gg_nL4o.js +122 -0
- package/dist/security-audit-Bk3CREj-.js +118 -0
- package/dist/security-audit.runtime-BmSJrdeq.js +2 -0
- package/dist/security-audit.runtime.js +1 -1
- package/dist/selection-BFGde9-_.js +3 -0
- package/dist/selection-DrLFlbgG.js +16157 -0
- package/dist/send-4dLa-BQo.js +1631 -0
- package/dist/send-CV6gCwYP.js +192 -0
- package/dist/send-aGiK3Efu.js +143 -0
- package/dist/send-dB3CehAN.js +2 -0
- package/dist/send.components-BcG9BlkC.js +2 -0
- package/dist/send.components-rLjDlfYU.js +500 -0
- package/dist/send.runtime-D20k2MSr.js +2 -0
- package/dist/send.runtime.js +1 -1
- package/dist/server-BTRx_U1s.js +73 -0
- package/dist/server-CY0ymhBg.js +24 -0
- package/dist/server-close.runtime.js +1 -1
- package/dist/server-context-BxGT8wbK.js +955 -0
- package/dist/server-context-DYlbhAaT.js +2 -0
- package/dist/server-cron-BB_15lcP.js +2 -0
- package/dist/server-cron-CfyJX_a4.js +2989 -0
- package/dist/server-methods-Ck2ab6TC.js +16494 -0
- package/dist/server-node-events-BdRizF-5.js +596 -0
- package/dist/server-plugin-bootstrap-CPUr8NFE.js +70 -0
- package/dist/server-plugins-__5AxjzH.js +432 -0
- package/dist/server-reload-handlers-BMl9Fx9b.js +714 -0
- package/dist/server-restart-sentinel-D11M_ZsH.js +747 -0
- package/dist/server-restart-sentinel-DGptQ27c.js +2 -0
- package/dist/server-runtime-services-BwtLs-8I.js +2 -0
- package/dist/server-runtime-services-aJydixRu.js +267 -0
- package/dist/server-startup-plugins-m_P3gY4m.js +113 -0
- package/dist/server-startup-post-attach-DuXQ1AXL.js +716 -0
- package/dist/server-ws-runtime-mXmxB1W8.js +349 -0
- package/dist/server.impl-fuV8aKSP.js +2586 -0
- package/dist/service-a8cTFka4.js +1446 -0
- package/dist/session-binding-BIycxo9u.js +2 -0
- package/dist/session-binding-xHwQr9vM.js +219 -0
- package/dist/session-kill-http-DiUZhcoN.js +121 -0
- package/dist/session-reset-service-DkepDoob.js +625 -0
- package/dist/session-route-Ba5u7ecb.js +93 -0
- package/dist/session-status.runtime-Cb1X69Rl.js +2 -0
- package/dist/session-status.runtime.js +1 -1
- package/dist/session-subagent-reactivation.runtime-rbqmto4G.js +2 -0
- package/dist/session-subagent-reactivation.runtime.js +1 -1
- package/dist/session-tab-registry-DSKOoJta.js +521 -0
- package/dist/sessions-history-http-CWP88I_C.js +430 -0
- package/dist/sessions.runtime-1q1OwLn3.js +2 -0
- package/dist/sessions.runtime.js +1 -1
- package/dist/setup-api-DtrOOYeP.js +29 -0
- package/dist/setup-core-DMrJS6LP.js +174 -0
- package/dist/setup-surface-BFPNu1qA.js +405 -0
- package/dist/setup-surface-CedShqhO.js +288 -0
- package/dist/setup-surface-DLOBH1Vf.js +320 -0
- package/dist/setup-surface-MhCeEWSj.js +221 -0
- package/dist/shared-DamL-e8D.js +121 -0
- package/dist/shared-client-B48JoAsz.js +2 -0
- package/dist/shared-client-BoSiDnUo.js +629 -0
- package/dist/side-question-DQoQ_ETb.js +683 -0
- package/dist/skill-tool-dispatch.runtime-CWLloYA8.js +143 -0
- package/dist/skill-tool-dispatch.runtime.js +1 -1
- package/dist/slash-state-DrrWJQfv.js +2166 -0
- package/dist/speech-provider-8Hx1uw41.js +184 -0
- package/dist/src-BbUYOPwU.js +4256 -0
- package/dist/startup-context-opAJAamX.js +313 -0
- package/dist/status-subagents.runtime-DSo7ZK_Z.js +18 -0
- package/dist/status-subagents.runtime.js +1 -1
- package/dist/status-text-AyPuDzrO.js +296 -0
- package/dist/sticker-cache-91Tp8mLP.js +206 -0
- package/dist/sticker-vision.runtime-DjleFXi5.js +17 -0
- package/dist/sticker-vision.runtime.js +1 -1
- package/dist/subagent-announce-delivery-DS_2W9oZ.js +958 -0
- package/dist/subagent-announce-hatA_PZ2.js +354 -0
- package/dist/subagent-control-IyFIvc7T.js +508 -0
- package/dist/subagent-hooks-BCj5eB0Z.js +2 -0
- package/dist/subagent-hooks-BWjTBkox.js +146 -0
- package/dist/subagent-hooks-CdGLK9_3.js +230 -0
- package/dist/subagent-hooks-EH0tBkw9.js +2 -0
- package/dist/subagent-hooks-api-BhYaXQJV.js +23 -0
- package/dist/subagent-hooks-api-uGsV8Xn5.js +22 -0
- package/dist/subagent-hooks-api-wfJFKIPx.js +23 -0
- package/dist/subagent-hooks-l8Xd94dt.js +2 -0
- package/dist/subagent-hooks-m2eHUO_d.js +116 -0
- package/dist/subagent-orphan-recovery-Bq9zvX3L.js +352 -0
- package/dist/subagent-registry-Bcb3zy6b.js +3 -0
- package/dist/subagent-registry-CoZNTFx-.js +2351 -0
- package/dist/subagent-registry.runtime.js +1 -1
- package/dist/subagent-session-cleanup--00EjDLe.js +525 -0
- package/dist/subagent-spawn-ClJJkz3D.js +1164 -0
- package/dist/target-id-BsZLiF3x.js +107 -0
- package/dist/targets-CQMe4AZP.js +44 -0
- package/dist/targets-SmL1AU-H.js +19 -0
- package/dist/targets-Y-roiA47.js +19 -0
- package/dist/task-registry-control.runtime.js +1 -1
- package/dist/telegram/token.js +1 -1
- package/dist/testing-B5hNJDuF.js +267 -0
- package/dist/thread-bindings-BD3eE0kK.js +232 -0
- package/dist/thread-bindings-C9NNiv8K.js +571 -0
- package/dist/thread-bindings-Dl4PN1vZ.js +228 -0
- package/dist/thread-bindings-uK1LLAwy.js +8 -0
- package/dist/thread-bindings.discord-api-BHNEUbnk.js +187 -0
- package/dist/thread-bindings.manager-B0LXiH3F.js +2 -0
- package/dist/thread-bindings.manager-BTlH4XMG.js +536 -0
- package/dist/thread-lifecycle-DG8uyPHd.js +1614 -0
- package/dist/token-BbGCANnE.js +134 -0
- package/dist/tool-actions.runtime-oUUrD8HB.js +534 -0
- package/dist/tool-actions.runtime.js +1 -1
- package/dist/tool-bqg-_vYv.js +139 -0
- package/dist/tool-resolution-CSZsa3kP.js +149 -0
- package/dist/tools-effective-inventory-0CqFp-vq.js +204 -0
- package/dist/tools-invoke-http-DhYHY0Yh.js +67 -0
- package/dist/tools-invoke-shared-CbVJJUj7.js +200 -0
- package/dist/tts-C2qD-ad6.js +66 -0
- package/dist/tui-CzGY254f.js +4709 -0
- package/dist/tui-backend-fzoeoSN0.js +256 -0
- package/dist/tui-cli-B2RWb2gM.js +37 -0
- package/dist/tui-dgS8hdXU.js +2 -0
- package/dist/update-cli-BmiBDf3c.js +3664 -0
- package/dist/update-runner-CM7QVX6u.js +2379 -0
- package/dist/vision-tools-BzoBZnIc.js +1409 -0
- package/dist/web-search-DmfOd66S.js +62 -0
- package/dist/web-search-provider.runtime-3C5-pODD.js +328 -0
- package/dist/web-search-provider.runtime-Doh-Z9We.js +2 -0
- package/dist/web-search-provider.runtime.js +1 -1
- package/dist/xai-oauth-BLvQ43hx.js +479 -0
- package/dist/xai-user-agent-BdMQ_0_O.js +32 -0
- package/package.json +1 -1
- package/dist/abort-DeEb_wKd.js +0 -277
- package/dist/abort.runtime-CoYXQGPK.js +0 -2
- package/dist/account-inspect-D_06-CYS.js +0 -173
- package/dist/accounts-BCTqtj4Y.js +0 -107
- package/dist/accounts-BudLl8P2.js +0 -107
- package/dist/accounts-CUReBDA0.js +0 -2
- package/dist/accounts-PCioSzzr.js +0 -119
- package/dist/acp-runtime-B2t9_BHD.js +0 -26
- package/dist/acp-spawn-0x4Iiu1o.js +0 -2
- package/dist/acp-spawn-B2Ghe0jJ.js +0 -1275
- package/dist/acp-stateful-target-driver-C4VgcSyz.js +0 -89
- package/dist/action-kill-ClYsXEz7.js +0 -33
- package/dist/action-runtime-DrLPkNvU.js +0 -469
- package/dist/action-runtime-api-BqSflr9q.js +0 -2
- package/dist/action-send-CIy9cziD.js +0 -39
- package/dist/action-spawn-BjV6lFnX.js +0 -47
- package/dist/actions-D-QH7wq5.js +0 -161
- package/dist/actions.runtime-hzDtXQGU.js +0 -5
- package/dist/agent-Bb1DTBgv.js +0 -3
- package/dist/agent-Cnqj258q.js +0 -2
- package/dist/agent-command-BRx6ZNgJ.js +0 -1367
- package/dist/agent-components.runtime-D-g6xXJ0.js +0 -10
- package/dist/agent-harness-runtime-dKUQFuOc.js +0 -180
- package/dist/agent-harness-task-runtime-v2crUm3i.js +0 -140
- package/dist/agent-runner-execution-AOzdxvWR.js +0 -1713
- package/dist/agent-runner-utils-Byv50B4Z.js +0 -266
- package/dist/agent-runner.runtime-hhgRE0Z-.js +0 -3455
- package/dist/agent-runtime-BHmh3i0o.js +0 -229
- package/dist/agent-via-gateway-DnCqaK4a.js +0 -463
- package/dist/api-Bzyb8sFU.js +0 -3
- package/dist/api-CIEDFXZs.js +0 -2
- package/dist/api-D_dTQAu8.js +0 -2
- package/dist/api-DilBjZ9Q.js +0 -6
- package/dist/api-E5zoQMmh.js +0 -134
- package/dist/api-OJZug8gQ.js +0 -639
- package/dist/apply-C0eV5T0O.js +0 -41
- package/dist/apply-alsYvxLE.js +0 -54
- package/dist/approval-handler.runtime-C6jqZXSN.js +0 -130
- package/dist/assistant-DDXnAAB5.js +0 -291
- package/dist/attachment-normalize-Cv-_4DWU.js +0 -225
- package/dist/attempt-execution-DECEU59r.js +0 -558
- package/dist/attempt-execution.runtime-kasMxuER.js +0 -3
- package/dist/attempt-execution.shared-ClUxk52p.js +0 -38
- package/dist/attempt.prompt-helpers-Bxlv9VSu.js +0 -475
- package/dist/attempt.tool-run-context-CplQWX6g.js +0 -2094
- package/dist/binding-routing-0Obpp-Ij.js +0 -113
- package/dist/binding-targets-CZHQaZL4.js +0 -121
- package/dist/bot-BPbQ0840.js +0 -7894
- package/dist/bot-deps-Cs1M9USs.js +0 -2
- package/dist/bot-deps-xAcaOtTZ.js +0 -747
- package/dist/bot-message-context.runtime-Bji78Cbn.js +0 -7
- package/dist/bot-message-context.session.runtime-C3dyOIYH.js +0 -12
- package/dist/bot-native-commands.delivery.runtime-WshH99fy.js +0 -4
- package/dist/bot-native-commands.runtime-C1L364gU.js +0 -13
- package/dist/bridge-server-DPlM8_Lk.js +0 -113
- package/dist/browser-cli-DFNQE98N.js +0 -230
- package/dist/browser-cli-Drb5E5--.js +0 -2
- package/dist/browser-cli-actions-input-Dx-1OXmE.js +0 -473
- package/dist/browser-cli-actions-observe-BMDfE7xU.js +0 -81
- package/dist/browser-cli-debug-nu7Ih09g.js +0 -137
- package/dist/browser-cli-inspect-D5X2wohg.js +0 -104
- package/dist/browser-cli-manage-DkzYwph4.js +0 -443
- package/dist/browser-cli-resize-B30Avedl.js +0 -26
- package/dist/browser-cli-shared-cMgQoQzF.js +0 -50
- package/dist/browser-cli-state-CQLDvDy7.js +0 -337
- package/dist/browser-control-auth-D7ArmHUt.js +0 -2
- package/dist/browser-profiles-B39SIZNb.js +0 -2
- package/dist/browser-runtime-DUbSAOOS.js +0 -384
- package/dist/build-D25KqC92.js +0 -257
- package/dist/capability-cli-CuyXrlAB.js +0 -1782
- package/dist/channel-0N3YGMGg.js +0 -1496
- package/dist/channel-1UyKoLyp.js +0 -481
- package/dist/channel-BG87pSEW.js +0 -740
- package/dist/channel-BPGSaZW7.js +0 -1249
- package/dist/channel-BQMPh1J_.js +0 -376
- package/dist/channel-Bd_8V6zn.js +0 -1134
- package/dist/channel-BtBjh_ij.js +0 -362
- package/dist/channel-CpFBlVH6.js +0 -562
- package/dist/channel-CwuTrIrF.js +0 -508
- package/dist/channel-Cxl4sJA-.js +0 -1777
- package/dist/channel-D-VfU4Z2.js +0 -2126
- package/dist/channel-D9q8aYrN.js +0 -867
- package/dist/channel-DKSvVvZh.js +0 -238
- package/dist/channel-DvoFfWLx.js +0 -808
- package/dist/channel-GktTcGHm.js +0 -955
- package/dist/channel-actions.runtime-CKcRA0GW.js +0 -265
- package/dist/channel-core-nm8s1qFZ.js +0 -5
- package/dist/channel-d3t2ESlE.js +0 -653
- package/dist/channel-inbound-DKz40dq-.js +0 -80
- package/dist/channel-plugin-runtime-DKIGZWfW.js +0 -998
- package/dist/channel-runtime-Bh8_GY12.js +0 -408
- package/dist/channel-yQ8jCOb9.js +0 -1556
- package/dist/channel.runtime-BG4mM5cQ.js +0 -652
- package/dist/channel.runtime-BS6PyFFa.js +0 -1008
- package/dist/channel.runtime-CE_xECqN.js +0 -88
- package/dist/channel.runtime-Cd1Sw8U4.js +0 -254
- package/dist/channel.runtime-DIq3XOEe.js +0 -733
- package/dist/channel.runtime-Dy1cx35I.js +0 -21009
- package/dist/channel.runtime-NyIMjLnP.js +0 -109
- package/dist/channel.runtime-Zx5mfE2V.js +0 -4
- package/dist/channel.runtime-rJRibGfN.js +0 -2528
- package/dist/channel.setup--4ACadmF.js +0 -10
- package/dist/channel.setup-DzwqIlo3.js +0 -1098
- package/dist/channel.setup-FKYSJwXR.js +0 -343
- package/dist/chat-CKxSm7r1.js +0 -2666
- package/dist/chrome-rMubJwRG.js +0 -1503
- package/dist/cli-CbTod55I.js +0 -1341
- package/dist/cli-compaction-BbHgjJyW.js +0 -347
- package/dist/cli-metadata-DkOWLC_p.js +0 -22
- package/dist/cli-runner-Cwzv_RKf.js +0 -540
- package/dist/cli-runner-DdnUsgPl.js +0 -2
- package/dist/cli-runner.runtime-BlrSgbEW.js +0 -3
- package/dist/cli-runner.runtime-D8kVfvFH.js +0 -4
- package/dist/client-ClvxsWgL.js +0 -650
- package/dist/client-adapter-CsrIIjK1.js +0 -897
- package/dist/client-factory-DAYClhwm.js +0 -9
- package/dist/command-auth-CCha2ofd.js +0 -135
- package/dist/command-handlers-DCWJzHyB.js +0 -1609
- package/dist/command-registry-BIf61QNz.js +0 -9
- package/dist/command-registry-DJROBy4h.js +0 -4
- package/dist/command-registry-core-BQOWqi6S.js +0 -110
- package/dist/command-status.runtime-D88CUglL.js +0 -90
- package/dist/commands-acp-B-8dHX4Z.js +0 -74
- package/dist/commands-compact.runtime-ComX5mUk.js +0 -10
- package/dist/commands-handlers.runtime-DR3wjXHX.js +0 -6154
- package/dist/commands-status-CDyGrwsI.js +0 -3
- package/dist/commands-status-DV-i_ZIK.js +0 -16
- package/dist/commands-status.runtime-CDyGrwsI.js +0 -3
- package/dist/commands-subagents-control.runtime-B029cXAS.js +0 -3
- package/dist/commands-subagents-control.runtime-DR8Qspe0.js +0 -2
- package/dist/commands-system-prompt-C10ctsG3.js +0 -162
- package/dist/commands-system-prompt-DQlRWwnk.js +0 -2
- package/dist/commands.runtime-BYmIAuN0.js +0 -176
- package/dist/compact-14Ljaaeu.js +0 -480
- package/dist/compact-B--ovdkx.js +0 -1141
- package/dist/compact.runtime-E0Idf2Dq.js +0 -12
- package/dist/completion-cli-DMvvQGSk.js +0 -315
- package/dist/computer-use-CcLwX5SR.js +0 -367
- package/dist/config-B39SIZNb.js +0 -2
- package/dist/config-D4rsGOyV.js +0 -373
- package/dist/config-mutations-CsI3YJu7.js +0 -159
- package/dist/context-engine-host-compat-BZpDFiMJ.js +0 -2
- package/dist/context-engine-host-compat-runUdES5.js +0 -288
- package/dist/context-engine-lifecycle-D6odtNrn.js +0 -1274
- package/dist/control-auth-BPGpPtfz.js +0 -114
- package/dist/control-service-C-OmdPCe.js +0 -145
- package/dist/control-ui/assets/agents-GeyOHPuW.js +0 -1008
- package/dist/control-ui/assets/channel-config-extras-D7en6iUg.js +0 -2
- package/dist/control-ui/assets/channels-DkEyr1w5.js +0 -367
- package/dist/control-ui/assets/cron-C-wThQ1Q.js +0 -1013
- package/dist/control-ui/assets/debug-83AFRtIX.js +0 -97
- package/dist/control-ui/assets/index-CiGEhMOA.js +0 -7370
- package/dist/control-ui/assets/instances-CVl0t-1h.js +0 -57
- package/dist/control-ui/assets/logs-eybVEXxg.js +0 -74
- package/dist/control-ui/assets/nodes-C_A7eoU2.js +0 -436
- package/dist/control-ui/assets/sessions-Cq4Nc69u.js +0 -399
- package/dist/control-ui/assets/skills-ZN6hroIh.js +0 -314
- package/dist/control-ui/assets/skills-shared-DIWGwmdC.js +0 -11
- package/dist/conversation-binding-runtime-C2U1JElL.js +0 -4
- package/dist/conversation-runtime-NmIUd3Zu.js +0 -31
- package/dist/core-DGKJP1dm.js +0 -282
- package/dist/core-api-1fA4sNeC.js +0 -5
- package/dist/core-api-BY822Quq.js +0 -2
- package/dist/crestodian-Bp-NXiBC.js +0 -55
- package/dist/daocore-tools-Defpam0j.js +0 -11727
- package/dist/delivery-CEdVAUGB.js +0 -1002
- package/dist/dialogue-CZ69INPq.js +0 -37
- package/dist/dir-fetch-tool-BCicHhQE.js +0 -565
- package/dist/dir-list-tool-Cv_WktsJ.js +0 -100
- package/dist/direct-dm-Bz89rM8x.js +0 -64
- package/dist/directive-handling.fast-lane-DDnbcc5S.js +0 -68
- package/dist/directive-handling.impl-B0H52Ymr.js +0 -818
- package/dist/directive-handling.impl-CgKpwEW-.js +0 -2
- package/dist/directive-handling.model-selection-_cXAr0vt.js +0 -122
- package/dist/directive-handling.persist.runtime-Dw0mfzXT.js +0 -263
- package/dist/dispatch-C3AeYvyP.js +0 -1640
- package/dist/dispatch-acp-transcript.runtime-CP8pqBwS.js +0 -40
- package/dist/dispatch-acp.runtime-Cdap-AZI.js +0 -18
- package/dist/doctor-BZwVH97p.js +0 -2
- package/dist/doctor-BfkGBii1.js +0 -6
- package/dist/doctor-config-flow-D3JASGDt.js +0 -1741
- package/dist/doctor-core-checks-DH5AIT0Q.js +0 -573
- package/dist/doctor-core-checks-DiNu7VSh.js +0 -2
- package/dist/doctor-health-BDIJ-Nro.js +0 -65
- package/dist/doctor-health-contributions-BgmF7w8X.js +0 -696
- package/dist/doctor-lint-uIxkMUSO.js +0 -94
- package/dist/doctor-state-integrity-ws1b_BGk.js +0 -1231
- package/dist/doctor-update-Cx4CqxaX.js +0 -58
- package/dist/dynamic-tools-DiqOxhJh.js +0 -486
- package/dist/embedded-backend-C634irMl.js +0 -579
- package/dist/embedded-gateway-stub.runtime-CCfrTOeN.js +0 -12
- package/dist/exec-approvals-D85KThSg.js +0 -149
- package/dist/file-fetch-tool-BERaGYCT.js +0 -124
- package/dist/file-write-tool-BjHrMyfe.js +0 -127
- package/dist/format-DE9PuPg1.js +0 -1145
- package/dist/gateway-cli-B5hjhbUy.js +0 -435
- package/dist/gateway-method-runtime-Bu0E9Eki.js +0 -21
- package/dist/get-reply-6PtPz9hv.js +0 -4689
- package/dist/get-reply-from-config.runtime-CwprvhoR.js +0 -2
- package/dist/graph-users-DUp1kgnS.js +0 -1419
- package/dist/group-access-D5GBQ3w6.js +0 -112
- package/dist/handle-action.guild-admin-CA0Y-buD.js +0 -288
- package/dist/harness-B5nOFVfm.js +0 -61
- package/dist/health-D5heIDj3.js +0 -4
- package/dist/heartbeat-runner-BzFkIFHh.js +0 -5
- package/dist/heartbeat-runner.runtime-p6H1_xMV.js +0 -4
- package/dist/hooks-BvTyQ14X.js +0 -534
- package/dist/inbound-direct-dm-runtime-DwiDXi8L.js +0 -2
- package/dist/inbound-reply-dispatch-YoDUOC6C.js +0 -148
- package/dist/init-CPtcV5Xu.js +0 -59
- package/dist/inline-buttons-DeD5d42c.js +0 -40
- package/dist/internal-events-DEb50Sw3.js +0 -90
- package/dist/isolated-agent-CcxFFZ-Z.js +0 -1118
- package/dist/isolated-agent-CpHzq5qr.js +0 -2
- package/dist/lifecycle-DNel-oMe.js +0 -571
- package/dist/list.probe-DCa3N25d.js +0 -449
- package/dist/list.status-command-CyeAJmZW.js +0 -789
- package/dist/llm-slug-generator-CyqTY37Z.js +0 -78
- package/dist/local-dispatch.runtime-nE39kFIP.js +0 -9
- package/dist/manager.runtime-CpAPB8D5.js +0 -2714
- package/dist/markdown-to-line-Ci6TRmoZ.js +0 -811
- package/dist/mcp-http-BR0vmxFL.js +0 -2
- package/dist/mcp-http-C0oniA8h.js +0 -555
- package/dist/media-understanding-provider-BwA0XqC3.js +0 -339
- package/dist/message-actions-C9C_Ngkd.js +0 -145
- package/dist/message-handler-CUUKPC5n.js +0 -384
- package/dist/message-handler-xRTfIXWV.js +0 -1715
- package/dist/message-handler.preflight-Ch2Q7V3M.js +0 -1125
- package/dist/message-handler.process-HM3mK93q.js +0 -1484
- package/dist/model-CB8Ex5xS.js +0 -74
- package/dist/model-selection-2s0Dinux.js +0 -272
- package/dist/models-Cn-6DJ41.js +0 -2
- package/dist/models-CzszJD__.js +0 -104
- package/dist/models-cli-CbhElnA5.js +0 -256
- package/dist/monitor-7aprTsMV.js +0 -715
- package/dist/monitor-BuVhqwDS.js +0 -1370
- package/dist/monitor-C4H_YkgY.js +0 -2
- package/dist/monitor-CPQsMxgv.js +0 -1657
- package/dist/monitor-Ce0V1PiR.js +0 -4377
- package/dist/monitor-D0bcGJWI.js +0 -60
- package/dist/monitor-DEjlJqzh.js +0 -834
- package/dist/monitor-auth-DjJZsjgV.js +0 -179
- package/dist/monitor-ikSYagv3.js +0 -2788
- package/dist/monitor-polling.runtime-D7yEflMM.js +0 -883
- package/dist/monitor-webhook.runtime-CIZt-biI.js +0 -387
- package/dist/monitor.account-C2sMOBCS.js +0 -5233
- package/dist/monitor.runtime-BAi6zmcn.js +0 -2
- package/dist/monitor.webhook-BcjpfRCX.js +0 -180
- package/dist/node-cli-sessions-CIhkJRU4.js +0 -1228
- package/dist/openai-http-Dj21RwSj.js +0 -824
- package/dist/openresponses-http-COnnr1ox.js +0 -1173
- package/dist/operations-H3vUh0lM.js +0 -805
- package/dist/outbound-adapter-WLtWE7wv.js +0 -543
- package/dist/outbound-session-route-CZtd64EH.js +0 -45
- package/dist/outbound.runtime-D98P0sN1.js +0 -2
- package/dist/pi-embedded-BWJzd4mK.js +0 -4
- package/dist/pi-embedded-DeNsSqQQ.js +0 -3796
- package/dist/pi-embedded.runtime-CZnNwFpc.js +0 -4
- package/dist/pi-tools-CNHSpjBa.js +0 -2413
- package/dist/plan-B0reFFlM.js +0 -81
- package/dist/plan-CrfF-TH8.js +0 -112
- package/dist/plugin-DI_8eYOe.js +0 -12396
- package/dist/plugin-app-cache-key-WaTUD3e-.js +0 -46
- package/dist/plugin-enabled-aWLXgGGi.js +0 -233
- package/dist/plugin-registration-ByjRIVJm.js +0 -88
- package/dist/plugin-service-VQm_241d.js +0 -1229
- package/dist/policy-BmJH-swe.js +0 -680
- package/dist/policy-DE-bO1zn.js +0 -138
- package/dist/prepare.runtime-bSj3-res.js +0 -732
- package/dist/preview-warnings-CbuGYsF9.js +0 -392
- package/dist/probe-C_rWg7_m.js +0 -682
- package/dist/probe-CagOFfx6.js +0 -2
- package/dist/probe-thX1HqOh.js +0 -2204
- package/dist/probe-zU6B6gFt.js +0 -47
- package/dist/program-BtVdH743.js +0 -131
- package/dist/provider-CDoD7tO0.js +0 -8735
- package/dist/provider-CK1gMdJ2.js +0 -32
- package/dist/provider-DJqr9djy.js +0 -32
- package/dist/provider-DsOJp5bK.js +0 -152
- package/dist/provider-dispatcher-UNQ-LSx9.js +0 -22
- package/dist/provider-session.runtime-DMrkMb4x.js +0 -9
- package/dist/provider.runtime-C2-t3zm8.js +0 -2
- package/dist/public-surface-loader-hTeyyHcd.js +0 -114
- package/dist/pw-ai-BaL3eVYx.js +0 -3029
- package/dist/pw-role-snapshot-DKeBqhN1.js +0 -333
- package/dist/reaction-level-BV2potsD.js +0 -19
- package/dist/reaction-runtime-api-CCBxgM10.js +0 -116
- package/dist/realtime-transcription-provider-CHYtIXZm.js +0 -205
- package/dist/register-BJf28G9B.js +0 -2178
- package/dist/register.agent-oXAHwNQv.js +0 -156
- package/dist/register.crestodian-CQ0sqdV2.js +0 -24
- package/dist/register.maintenance-CylVRs5M.js +0 -83
- package/dist/register.runtime-CcDgwy0X.js +0 -54
- package/dist/register.subclis-CpBYNw2X.js +0 -3
- package/dist/register.subclis-N2CvieOL.js +0 -31
- package/dist/register.subclis-core-BHaGvbzg.js +0 -273
- package/dist/repair-sequencing-EBBnBdIB.js +0 -640
- package/dist/reply-delivery-BLoPALlI.js +0 -196
- package/dist/reply-runtime-D4asNTSa.js +0 -11
- package/dist/reply.runtime-CwprvhoR.js +0 -2
- package/dist/request-DlWPRUDt.js +0 -54
- package/dist/resolve-allowlist-BukQf58x.js +0 -220
- package/dist/result-fallback-classifier-DyHFnqfV.js +0 -79
- package/dist/route-BMf4keN5.js +0 -469
- package/dist/route-resolution-DWNi1QAu.js +0 -274
- package/dist/routes-C20LC8c4.js +0 -3602
- package/dist/routes-CkK7Vv_m.js +0 -2
- package/dist/run-KDF_AEza.js +0 -1162
- package/dist/run-attempt-JHYCfonU.js +0 -7704
- package/dist/run-command-3PVihYIy.js +0 -2
- package/dist/run-command-QHAXnyKY.js +0 -23
- package/dist/run-embedded.runtime-BJw-2vss.js +0 -4
- package/dist/run-execution-cli.runtime-GHClMn2g.js +0 -4
- package/dist/run-subagent-registry.runtime-BLLMrhtE.js +0 -2
- package/dist/runtime-C2u-dC1r.js +0 -1287
- package/dist/runtime-ClyfkDWT.js +0 -6179
- package/dist/runtime-WXCarlPc.js +0 -438
- package/dist/runtime-api-3QhGLpZf.js +0 -13
- package/dist/runtime-api-DRAu3mjv.js +0 -4
- package/dist/runtime-api-JE07pOem.js +0 -13
- package/dist/runtime-api-JuKylHvw.js +0 -24
- package/dist/runtime-api-bcbk4yax.js +0 -21
- package/dist/runtime-api-dDe9U2_V.js +0 -17
- package/dist/runtime-api-uMexLqGS.js +0 -3
- package/dist/runtime-api.actions-Bo7TLDFl.js +0 -3
- package/dist/runtime-api.monitor-7q78L1Em.js +0 -6
- package/dist/runtime-api.send-Bf9RR4nV.js +0 -4
- package/dist/runtime-api.threads-Dj2QuhHj.js +0 -2
- package/dist/runtime-channel-k1C0Satl.js +0 -2
- package/dist/runtime-channel-qt9t0J-J.js +0 -150
- package/dist/runtime-embedded-pi.runtime-BPB9NlTS.js +0 -2
- package/dist/sanitize-outbound-BLH_SQvg.js +0 -127
- package/dist/sdk-setup-tools-BshlBPau.js +0 -8
- package/dist/secrets-CsHXv7d3.js +0 -113
- package/dist/security-audit-CUXBQXNX.js +0 -122
- package/dist/security-audit-CzdXbRma.js +0 -118
- package/dist/security-audit.runtime-Ca0FMFJG.js +0 -2
- package/dist/selection-BmvQJlSD.js +0 -3
- package/dist/selection-BplDXc9w.js +0 -16157
- package/dist/send--qFg8a9v.js +0 -1631
- package/dist/send-BPdryPPA.js +0 -192
- package/dist/send-Bl4YaRkS.js +0 -143
- package/dist/send-Ccm7RMXC.js +0 -2
- package/dist/send.components-c3OUPSmR.js +0 -2
- package/dist/send.components-rhQJYoXs.js +0 -500
- package/dist/send.runtime-dfUkAp5d.js +0 -2
- package/dist/server-DRhyTMLe.js +0 -73
- package/dist/server-Dv4BzjGn.js +0 -24
- package/dist/server-context-4HRJy2vp.js +0 -955
- package/dist/server-context-DlxYb1G5.js +0 -2
- package/dist/server-cron-Bt8Pkc3i.js +0 -2989
- package/dist/server-cron-CtNWsa4Z.js +0 -2
- package/dist/server-methods-rye_okUW.js +0 -16494
- package/dist/server-node-events-C1yZ5a1u.js +0 -596
- package/dist/server-plugin-bootstrap-DUMyVtlP.js +0 -70
- package/dist/server-plugins-B5ZWWkRE.js +0 -432
- package/dist/server-reload-handlers-CTnMXNjT.js +0 -714
- package/dist/server-restart-sentinel-CAYxtwmY.js +0 -747
- package/dist/server-restart-sentinel-DqrtSIvy.js +0 -2
- package/dist/server-runtime-services-CuptQTe5.js +0 -2
- package/dist/server-runtime-services-aaS2IiW3.js +0 -267
- package/dist/server-startup-plugins-D0ymJgQT.js +0 -113
- package/dist/server-startup-post-attach-DlrN2uyt.js +0 -716
- package/dist/server-ws-runtime-CdpLbv4o.js +0 -349
- package/dist/server.impl-Cb4qcMTg.js +0 -2586
- package/dist/service-qxvDlMQE.js +0 -1446
- package/dist/session-binding-6bLobLHL.js +0 -219
- package/dist/session-binding-Bm6fCpoY.js +0 -2
- package/dist/session-kill-http-B6WjVo8V.js +0 -121
- package/dist/session-reset-service-GUVIhxp4.js +0 -625
- package/dist/session-route-BcRDnvzG.js +0 -93
- package/dist/session-status.runtime-BcjOunV4.js +0 -2
- package/dist/session-subagent-reactivation.runtime-BSmk_KYn.js +0 -2
- package/dist/session-tab-registry-BBYzbSOu.js +0 -521
- package/dist/sessions-history-http-bLJJfgLm.js +0 -430
- package/dist/sessions.runtime-DNRtQzCk.js +0 -2
- package/dist/setup-api-CcolVVJs.js +0 -29
- package/dist/setup-core-eJD18F3B.js +0 -174
- package/dist/setup-surface-B75C2Qtz.js +0 -221
- package/dist/setup-surface-CFuPfVHp.js +0 -405
- package/dist/setup-surface-Dyy-Mzyz.js +0 -288
- package/dist/setup-surface-ZsVF_g8W.js +0 -320
- package/dist/shared-BkCNrcLX.js +0 -121
- package/dist/shared-client-B7zqC9p2.js +0 -2
- package/dist/shared-client-yXjKgZBz.js +0 -629
- package/dist/side-question-DU3gESCb.js +0 -683
- package/dist/skill-tool-dispatch.runtime-CPBuqyvk.js +0 -143
- package/dist/slash-state-Do3bAahA.js +0 -2166
- package/dist/speech-provider-Bg9los3C.js +0 -184
- package/dist/src-D7LzUctH.js +0 -4256
- package/dist/startup-context-CCF2gIOl.js +0 -313
- package/dist/status-subagents.runtime-DX5FTymp.js +0 -18
- package/dist/status-text-B-1u5dSV.js +0 -296
- package/dist/sticker-cache-DwpU_9RJ.js +0 -206
- package/dist/sticker-vision.runtime-Bbldi_YL.js +0 -17
- package/dist/subagent-announce-BCo0VHVL.js +0 -354
- package/dist/subagent-announce-delivery-JcnuDN_N.js +0 -958
- package/dist/subagent-control-BzbA3Suz.js +0 -508
- package/dist/subagent-hooks-BIGZQWrG.js +0 -2
- package/dist/subagent-hooks-Bj4qYZfv.js +0 -230
- package/dist/subagent-hooks-C-rvhVBv.js +0 -2
- package/dist/subagent-hooks-P01_AFl5.js +0 -116
- package/dist/subagent-hooks-VEfak8nl.js +0 -2
- package/dist/subagent-hooks-api-BPnSxxN4.js +0 -23
- package/dist/subagent-hooks-api-DwIAvMoS.js +0 -22
- package/dist/subagent-hooks-api-kgyR9FOb.js +0 -23
- package/dist/subagent-hooks-oED56wqq.js +0 -146
- package/dist/subagent-orphan-recovery-D79ZzwKN.js +0 -352
- package/dist/subagent-registry-D0soBT5j.js +0 -2351
- package/dist/subagent-registry-DRJDkmty.js +0 -3
- package/dist/subagent-session-cleanup-9eAO1aJe.js +0 -525
- package/dist/subagent-spawn-D80vbogm.js +0 -1164
- package/dist/target-id-COLv5LsJ.js +0 -107
- package/dist/targets-CDW5IQ6a.js +0 -44
- package/dist/targets-Ci6O1ZdP.js +0 -19
- package/dist/targets-CsaWFBg1.js +0 -19
- package/dist/testing-BoJit-h1.js +0 -267
- package/dist/thread-bindings-CGCvw0KT.js +0 -571
- package/dist/thread-bindings-ClCTNacX.js +0 -228
- package/dist/thread-bindings-DRb7BMZ6.js +0 -8
- package/dist/thread-bindings-Xc6smav0.js +0 -232
- package/dist/thread-bindings.discord-api-DC467oeF.js +0 -187
- package/dist/thread-bindings.manager-C_mpTDIZ.js +0 -536
- package/dist/thread-bindings.manager-DkCRs612.js +0 -2
- package/dist/thread-lifecycle-DpqCXlx9.js +0 -1614
- package/dist/token-CI6HjbTA.js +0 -134
- package/dist/tool-BMe7hjBK.js +0 -139
- package/dist/tool-actions.runtime-D-h5PI_m.js +0 -534
- package/dist/tool-resolution-CnLx0CHe.js +0 -149
- package/dist/tools-effective-inventory-BR6MUMtE.js +0 -204
- package/dist/tools-invoke-http-CTTs2yMT.js +0 -67
- package/dist/tools-invoke-shared-9B1EjXWf.js +0 -200
- package/dist/tts-eGOviZ5c.js +0 -66
- package/dist/tui-Bw0HqKd7.js +0 -4709
- package/dist/tui-CIZJnPzj.js +0 -2
- package/dist/tui-backend-BJ_r7tcF.js +0 -256
- package/dist/tui-cli-BaSMBpuA.js +0 -37
- package/dist/update-cli-zuCybGNR.js +0 -3664
- package/dist/update-runner-Dek2BHmQ.js +0 -2379
- package/dist/vision-tools-1ps0BEE5.js +0 -1409
- package/dist/web-search-B7EziZXE.js +0 -62
- package/dist/web-search-provider.runtime-DGTCvGch.js +0 -328
- package/dist/web-search-provider.runtime-DWL5t39M.js +0 -2
- package/dist/xai-oauth-CRtsj2Gs.js +0 -479
- package/dist/xai-user-agent-Dndwzw2S.js +0 -32
- /package/dist/{acp-runtime-backend-DSDBcyh9.js → acp-runtime-backend-Cxo7eBHf.js} +0 -0
- /package/dist/{channel-actions-FV66JqtI.js → channel-actions-UDeVjgGz.js} +0 -0
- /package/dist/{command-status-runtime-BRWKSoG7.js → command-status-runtime-CoHd4Fws.js} +0 -0
- /package/dist/{delegate-DGqKhwB4.js → delegate-B-2xZ77o.js} +0 -0
- /package/dist/{dispatch-acp-Ceoxja_Z.js → dispatch-acp-DLPkmK7K.js} +0 -0
- /package/dist/{heartbeat-runner-CJHvr5pG.js → heartbeat-runner-D2j6JwOI.js} +0 -0
- /package/dist/{library-Bq3aDek_.js → library-DBT0cIPP.js} +0 -0
- /package/dist/{run-executor.runtime-DNJhGPbA.js → run-executor.runtime-DE4J7f4M.js} +0 -0
- /package/dist/{shared-CYxmRpq1.js → shared-xeo8Yh5n.js} +0 -0
|
@@ -0,0 +1,2989 @@
|
|
|
1
|
+
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-DyL154ka.js";
|
|
2
|
+
import { i as formatErrorMessage } from "./errors-D_oyTIw2.js";
|
|
3
|
+
import "./agent-scope-Dx1S0aWX.js";
|
|
4
|
+
import { a as isSubagentSessionKey, c as parseAgentSessionKey, r as isCronRunSessionKey } from "./session-key-utils-Ce_xWkNq.js";
|
|
5
|
+
import { d as resolveAgentIdFromSessionKey, f as resolveEventSessionKey, l as normalizeAgentId, v as toAgentStoreSessionKey } from "./session-key-Bte0mmcq.js";
|
|
6
|
+
import { c as resolveDefaultAgentId } from "./agent-scope-config-CJUQxn7u.js";
|
|
7
|
+
import { n as defaultRuntime } from "./runtime-E_A14BX_.js";
|
|
8
|
+
import { r as getChildLogger } from "./logger-DBVVIUcX.js";
|
|
9
|
+
import { i as getRuntimeConfig } from "./io-Ct2JqgbR.js";
|
|
10
|
+
import { t as parseDurationMs } from "./parse-duration-CD4d_yk2.js";
|
|
11
|
+
import { t as getGlobalHookRunner } from "./hook-runner-global-RR7B_IHd.js";
|
|
12
|
+
import { n as resolveAgentMainSessionKey, t as canonicalizeMainSessionAlias } from "./main-session-D3q_5w0B.js";
|
|
13
|
+
import { n as deliveryContextFromSession } from "./delivery-context.shared-XybGGRNt.js";
|
|
14
|
+
import { u as resolveStorePath } from "./paths-8MFrTNIB.js";
|
|
15
|
+
import { i as readSessionEntry, t as loadSessionStore } from "./store-load-B817r-3Z.js";
|
|
16
|
+
import { t as archiveRemovedSessionTranscripts, u as updateSessionStore } from "./store-C4uyNaPD.js";
|
|
17
|
+
import "./sessions-4ZNlAnYc.js";
|
|
18
|
+
import "./logging-CvTe2C0z.js";
|
|
19
|
+
import { a as isRetryableHeartbeatBusySkipReason, o as requestHeartbeat } from "./heartbeat-wake-BEc0M8hq.js";
|
|
20
|
+
import { a as enqueueSystemEvent } from "./system-events-By5h2EoB.js";
|
|
21
|
+
import { a as failTaskRunByRunId, i as createRunningTaskRun, n as completeTaskRunByRunId } from "./detached-task-runtime-B8jNifvc.js";
|
|
22
|
+
import { t as abortAndDrainEmbeddedPiRun } from "./runs-SleIj69Z.js";
|
|
23
|
+
import "./session-utils.fs-CG2Uh2qt.js";
|
|
24
|
+
import { i as cleanupArchivedSessionTranscripts } from "./session-transcript-files.fs-Bv7kT0GX.js";
|
|
25
|
+
import { i as enqueueCommandInLane } from "./command-queue-B4bof8Vu.js";
|
|
26
|
+
import { i as markCronJobActive, t as clearCronJobActive } from "./active-jobs-CbP6gty6.js";
|
|
27
|
+
import { a as summarizeCronRunDiagnostics, i as normalizeCronRunDiagnostics, n as createCronRunDiagnosticsFromError } from "./run-diagnostics-DCtUqVFF.js";
|
|
28
|
+
import { a as resolveCronRunLogPath, o as resolveCronRunLogPruneOptions, t as appendCronRunLog } from "./run-log-DI4c1joe.js";
|
|
29
|
+
import { a as cronSchedulingInputsEqual, i as saveCronStore, r as resolveCronStorePath, t as loadCronStore } from "./store-jjhplGXD.js";
|
|
30
|
+
import { d as normalizeCronJobInput } from "./daocore-tools-BMdF8RwK.js";
|
|
31
|
+
import { s as resolveFailoverReasonFromError } from "./failover-error-5VF0hP0c.js";
|
|
32
|
+
import { r as resolveMainScopedEventSessionKey } from "./event-session-routing-CiWm70tS.js";
|
|
33
|
+
import { a as resolveCronDeliverySessionKey, o as resolveCronNotificationSessionKey, r as isInvalidCronSessionTargetIdError, s as resolveCronSessionTargetSessionKey, t as normalizeHttpWebhookUrl } from "./webhook-url-Mg_qwzOp.js";
|
|
34
|
+
import { n as normalizeOptionalAgentId } from "./normalize-esRscAf6.js";
|
|
35
|
+
import { t as SsrFBlockedError } from "./ssrf-BNF8aODC.js";
|
|
36
|
+
import { n as fetchWithSsrFGuard } from "./fetch-guard-CeBd6u85.js";
|
|
37
|
+
import { t as buildOutboundSessionContext } from "./session-context-DkisUYMa.js";
|
|
38
|
+
import { t as sendDurableMessageBatch } from "./send-ymTTYJHj.js";
|
|
39
|
+
import "./runtime-DQNx44DZ.js";
|
|
40
|
+
import "./pi-embedded-1-rsueO_.js";
|
|
41
|
+
import { n as resolveAgentOutboundIdentity } from "./identity-D0dQQH5U.js";
|
|
42
|
+
import { t as createOutboundSendDeps } from "./outbound-send-deps-CGRtDmXy.js";
|
|
43
|
+
import { t as cleanupBrowserSessionsForLifecycleEnd } from "./browser-lifecycle-cleanup-c153PKlW.js";
|
|
44
|
+
import { t as getInvalidPersistedCronJobReason } from "./persisted-shape-DyK3Ydhz.js";
|
|
45
|
+
import { t as runCronIsolatedAgentTurn } from "./isolated-agent-CnsD2TmP.js";
|
|
46
|
+
import { n as resolveFailureDestination, t as resolveCronDeliveryPlan } from "./delivery-plan-DEw2f8Aw.js";
|
|
47
|
+
import { t as resolveCronAgentSessionKey } from "./session-key-D_ijetg2.js";
|
|
48
|
+
import { _ as resolveDeliveryTarget, a as computeJobPreviousRunAtMs, c as findJobOrThrow, d as isJobEnabled, f as nextWakeAtMs, g as resolveJobPayloadTextForMain, h as recordScheduleComputeError, i as computeJobNextRunAtMs, l as hasScheduledNextRunAtMs, m as recomputeNextRunsForMaintenance, n as applyJobPatch, o as createJob, p as recomputeNextRuns, r as assertSupportedJobSpec, s as errorBackoffMs, t as DEFAULT_ERROR_BACKOFF_SCHEDULE_MS, u as isJobDue } from "./jobs-DDfJHtnF.js";
|
|
49
|
+
import { t as createCronExecutionId } from "./run-id-BEdij8BZ.js";
|
|
50
|
+
import { n as runHeartbeatOnce } from "./heartbeat-runner-D2j6JwOI.js";
|
|
51
|
+
import fs from "node:fs";
|
|
52
|
+
//#region src/cron/service/locked.ts
|
|
53
|
+
const storeLocks = /* @__PURE__ */ new Map();
|
|
54
|
+
const resolveChain = (promise) => promise.then(() => void 0, () => void 0);
|
|
55
|
+
async function locked(state, fn) {
|
|
56
|
+
const storePath = state.deps.storePath;
|
|
57
|
+
const storeOp = storeLocks.get(storePath) ?? Promise.resolve();
|
|
58
|
+
const next = Promise.all([resolveChain(state.op), resolveChain(storeOp)]).then(fn);
|
|
59
|
+
const keepAlive = resolveChain(next);
|
|
60
|
+
state.op = keepAlive;
|
|
61
|
+
storeLocks.set(storePath, keepAlive);
|
|
62
|
+
return await next;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/cron/normalize-job-identity.ts
|
|
66
|
+
function normalizeCronJobIdentityFields(raw) {
|
|
67
|
+
const rawId = normalizeOptionalString(raw.id) ?? "";
|
|
68
|
+
const legacyJobId = normalizeOptionalString(raw.jobId) ?? "";
|
|
69
|
+
const hadJobIdKey = "jobId" in raw;
|
|
70
|
+
const normalizedId = rawId || legacyJobId;
|
|
71
|
+
const idChanged = Boolean(normalizedId && raw.id !== normalizedId);
|
|
72
|
+
if (idChanged) raw.id = normalizedId;
|
|
73
|
+
if (hadJobIdKey) delete raw.jobId;
|
|
74
|
+
return {
|
|
75
|
+
mutated: idChanged || hadJobIdKey,
|
|
76
|
+
legacyJobIdIssue: hadJobIdKey
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/cron/service/store.ts
|
|
81
|
+
function invalidateStaleNextRunOnScheduleChange(params) {
|
|
82
|
+
const previousJob = params.previousJobsById.get(params.hydrated.id);
|
|
83
|
+
if (!previousJob || cronSchedulingInputsEqual(previousJob, params.hydrated)) return;
|
|
84
|
+
params.hydrated.state ??= {};
|
|
85
|
+
params.hydrated.state.nextRunAtMs = void 0;
|
|
86
|
+
}
|
|
87
|
+
function warnInvalidPersistedCronJob(params) {
|
|
88
|
+
const jobId = typeof params.raw.id === "string" ? params.raw.id : void 0;
|
|
89
|
+
const dedupeKey = jobId ?? `index:${params.index}`;
|
|
90
|
+
if (params.state.warnedInvalidPersistedJobKeys.has(dedupeKey)) return;
|
|
91
|
+
params.state.warnedInvalidPersistedJobKeys.add(dedupeKey);
|
|
92
|
+
params.state.deps.log.warn({
|
|
93
|
+
storePath: params.state.deps.storePath,
|
|
94
|
+
jobId,
|
|
95
|
+
jobIndex: params.index,
|
|
96
|
+
reason: params.reason
|
|
97
|
+
}, "cron: skipped invalid persisted job; run daocore doctor --fix to repair");
|
|
98
|
+
}
|
|
99
|
+
async function getFileMtimeMs(path) {
|
|
100
|
+
try {
|
|
101
|
+
return (await fs.promises.stat(path)).mtimeMs;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function ensureLoaded(state, opts) {
|
|
107
|
+
if (state.store && !opts?.forceReload) return;
|
|
108
|
+
const previousJobsById = /* @__PURE__ */ new Map();
|
|
109
|
+
for (const job of state.store?.jobs ?? []) previousJobsById.set(job.id, job);
|
|
110
|
+
const fileMtimeMs = await getFileMtimeMs(state.deps.storePath);
|
|
111
|
+
const loadedJobs = (await loadCronStore(state.deps.storePath)).jobs ?? [];
|
|
112
|
+
const jobs = [];
|
|
113
|
+
for (const [index, job] of loadedJobs.entries()) {
|
|
114
|
+
const raw = job;
|
|
115
|
+
const { legacyJobIdIssue } = normalizeCronJobIdentityFields(raw);
|
|
116
|
+
let normalized;
|
|
117
|
+
try {
|
|
118
|
+
normalized = normalizeCronJobInput(raw);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (!isInvalidCronSessionTargetIdError(error)) throw error;
|
|
121
|
+
normalized = null;
|
|
122
|
+
state.deps.log.warn({
|
|
123
|
+
storePath: state.deps.storePath,
|
|
124
|
+
jobId: typeof raw.id === "string" ? raw.id : void 0
|
|
125
|
+
}, "cron: job has invalid persisted sessionTarget; run daocore doctor --fix to repair");
|
|
126
|
+
}
|
|
127
|
+
const hydrated = normalized && typeof normalized === "object" ? normalized : job;
|
|
128
|
+
const invalidReason = getInvalidPersistedCronJobReason(hydrated);
|
|
129
|
+
if (invalidReason) {
|
|
130
|
+
warnInvalidPersistedCronJob({
|
|
131
|
+
state,
|
|
132
|
+
raw,
|
|
133
|
+
index,
|
|
134
|
+
reason: invalidReason
|
|
135
|
+
});
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
jobs.push(hydrated);
|
|
139
|
+
if (legacyJobIdIssue) {
|
|
140
|
+
const resolvedId = typeof hydrated.id === "string" ? hydrated.id : void 0;
|
|
141
|
+
state.deps.log.warn({
|
|
142
|
+
storePath: state.deps.storePath,
|
|
143
|
+
jobId: resolvedId
|
|
144
|
+
}, "cron: job used legacy jobId field; normalized id in memory (run daocore doctor --fix to persist canonical shape)");
|
|
145
|
+
}
|
|
146
|
+
if (typeof hydrated.enabled !== "boolean") hydrated.enabled = true;
|
|
147
|
+
invalidateStaleNextRunOnScheduleChange({
|
|
148
|
+
previousJobsById,
|
|
149
|
+
hydrated
|
|
150
|
+
});
|
|
151
|
+
if (typeof hydrated.sessionTarget !== "string") {
|
|
152
|
+
const payload = hydrated.payload;
|
|
153
|
+
const payloadKind = payload && typeof payload === "object" && !Array.isArray(payload) && Object.hasOwn(payload, "kind") ? payload.kind : void 0;
|
|
154
|
+
let defaulted;
|
|
155
|
+
if (payloadKind === "systemEvent") defaulted = "main";
|
|
156
|
+
else if (payloadKind === "agentTurn") defaulted = "isolated";
|
|
157
|
+
if (defaulted) {
|
|
158
|
+
hydrated.sessionTarget = defaulted;
|
|
159
|
+
const jobId = typeof hydrated.id === "string" ? hydrated.id : void 0;
|
|
160
|
+
const dedupeKey = jobId ?? "<unknown>";
|
|
161
|
+
if (!state.warnedMissingSessionTargetJobIds.has(dedupeKey)) {
|
|
162
|
+
state.warnedMissingSessionTargetJobIds.add(dedupeKey);
|
|
163
|
+
state.deps.log.warn({
|
|
164
|
+
storePath: state.deps.storePath,
|
|
165
|
+
jobId,
|
|
166
|
+
defaulted
|
|
167
|
+
}, "cron: job missing sessionTarget; defaulted in memory (edit jobs.json to persist canonical shape)");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
state.store = {
|
|
173
|
+
version: 1,
|
|
174
|
+
jobs
|
|
175
|
+
};
|
|
176
|
+
state.storeLoadedAtMs = state.deps.nowMs();
|
|
177
|
+
state.storeFileMtimeMs = fileMtimeMs;
|
|
178
|
+
if (!opts?.skipRecompute) recomputeNextRuns(state);
|
|
179
|
+
}
|
|
180
|
+
function warnIfDisabled(state, action) {
|
|
181
|
+
if (state.deps.cronEnabled) return;
|
|
182
|
+
if (state.warnedDisabled) return;
|
|
183
|
+
state.warnedDisabled = true;
|
|
184
|
+
state.deps.log.warn({
|
|
185
|
+
enabled: false,
|
|
186
|
+
action,
|
|
187
|
+
storePath: state.deps.storePath
|
|
188
|
+
}, "cron: scheduler disabled; jobs will not run automatically");
|
|
189
|
+
}
|
|
190
|
+
async function persist(state, opts) {
|
|
191
|
+
if (!state.store) return;
|
|
192
|
+
await saveCronStore(state.deps.storePath, state.store, opts);
|
|
193
|
+
state.storeFileMtimeMs = await getFileMtimeMs(state.deps.storePath);
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/agents/pi-embedded-runner/execution-phase.ts
|
|
197
|
+
const EMBEDDED_AGENT_EXECUTION_PHASE_LABELS = {
|
|
198
|
+
runner_entered: "runner-entered",
|
|
199
|
+
workspace: "workspace",
|
|
200
|
+
runtime_plugins: "runtime-plugins",
|
|
201
|
+
before_agent_reply: "before-agent-reply",
|
|
202
|
+
model_resolution: "model-resolution",
|
|
203
|
+
auth: "auth",
|
|
204
|
+
context_engine: "context-engine",
|
|
205
|
+
attempt_dispatch: "attempt-dispatch",
|
|
206
|
+
context_assembled: "context-assembled",
|
|
207
|
+
turn_accepted: "turn-accepted",
|
|
208
|
+
process_spawned: "process-spawned",
|
|
209
|
+
tool_execution_started: "tool-execution-started",
|
|
210
|
+
assistant_output_started: "assistant-output-started",
|
|
211
|
+
model_call_started: "model-call-started"
|
|
212
|
+
};
|
|
213
|
+
function formatEmbeddedAgentExecutionPhase(phase) {
|
|
214
|
+
return phase ? EMBEDDED_AGENT_EXECUTION_PHASE_LABELS[phase] : void 0;
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/cron/retry-hint.ts
|
|
218
|
+
const TRANSIENT_PATTERNS = {
|
|
219
|
+
rate_limit: /(rate[_ ]limit|too many requests|429|resource has been exhausted|cloudflare|tokens per day)/i,
|
|
220
|
+
overloaded: /\b529\b|\boverloaded(?:_error)?\b|high demand|temporar(?:ily|y) overloaded|capacity exceeded/i,
|
|
221
|
+
network: /(network|fetch failed|socket|econnreset|econnrefused|eai_again|ehostunreach|ehostdown|enetreset|enetunreach|epipe)/i,
|
|
222
|
+
timeout: /(timeout|etimedout)/i,
|
|
223
|
+
server_error: /\b5\d{2}\b/
|
|
224
|
+
};
|
|
225
|
+
function resolveCronExecutionRetryHint(error, retryOn, classifiedReason) {
|
|
226
|
+
if (!error || typeof error !== "string") return { retryable: false };
|
|
227
|
+
const keys = retryOn?.length ? retryOn : Object.keys(TRANSIENT_PATTERNS);
|
|
228
|
+
const classified = classifiedReason ?? void 0;
|
|
229
|
+
if (classified && keys.includes(classified)) return {
|
|
230
|
+
retryable: true,
|
|
231
|
+
category: classified
|
|
232
|
+
};
|
|
233
|
+
for (const key of keys) if (TRANSIENT_PATTERNS[key]?.test(error)) return {
|
|
234
|
+
retryable: true,
|
|
235
|
+
category: key
|
|
236
|
+
};
|
|
237
|
+
return { retryable: false };
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/cron/session-reaper.ts
|
|
241
|
+
/**
|
|
242
|
+
* Cron session reaper — prunes completed isolated cron run sessions
|
|
243
|
+
* from the session store after a configurable retention period.
|
|
244
|
+
*
|
|
245
|
+
* Pattern: sessions keyed as `...:cron:<jobId>:run:<uuid>` are ephemeral
|
|
246
|
+
* run records. The base session (`...:cron:<jobId>`) is kept as-is.
|
|
247
|
+
*/
|
|
248
|
+
const DEFAULT_RETENTION_MS = 24 * 36e5;
|
|
249
|
+
/** Minimum interval between reaper sweeps (avoid running every timer tick). */
|
|
250
|
+
const MIN_SWEEP_INTERVAL_MS = 5 * 6e4;
|
|
251
|
+
const lastSweepAtMsByStore = /* @__PURE__ */ new Map();
|
|
252
|
+
function resolveRetentionMs(cronConfig) {
|
|
253
|
+
if (cronConfig?.sessionRetention === false) return null;
|
|
254
|
+
const raw = cronConfig?.sessionRetention;
|
|
255
|
+
if (typeof raw === "string" && raw.trim()) try {
|
|
256
|
+
return parseDurationMs(raw.trim(), { defaultUnit: "h" });
|
|
257
|
+
} catch {
|
|
258
|
+
return DEFAULT_RETENTION_MS;
|
|
259
|
+
}
|
|
260
|
+
return DEFAULT_RETENTION_MS;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Sweep the session store and prune expired cron run sessions.
|
|
264
|
+
* Designed to be called from the cron timer tick — self-throttles via
|
|
265
|
+
* MIN_SWEEP_INTERVAL_MS to avoid excessive I/O.
|
|
266
|
+
*
|
|
267
|
+
* Lock ordering: this function acquires the session-store file lock via
|
|
268
|
+
* `updateSessionStore`. It must be called OUTSIDE of the cron service's
|
|
269
|
+
* own `locked()` section to avoid lock-order inversions. The cron timer
|
|
270
|
+
* calls this after all `locked()` sections have been released.
|
|
271
|
+
*/
|
|
272
|
+
async function sweepCronRunSessions(params) {
|
|
273
|
+
const now = params.nowMs ?? Date.now();
|
|
274
|
+
const storePath = params.sessionStorePath;
|
|
275
|
+
const lastSweepAtMs = lastSweepAtMsByStore.get(storePath) ?? 0;
|
|
276
|
+
if (!params.force && now - lastSweepAtMs < MIN_SWEEP_INTERVAL_MS) return {
|
|
277
|
+
swept: false,
|
|
278
|
+
pruned: 0
|
|
279
|
+
};
|
|
280
|
+
const retentionMs = resolveRetentionMs(params.cronConfig);
|
|
281
|
+
if (retentionMs === null) {
|
|
282
|
+
lastSweepAtMsByStore.set(storePath, now);
|
|
283
|
+
return {
|
|
284
|
+
swept: false,
|
|
285
|
+
pruned: 0
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
let pruned = 0;
|
|
289
|
+
const prunedSessions = /* @__PURE__ */ new Map();
|
|
290
|
+
try {
|
|
291
|
+
await updateSessionStore(storePath, (store) => {
|
|
292
|
+
const cutoff = now - retentionMs;
|
|
293
|
+
for (const key of Object.keys(store)) {
|
|
294
|
+
if (!isCronRunSessionKey(key)) continue;
|
|
295
|
+
const entry = store[key];
|
|
296
|
+
if (!entry) continue;
|
|
297
|
+
if ((entry.updatedAt ?? 0) < cutoff) {
|
|
298
|
+
if (!prunedSessions.has(entry.sessionId) || entry.sessionFile) prunedSessions.set(entry.sessionId, entry.sessionFile);
|
|
299
|
+
delete store[key];
|
|
300
|
+
pruned++;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
} catch (err) {
|
|
305
|
+
params.log.warn({ err: String(err) }, "cron-reaper: failed to sweep session store");
|
|
306
|
+
return {
|
|
307
|
+
swept: false,
|
|
308
|
+
pruned: 0
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
lastSweepAtMsByStore.set(storePath, now);
|
|
312
|
+
if (prunedSessions.size > 0) try {
|
|
313
|
+
const store = loadSessionStore(storePath, { skipCache: true });
|
|
314
|
+
const archivedDirs = await archiveRemovedSessionTranscripts({
|
|
315
|
+
removedSessionFiles: prunedSessions,
|
|
316
|
+
referencedSessionIds: new Set(Object.values(store).map((entry) => entry?.sessionId).filter((id) => Boolean(id))),
|
|
317
|
+
storePath,
|
|
318
|
+
reason: "deleted",
|
|
319
|
+
restrictToStoreDir: true
|
|
320
|
+
});
|
|
321
|
+
if (archivedDirs.size > 0) await cleanupArchivedSessionTranscripts({
|
|
322
|
+
directories: [...archivedDirs],
|
|
323
|
+
olderThanMs: retentionMs,
|
|
324
|
+
reason: "deleted",
|
|
325
|
+
nowMs: now
|
|
326
|
+
});
|
|
327
|
+
} catch (err) {
|
|
328
|
+
params.log.warn({ err: String(err) }, "cron-reaper: transcript cleanup failed");
|
|
329
|
+
}
|
|
330
|
+
if (pruned > 0) params.log.info({
|
|
331
|
+
pruned,
|
|
332
|
+
retentionMs
|
|
333
|
+
}, `cron-reaper: pruned ${pruned} expired cron run session(s)`);
|
|
334
|
+
return {
|
|
335
|
+
swept: true,
|
|
336
|
+
pruned
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/cron/service/timeout-policy.ts
|
|
341
|
+
/**
|
|
342
|
+
* Maximum wall-clock time for a single job execution. Acts as a safety net
|
|
343
|
+
* on top of per-provider/per-agent timeouts to prevent one stuck job from
|
|
344
|
+
* wedging the entire cron lane.
|
|
345
|
+
*/
|
|
346
|
+
const DEFAULT_JOB_TIMEOUT_MS = 10 * 6e4;
|
|
347
|
+
/**
|
|
348
|
+
* Agent turns can legitimately run much longer than generic cron jobs.
|
|
349
|
+
* Use a larger safety ceiling when no explicit timeout is set.
|
|
350
|
+
*/
|
|
351
|
+
const AGENT_TURN_SAFETY_TIMEOUT_MS = 60 * 6e4;
|
|
352
|
+
function resolveCronJobTimeoutMs(job) {
|
|
353
|
+
const configuredTimeoutMs = job.payload.kind === "agentTurn" && typeof job.payload.timeoutSeconds === "number" ? Math.floor(job.payload.timeoutSeconds * 1e3) : void 0;
|
|
354
|
+
if (configuredTimeoutMs === void 0) return job.payload.kind === "agentTurn" ? AGENT_TURN_SAFETY_TIMEOUT_MS : DEFAULT_JOB_TIMEOUT_MS;
|
|
355
|
+
return configuredTimeoutMs <= 0 ? void 0 : configuredTimeoutMs;
|
|
356
|
+
}
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/cron/service/timer.ts
|
|
359
|
+
const MAX_TIMER_DELAY_MS = 6e4;
|
|
360
|
+
const CRON_TIMEOUT_CLEANUP_GUARD_MS = 2e4;
|
|
361
|
+
const CRON_AGENT_SETUP_WATCHDOG_MS = 6e4;
|
|
362
|
+
const CRON_AGENT_PRE_EXECUTION_WATCHDOG_MS = 6e4;
|
|
363
|
+
const CRON_AGENT_PRE_EXECUTION_MIN_WATCHDOG_MS = 1e3;
|
|
364
|
+
/**
|
|
365
|
+
* Minimum gap between consecutive fires of the same cron job. This is a
|
|
366
|
+
* safety net that prevents spin-loops when `computeJobNextRunAtMs` returns
|
|
367
|
+
* a value within the same second as the just-completed run. The guard
|
|
368
|
+
* is intentionally generous (2 s) so it never masks a legitimate schedule
|
|
369
|
+
* but always breaks an infinite re-trigger cycle. (See #17821)
|
|
370
|
+
*/
|
|
371
|
+
const MIN_REFIRE_GAP_MS = 2e3;
|
|
372
|
+
const DEFAULT_MISSED_JOB_STAGGER_MS = 5e3;
|
|
373
|
+
const DEFAULT_MAX_MISSED_JOBS_PER_RESTART = 5;
|
|
374
|
+
const DEFAULT_STARTUP_DEFERRED_MISSED_AGENT_JOB_DELAY_MS = 2 * 6e4;
|
|
375
|
+
const DEFAULT_FAILURE_ALERT_AFTER = 2;
|
|
376
|
+
const DEFAULT_FAILURE_ALERT_COOLDOWN_MS = 60 * 6e4;
|
|
377
|
+
const CRON_AGENT_PHASE_WATCHDOG_STAGE = {
|
|
378
|
+
runner_entered: "pre_execution",
|
|
379
|
+
workspace: "pre_execution",
|
|
380
|
+
runtime_plugins: "pre_execution",
|
|
381
|
+
before_agent_reply: "execution",
|
|
382
|
+
model_resolution: "pre_execution",
|
|
383
|
+
auth: "pre_execution",
|
|
384
|
+
context_engine: "pre_execution",
|
|
385
|
+
attempt_dispatch: "execution",
|
|
386
|
+
context_assembled: "execution",
|
|
387
|
+
turn_accepted: "execution",
|
|
388
|
+
process_spawned: "execution",
|
|
389
|
+
tool_execution_started: "execution",
|
|
390
|
+
assistant_output_started: "execution",
|
|
391
|
+
model_call_started: "execution"
|
|
392
|
+
};
|
|
393
|
+
async function executeJobCoreWithTimeout(state, job) {
|
|
394
|
+
const jobTimeoutMs = resolveCronJobTimeoutMs(job);
|
|
395
|
+
if (typeof jobTimeoutMs !== "number") return await executeJobCore(state, job);
|
|
396
|
+
const runAbortController = new AbortController();
|
|
397
|
+
let timeoutReason;
|
|
398
|
+
const timeoutMarker = Symbol("cron-timeout");
|
|
399
|
+
let resolveTimeout;
|
|
400
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
401
|
+
resolveTimeout = resolve;
|
|
402
|
+
});
|
|
403
|
+
const deferTimeoutUntilExecutionStart = job.sessionTarget !== "main" && job.payload.kind === "agentTurn";
|
|
404
|
+
const triggerTimeout = (reason) => {
|
|
405
|
+
if (runAbortController.signal.aborted) return;
|
|
406
|
+
timeoutReason = reason;
|
|
407
|
+
runAbortController.abort(reason);
|
|
408
|
+
resolveTimeout?.(timeoutMarker);
|
|
409
|
+
};
|
|
410
|
+
const watchdog = createCronAgentWatchdog({
|
|
411
|
+
deferUntilRunner: deferTimeoutUntilExecutionStart,
|
|
412
|
+
jobTimeoutMs,
|
|
413
|
+
triggerTimeout
|
|
414
|
+
});
|
|
415
|
+
const corePromise = executeJobCore(state, job, runAbortController.signal, {
|
|
416
|
+
onExecutionStarted: deferTimeoutUntilExecutionStart ? watchdog.noteRunnerStarted : void 0,
|
|
417
|
+
onExecutionPhase: deferTimeoutUntilExecutionStart ? watchdog.notePhase : void 0
|
|
418
|
+
});
|
|
419
|
+
watchdog.start();
|
|
420
|
+
corePromise.catch((err) => {
|
|
421
|
+
if (runAbortController.signal.aborted) state.deps.log.warn({
|
|
422
|
+
jobId: job.id,
|
|
423
|
+
err: String(err)
|
|
424
|
+
}, "cron: job core rejected after timeout abort");
|
|
425
|
+
});
|
|
426
|
+
try {
|
|
427
|
+
const first = await Promise.race([corePromise, timeoutPromise]);
|
|
428
|
+
if (first !== timeoutMarker) return first;
|
|
429
|
+
const activeExecution = watchdog.activeExecution();
|
|
430
|
+
await cleanupTimedOutCronAgentRun(state, job, jobTimeoutMs, activeExecution);
|
|
431
|
+
const error = timeoutReason ?? timeoutErrorMessage(activeExecution);
|
|
432
|
+
return {
|
|
433
|
+
status: "error",
|
|
434
|
+
error,
|
|
435
|
+
diagnostics: createCronRunDiagnosticsFromError("cron-setup", error, { nowMs: state.deps.nowMs })
|
|
436
|
+
};
|
|
437
|
+
} finally {
|
|
438
|
+
watchdog.dispose();
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function createCronAgentWatchdog(params) {
|
|
442
|
+
let state = params.deferUntilRunner ? "waiting_for_runner" : "executing";
|
|
443
|
+
let timeoutId;
|
|
444
|
+
let setupTimeoutId;
|
|
445
|
+
let preExecutionTimeoutId;
|
|
446
|
+
let activeExecution;
|
|
447
|
+
const setTimedOut = (reason) => {
|
|
448
|
+
if (state === "timed_out" || state === "disposed") return;
|
|
449
|
+
state = "timed_out";
|
|
450
|
+
params.triggerTimeout(reason);
|
|
451
|
+
};
|
|
452
|
+
const startTimeout = () => {
|
|
453
|
+
if (timeoutId || state === "disposed") return;
|
|
454
|
+
timeoutId = setTimeout(() => {
|
|
455
|
+
setTimedOut(timeoutErrorMessage(activeExecution));
|
|
456
|
+
}, params.jobTimeoutMs);
|
|
457
|
+
};
|
|
458
|
+
const clearSetupTimeout = () => {
|
|
459
|
+
if (!setupTimeoutId) return;
|
|
460
|
+
clearTimeout(setupTimeoutId);
|
|
461
|
+
setupTimeoutId = void 0;
|
|
462
|
+
};
|
|
463
|
+
const clearPreExecutionTimeout = () => {
|
|
464
|
+
if (!preExecutionTimeoutId) return;
|
|
465
|
+
clearTimeout(preExecutionTimeoutId);
|
|
466
|
+
preExecutionTimeoutId = void 0;
|
|
467
|
+
};
|
|
468
|
+
const startPreExecutionTimeout = () => {
|
|
469
|
+
if (preExecutionTimeoutId || state !== "waiting_for_execution") return;
|
|
470
|
+
preExecutionTimeoutId = setTimeout(() => {
|
|
471
|
+
if (state === "waiting_for_execution") setTimedOut(preExecutionTimeoutErrorMessage(activeExecution));
|
|
472
|
+
}, resolveCronAgentPreExecutionWatchdogMs(params.jobTimeoutMs));
|
|
473
|
+
};
|
|
474
|
+
const noteExecutionProgress = (info) => {
|
|
475
|
+
if (!info) return;
|
|
476
|
+
const previousPhase = activeExecution?.phase;
|
|
477
|
+
activeExecution = {
|
|
478
|
+
...activeExecution,
|
|
479
|
+
...info
|
|
480
|
+
};
|
|
481
|
+
const stage = info.phase ? CRON_AGENT_PHASE_WATCHDOG_STAGE[info.phase] : void 0;
|
|
482
|
+
if (state === "executing" && previousPhase === "before_agent_reply" && stage === "pre_execution") {
|
|
483
|
+
state = "waiting_for_execution";
|
|
484
|
+
startPreExecutionTimeout();
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
if (stage === "execution" || info.firstModelCallStarted) {
|
|
488
|
+
state = "executing";
|
|
489
|
+
clearPreExecutionTimeout();
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
return {
|
|
493
|
+
start: () => {
|
|
494
|
+
if (params.deferUntilRunner) {
|
|
495
|
+
setupTimeoutId = setTimeout(() => {
|
|
496
|
+
if (state === "waiting_for_runner") setTimedOut(setupTimeoutErrorMessage(activeExecution));
|
|
497
|
+
}, CRON_AGENT_SETUP_WATCHDOG_MS);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
startTimeout();
|
|
501
|
+
},
|
|
502
|
+
noteRunnerStarted: (info) => {
|
|
503
|
+
if (state === "disposed" || state === "timed_out") return;
|
|
504
|
+
clearSetupTimeout();
|
|
505
|
+
startTimeout();
|
|
506
|
+
if (state !== "executing") state = "waiting_for_execution";
|
|
507
|
+
noteExecutionProgress(info);
|
|
508
|
+
startPreExecutionTimeout();
|
|
509
|
+
},
|
|
510
|
+
notePhase: (info) => {
|
|
511
|
+
if (state === "disposed" || state === "timed_out") return;
|
|
512
|
+
noteExecutionProgress(info);
|
|
513
|
+
},
|
|
514
|
+
activeExecution: () => activeExecution,
|
|
515
|
+
dispose: () => {
|
|
516
|
+
state = "disposed";
|
|
517
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
518
|
+
clearSetupTimeout();
|
|
519
|
+
clearPreExecutionTimeout();
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
async function cleanupTimedOutCronAgentRun(state, job, timeoutMs, execution) {
|
|
524
|
+
if (!state.deps.cleanupTimedOutAgentRun) return;
|
|
525
|
+
let settleTimer;
|
|
526
|
+
const cleanupPromise = state.deps.cleanupTimedOutAgentRun({
|
|
527
|
+
job,
|
|
528
|
+
timeoutMs,
|
|
529
|
+
execution
|
|
530
|
+
});
|
|
531
|
+
const settleTimeout = new Promise((resolve) => {
|
|
532
|
+
settleTimer = setTimeout(resolve, CRON_TIMEOUT_CLEANUP_GUARD_MS);
|
|
533
|
+
});
|
|
534
|
+
try {
|
|
535
|
+
await Promise.race([cleanupPromise, settleTimeout]);
|
|
536
|
+
} catch (err) {
|
|
537
|
+
state.deps.log.warn({
|
|
538
|
+
jobId: job.id,
|
|
539
|
+
err: String(err)
|
|
540
|
+
}, "cron: timed-out agent cleanup failed");
|
|
541
|
+
} finally {
|
|
542
|
+
if (settleTimer) clearTimeout(settleTimer);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function resolveRunConcurrency(state) {
|
|
546
|
+
const raw = state.deps.cronConfig?.maxConcurrentRuns;
|
|
547
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return 1;
|
|
548
|
+
return Math.max(1, Math.floor(raw));
|
|
549
|
+
}
|
|
550
|
+
function timeoutErrorMessage(execution) {
|
|
551
|
+
const phase = formatCronAgentExecutionPhase(execution);
|
|
552
|
+
if (!phase) return "cron: job execution timed out";
|
|
553
|
+
return `cron: job execution timed out (last phase: ${phase})`;
|
|
554
|
+
}
|
|
555
|
+
function setupTimeoutErrorMessage(execution) {
|
|
556
|
+
const phase = formatCronAgentExecutionPhase(execution);
|
|
557
|
+
if (!phase) return "cron: isolated agent setup timed out before runner start";
|
|
558
|
+
return `cron: isolated agent setup timed out before runner start (last phase: ${phase})`;
|
|
559
|
+
}
|
|
560
|
+
function preExecutionTimeoutErrorMessage(execution) {
|
|
561
|
+
const phase = formatCronAgentExecutionPhase(execution);
|
|
562
|
+
if (!phase) return "cron: isolated agent run stalled before execution start";
|
|
563
|
+
return `cron: isolated agent run stalled before execution start (last phase: ${phase})`;
|
|
564
|
+
}
|
|
565
|
+
function formatCronAgentExecutionPhase(execution) {
|
|
566
|
+
return formatEmbeddedAgentExecutionPhase(execution?.phase);
|
|
567
|
+
}
|
|
568
|
+
function resolveCronAgentPreExecutionWatchdogMs(jobTimeoutMs) {
|
|
569
|
+
return Math.max(CRON_AGENT_PRE_EXECUTION_MIN_WATCHDOG_MS, Math.min(CRON_AGENT_PRE_EXECUTION_WATCHDOG_MS, Math.floor(jobTimeoutMs / 2)));
|
|
570
|
+
}
|
|
571
|
+
function abortErrorMessage(signal) {
|
|
572
|
+
const reason = signal?.reason;
|
|
573
|
+
if (typeof reason === "string" && reason.trim()) return reason.trim();
|
|
574
|
+
return timeoutErrorMessage();
|
|
575
|
+
}
|
|
576
|
+
function isAbortError(err) {
|
|
577
|
+
if (!(err instanceof Error)) return false;
|
|
578
|
+
return err.name === "AbortError" || err.message === timeoutErrorMessage();
|
|
579
|
+
}
|
|
580
|
+
function normalizeCronRunErrorText(err) {
|
|
581
|
+
if (isAbortError(err)) return timeoutErrorMessage();
|
|
582
|
+
if (typeof err === "string") return err === `Error: ${timeoutErrorMessage()}` ? timeoutErrorMessage() : err;
|
|
583
|
+
return String(err);
|
|
584
|
+
}
|
|
585
|
+
function normalizeCronLaneSegment(value, fallback) {
|
|
586
|
+
return normalizeOptionalLowercaseString(value)?.replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || fallback;
|
|
587
|
+
}
|
|
588
|
+
function resolveMainSessionCronRunSessionKey(job, startedAt) {
|
|
589
|
+
const explicitAgentId = job.agentId?.trim();
|
|
590
|
+
return `agent:${normalizeAgentId(explicitAgentId || resolveAgentIdFromSessionKey(job.sessionKey))}:cron:${normalizeCronLaneSegment(job.id, "job")}:run:${normalizeCronLaneSegment(String(Math.max(0, Math.floor(startedAt))), "run")}`;
|
|
591
|
+
}
|
|
592
|
+
function resolveMainSessionCronDeliveryContext(state, job) {
|
|
593
|
+
const targetSessionKey = job.sessionKey?.trim();
|
|
594
|
+
if (!targetSessionKey) return;
|
|
595
|
+
const explicitAgentId = job.agentId?.trim();
|
|
596
|
+
const agentId = normalizeAgentId(explicitAgentId || resolveAgentIdFromSessionKey(targetSessionKey));
|
|
597
|
+
const storePath = state.deps.resolveSessionStorePath?.(agentId) ?? state.deps.sessionStorePath;
|
|
598
|
+
if (!storePath) return;
|
|
599
|
+
try {
|
|
600
|
+
return deliveryContextFromSession(readSessionEntry(storePath, targetSessionKey));
|
|
601
|
+
} catch {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
function resolveCronTaskChildSessionKey(params) {
|
|
606
|
+
if (params.job.sessionTarget === "main") return resolveMainSessionCronRunSessionKey(params.job, params.startedAt);
|
|
607
|
+
const explicitSessionKey = params.job.sessionKey?.trim();
|
|
608
|
+
if (explicitSessionKey) return explicitSessionKey;
|
|
609
|
+
if (params.job.sessionTarget !== "isolated") return;
|
|
610
|
+
return resolveCronAgentSessionKey({
|
|
611
|
+
sessionKey: `cron:${params.job.id}`,
|
|
612
|
+
agentId: params.job.agentId ?? params.state.deps.defaultAgentId ?? "main"
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
function tryCreateCronTaskRun(params) {
|
|
616
|
+
const runId = createCronExecutionId(params.job.id, params.startedAt);
|
|
617
|
+
try {
|
|
618
|
+
createRunningTaskRun({
|
|
619
|
+
runtime: "cron",
|
|
620
|
+
sourceId: params.job.id,
|
|
621
|
+
ownerKey: "",
|
|
622
|
+
scopeKind: "system",
|
|
623
|
+
childSessionKey: resolveCronTaskChildSessionKey(params),
|
|
624
|
+
agentId: params.job.agentId,
|
|
625
|
+
runId,
|
|
626
|
+
label: params.job.name,
|
|
627
|
+
task: params.job.name || params.job.id,
|
|
628
|
+
deliveryStatus: "not_applicable",
|
|
629
|
+
notifyPolicy: "silent",
|
|
630
|
+
startedAt: params.startedAt,
|
|
631
|
+
lastEventAt: params.startedAt
|
|
632
|
+
});
|
|
633
|
+
return runId;
|
|
634
|
+
} catch (error) {
|
|
635
|
+
params.state.deps.log.warn({
|
|
636
|
+
jobId: params.job.id,
|
|
637
|
+
error
|
|
638
|
+
}, "cron: failed to create task ledger record");
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
function tryFinishCronTaskRun(state, result) {
|
|
643
|
+
if (!result.taskRunId) return;
|
|
644
|
+
try {
|
|
645
|
+
if (result.status === "ok" || result.status === "skipped") {
|
|
646
|
+
completeTaskRunByRunId({
|
|
647
|
+
runId: result.taskRunId,
|
|
648
|
+
runtime: "cron",
|
|
649
|
+
endedAt: result.endedAt,
|
|
650
|
+
lastEventAt: result.endedAt,
|
|
651
|
+
terminalSummary: result.summary ?? void 0
|
|
652
|
+
});
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
failTaskRunByRunId({
|
|
656
|
+
runId: result.taskRunId,
|
|
657
|
+
runtime: "cron",
|
|
658
|
+
status: normalizeCronRunErrorText(result.error) === timeoutErrorMessage() ? "timed_out" : "failed",
|
|
659
|
+
endedAt: result.endedAt,
|
|
660
|
+
lastEventAt: result.endedAt,
|
|
661
|
+
error: result.status === "error" ? normalizeCronRunErrorText(result.error) : void 0,
|
|
662
|
+
terminalSummary: result.summary ?? void 0
|
|
663
|
+
});
|
|
664
|
+
} catch (error) {
|
|
665
|
+
state.deps.log.warn({
|
|
666
|
+
runId: result.taskRunId,
|
|
667
|
+
jobStatus: result.status,
|
|
668
|
+
error
|
|
669
|
+
}, "cron: failed to update task ledger record");
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/** Default max retries for one-shot jobs on transient errors (#24355). */
|
|
673
|
+
const DEFAULT_MAX_TRANSIENT_RETRIES = 3;
|
|
674
|
+
function resolveCronNextRunWithLowerBound(params) {
|
|
675
|
+
if (params.naturalNext === void 0) {
|
|
676
|
+
params.state.deps.log.warn({
|
|
677
|
+
jobId: params.job.id,
|
|
678
|
+
jobName: params.job.name,
|
|
679
|
+
context: params.context
|
|
680
|
+
}, "cron: next run unresolved; clearing schedule to avoid a refire loop");
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
return Math.max(params.naturalNext, params.lowerBoundMs);
|
|
684
|
+
}
|
|
685
|
+
function resolveRetryConfig(cronConfig) {
|
|
686
|
+
const retry = cronConfig?.retry;
|
|
687
|
+
return {
|
|
688
|
+
maxAttempts: typeof retry?.maxAttempts === "number" ? retry.maxAttempts : DEFAULT_MAX_TRANSIENT_RETRIES,
|
|
689
|
+
backoffMs: Array.isArray(retry?.backoffMs) && retry.backoffMs.length > 0 ? retry.backoffMs : DEFAULT_ERROR_BACKOFF_SCHEDULE_MS.slice(0, 3),
|
|
690
|
+
retryOn: Array.isArray(retry?.retryOn) && retry.retryOn.length > 0 ? retry.retryOn : void 0
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
function resolveDeliveryState(params) {
|
|
694
|
+
const primaryDeliveryRequested = resolveCronDeliveryPlan(params.job).requested;
|
|
695
|
+
const alternateFailureNotificationRequested = params.runStatus === "error" && params.job.delivery?.bestEffort !== true && resolveFailureDestination(params.job, params.globalFailureDestination) !== null;
|
|
696
|
+
if (!primaryDeliveryRequested) return {
|
|
697
|
+
status: "not-requested",
|
|
698
|
+
failureNotification: { status: alternateFailureNotificationRequested ? "unknown" : "not-requested" }
|
|
699
|
+
};
|
|
700
|
+
if (params.runStatus === "error") {
|
|
701
|
+
const failureNotification = alternateFailureNotificationRequested ? { status: "unknown" } : { status: "delivered" };
|
|
702
|
+
if (params.delivered === true) return {
|
|
703
|
+
delivered: false,
|
|
704
|
+
status: "not-delivered",
|
|
705
|
+
error: params.error,
|
|
706
|
+
failureNotification: alternateFailureNotificationRequested ? failureNotification : {
|
|
707
|
+
delivered: true,
|
|
708
|
+
status: "delivered"
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
if (params.delivered === false) return {
|
|
712
|
+
delivered: false,
|
|
713
|
+
status: "not-delivered",
|
|
714
|
+
error: params.error,
|
|
715
|
+
failureNotification: alternateFailureNotificationRequested ? failureNotification : {
|
|
716
|
+
delivered: false,
|
|
717
|
+
status: "not-delivered",
|
|
718
|
+
...params.error ? { error: params.error } : {}
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
return {
|
|
722
|
+
status: "unknown",
|
|
723
|
+
error: params.error,
|
|
724
|
+
failureNotification: { status: "unknown" }
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
if (params.delivered === true) return {
|
|
728
|
+
delivered: true,
|
|
729
|
+
status: "delivered",
|
|
730
|
+
failureNotification: { status: "not-requested" }
|
|
731
|
+
};
|
|
732
|
+
if (params.delivered === false) return {
|
|
733
|
+
delivered: false,
|
|
734
|
+
status: "not-delivered",
|
|
735
|
+
error: params.error,
|
|
736
|
+
failureNotification: { status: "not-requested" }
|
|
737
|
+
};
|
|
738
|
+
return {
|
|
739
|
+
status: "unknown",
|
|
740
|
+
failureNotification: { status: "not-requested" }
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function failureNotificationDeliveryFromJobState(job) {
|
|
744
|
+
const status = job.state.lastFailureNotificationDeliveryStatus;
|
|
745
|
+
if (!status || status === "not-requested") return;
|
|
746
|
+
return {
|
|
747
|
+
delivered: job.state.lastFailureNotificationDelivered,
|
|
748
|
+
status,
|
|
749
|
+
error: job.state.lastFailureNotificationDeliveryError
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
function normalizeCronMessageChannel(input) {
|
|
753
|
+
const channel = normalizeOptionalLowercaseString(input);
|
|
754
|
+
return channel ? channel : void 0;
|
|
755
|
+
}
|
|
756
|
+
function normalizeTo(input) {
|
|
757
|
+
if (typeof input !== "string") return;
|
|
758
|
+
const to = input.trim();
|
|
759
|
+
return to ? to : void 0;
|
|
760
|
+
}
|
|
761
|
+
function clampPositiveInt(value, fallback) {
|
|
762
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
763
|
+
const floored = Math.floor(value);
|
|
764
|
+
return floored >= 1 ? floored : fallback;
|
|
765
|
+
}
|
|
766
|
+
function clampNonNegativeInt(value, fallback) {
|
|
767
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
768
|
+
const floored = Math.floor(value);
|
|
769
|
+
return floored >= 0 ? floored : fallback;
|
|
770
|
+
}
|
|
771
|
+
function resolveFailureAlert(state, job) {
|
|
772
|
+
const globalConfig = state.deps.cronConfig?.failureAlert;
|
|
773
|
+
const jobConfig = job.failureAlert === false ? void 0 : job.failureAlert;
|
|
774
|
+
if (job.failureAlert === false) return null;
|
|
775
|
+
if (!jobConfig && globalConfig?.enabled !== true) return null;
|
|
776
|
+
const mode = jobConfig?.mode ?? globalConfig?.mode;
|
|
777
|
+
const explicitTo = normalizeTo(jobConfig?.to);
|
|
778
|
+
return {
|
|
779
|
+
after: clampPositiveInt(jobConfig?.after ?? globalConfig?.after, DEFAULT_FAILURE_ALERT_AFTER),
|
|
780
|
+
cooldownMs: clampNonNegativeInt(jobConfig?.cooldownMs ?? globalConfig?.cooldownMs, DEFAULT_FAILURE_ALERT_COOLDOWN_MS),
|
|
781
|
+
channel: normalizeCronMessageChannel(jobConfig?.channel) ?? normalizeCronMessageChannel(job.delivery?.channel) ?? "last",
|
|
782
|
+
to: mode === "webhook" ? explicitTo : explicitTo ?? normalizeTo(job.delivery?.to),
|
|
783
|
+
mode,
|
|
784
|
+
accountId: jobConfig?.accountId ?? globalConfig?.accountId,
|
|
785
|
+
includeSkipped: jobConfig?.includeSkipped ?? globalConfig?.includeSkipped ?? false
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
function emitFailureAlert(state, params) {
|
|
789
|
+
const safeJobName = params.job.name || params.job.id;
|
|
790
|
+
const truncatedError = (params.error?.trim() || "unknown reason").slice(0, 200);
|
|
791
|
+
const statusVerb = params.status === "skipped" ? "skipped" : "failed";
|
|
792
|
+
const detailLabel = params.status === "skipped" ? "Skip reason" : "Last error";
|
|
793
|
+
const text = [`Cron job "${safeJobName}" ${statusVerb} ${params.consecutiveErrors} times`, `${detailLabel}: ${truncatedError}`].join("\n");
|
|
794
|
+
if (state.deps.sendCronFailureAlert) {
|
|
795
|
+
state.deps.sendCronFailureAlert({
|
|
796
|
+
job: params.job,
|
|
797
|
+
text,
|
|
798
|
+
channel: params.channel,
|
|
799
|
+
to: params.to,
|
|
800
|
+
mode: params.mode,
|
|
801
|
+
accountId: params.accountId
|
|
802
|
+
}).catch((err) => {
|
|
803
|
+
state.deps.log.warn({
|
|
804
|
+
jobId: params.job.id,
|
|
805
|
+
err: String(err)
|
|
806
|
+
}, "cron: failure alert delivery failed");
|
|
807
|
+
});
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
state.deps.enqueueSystemEvent(text, { agentId: params.job.agentId });
|
|
811
|
+
if (params.job.wakeMode === "now") state.deps.requestHeartbeat({
|
|
812
|
+
source: "cron",
|
|
813
|
+
intent: "immediate",
|
|
814
|
+
reason: `cron:${params.job.id}:failure-alert`
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function maybeEmitFailureAlert(state, params) {
|
|
818
|
+
if (!params.alertConfig || params.consecutiveCount < params.alertConfig.after) return;
|
|
819
|
+
if (params.job.delivery?.bestEffort === true) return;
|
|
820
|
+
const now = state.deps.nowMs();
|
|
821
|
+
const lastAlert = params.job.state.lastFailureAlertAtMs;
|
|
822
|
+
if (typeof lastAlert === "number" && now - lastAlert < Math.max(0, params.alertConfig.cooldownMs)) return;
|
|
823
|
+
emitFailureAlert(state, {
|
|
824
|
+
job: params.job,
|
|
825
|
+
error: params.error,
|
|
826
|
+
consecutiveErrors: params.consecutiveCount,
|
|
827
|
+
channel: params.alertConfig.channel,
|
|
828
|
+
to: params.alertConfig.to,
|
|
829
|
+
mode: params.alertConfig.mode,
|
|
830
|
+
accountId: params.alertConfig.accountId,
|
|
831
|
+
status: params.status
|
|
832
|
+
});
|
|
833
|
+
params.job.state.lastFailureAlertAtMs = now;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Apply the result of a job execution to the job's state.
|
|
837
|
+
* Handles consecutive error tracking, exponential backoff, one-shot disable,
|
|
838
|
+
* and nextRunAtMs computation. Returns `true` if the job should be deleted.
|
|
839
|
+
*/
|
|
840
|
+
function applyJobResult(state, job, result, opts) {
|
|
841
|
+
const prevLastRunAtMs = job.state.lastRunAtMs;
|
|
842
|
+
const computeNextWithPreservedLastRun = (nowMs) => {
|
|
843
|
+
const saved = job.state.lastRunAtMs;
|
|
844
|
+
job.state.lastRunAtMs = prevLastRunAtMs;
|
|
845
|
+
try {
|
|
846
|
+
return computeJobNextRunAtMs(job, nowMs);
|
|
847
|
+
} finally {
|
|
848
|
+
job.state.lastRunAtMs = saved;
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
job.state.runningAtMs = void 0;
|
|
852
|
+
job.state.lastRunAtMs = result.startedAt;
|
|
853
|
+
job.state.lastRunStatus = result.status;
|
|
854
|
+
job.state.lastStatus = result.status;
|
|
855
|
+
job.state.lastDurationMs = Math.max(0, result.endedAt - result.startedAt);
|
|
856
|
+
job.state.lastError = result.error;
|
|
857
|
+
job.state.lastDiagnostics = normalizeCronRunDiagnostics(result.diagnostics);
|
|
858
|
+
job.state.lastDiagnosticSummary = summarizeCronRunDiagnostics(job.state.lastDiagnostics);
|
|
859
|
+
job.state.lastErrorReason = result.status === "error" && typeof result.error === "string" ? resolveFailoverReasonFromError(result.error) ?? void 0 : void 0;
|
|
860
|
+
if (result.status === "error") state.deps.log.warn({
|
|
861
|
+
jobId: job.id,
|
|
862
|
+
jobName: job.name,
|
|
863
|
+
error: result.error,
|
|
864
|
+
diagnosticsSummary: job.state.lastDiagnosticSummary
|
|
865
|
+
}, "cron: job run returned error status");
|
|
866
|
+
const deliveryState = resolveDeliveryState({
|
|
867
|
+
job,
|
|
868
|
+
runStatus: result.status,
|
|
869
|
+
delivered: result.delivered,
|
|
870
|
+
error: result.error,
|
|
871
|
+
globalFailureDestination: state.deps.cronConfig?.failureDestination
|
|
872
|
+
});
|
|
873
|
+
job.state.lastDelivered = deliveryState.delivered;
|
|
874
|
+
job.state.lastDeliveryStatus = deliveryState.status;
|
|
875
|
+
job.state.lastDeliveryError = deliveryState.status === "not-delivered" && deliveryState.error ? deliveryState.error : void 0;
|
|
876
|
+
job.state.lastFailureNotificationDelivered = deliveryState.failureNotification.delivered;
|
|
877
|
+
job.state.lastFailureNotificationDeliveryStatus = deliveryState.failureNotification.status;
|
|
878
|
+
job.state.lastFailureNotificationDeliveryError = deliveryState.failureNotification.error;
|
|
879
|
+
job.updatedAtMs = result.endedAt;
|
|
880
|
+
const alertConfig = resolveFailureAlert(state, job);
|
|
881
|
+
if (result.status === "error") {
|
|
882
|
+
job.state.consecutiveErrors = (job.state.consecutiveErrors ?? 0) + 1;
|
|
883
|
+
job.state.consecutiveSkipped = 0;
|
|
884
|
+
maybeEmitFailureAlert(state, {
|
|
885
|
+
job,
|
|
886
|
+
alertConfig,
|
|
887
|
+
status: "error",
|
|
888
|
+
error: result.error,
|
|
889
|
+
consecutiveCount: job.state.consecutiveErrors
|
|
890
|
+
});
|
|
891
|
+
} else if (result.status === "skipped") {
|
|
892
|
+
job.state.consecutiveErrors = 0;
|
|
893
|
+
job.state.consecutiveSkipped = (job.state.consecutiveSkipped ?? 0) + 1;
|
|
894
|
+
if (alertConfig?.includeSkipped) maybeEmitFailureAlert(state, {
|
|
895
|
+
job,
|
|
896
|
+
alertConfig,
|
|
897
|
+
status: "skipped",
|
|
898
|
+
error: result.error,
|
|
899
|
+
consecutiveCount: job.state.consecutiveSkipped
|
|
900
|
+
});
|
|
901
|
+
else job.state.lastFailureAlertAtMs = void 0;
|
|
902
|
+
} else {
|
|
903
|
+
job.state.consecutiveErrors = 0;
|
|
904
|
+
job.state.consecutiveSkipped = 0;
|
|
905
|
+
job.state.lastFailureAlertAtMs = void 0;
|
|
906
|
+
}
|
|
907
|
+
const shouldDelete = job.schedule.kind === "at" && job.deleteAfterRun === true && result.status === "ok";
|
|
908
|
+
if (!shouldDelete) if (job.schedule.kind === "at") {
|
|
909
|
+
if (result.status === "ok" || result.status === "skipped") {
|
|
910
|
+
job.enabled = false;
|
|
911
|
+
job.state.nextRunAtMs = void 0;
|
|
912
|
+
} else if (result.status === "error") {
|
|
913
|
+
const retryConfig = resolveRetryConfig(state.deps.cronConfig);
|
|
914
|
+
const retryHint = resolveCronExecutionRetryHint(result.error, retryConfig.retryOn, job.state.lastErrorReason);
|
|
915
|
+
const consecutive = job.state.consecutiveErrors;
|
|
916
|
+
if (retryHint.retryable && consecutive <= retryConfig.maxAttempts) {
|
|
917
|
+
const backoff = errorBackoffMs(consecutive, retryConfig.backoffMs);
|
|
918
|
+
job.state.nextRunAtMs = result.endedAt + backoff;
|
|
919
|
+
state.deps.log.info({
|
|
920
|
+
jobId: job.id,
|
|
921
|
+
jobName: job.name,
|
|
922
|
+
consecutiveErrors: consecutive,
|
|
923
|
+
backoffMs: backoff,
|
|
924
|
+
nextRunAtMs: job.state.nextRunAtMs
|
|
925
|
+
}, "cron: scheduling one-shot retry after transient error");
|
|
926
|
+
} else {
|
|
927
|
+
job.enabled = false;
|
|
928
|
+
job.state.nextRunAtMs = void 0;
|
|
929
|
+
state.deps.log.warn({
|
|
930
|
+
jobId: job.id,
|
|
931
|
+
jobName: job.name,
|
|
932
|
+
consecutiveErrors: consecutive,
|
|
933
|
+
error: result.error,
|
|
934
|
+
reason: retryHint.retryable ? "max retries exhausted" : "permanent error",
|
|
935
|
+
retryCategory: retryHint.category
|
|
936
|
+
}, "cron: disabling one-shot job after error");
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
} else if (result.status === "error" && isJobEnabled(job)) {
|
|
940
|
+
const backoff = errorBackoffMs(job.state.consecutiveErrors ?? 1);
|
|
941
|
+
let normalNext;
|
|
942
|
+
try {
|
|
943
|
+
normalNext = opts?.preserveSchedule && job.schedule.kind === "every" ? computeNextWithPreservedLastRun(result.endedAt) : computeJobNextRunAtMs(job, result.endedAt);
|
|
944
|
+
} catch (err) {
|
|
945
|
+
recordScheduleComputeError({
|
|
946
|
+
state,
|
|
947
|
+
job,
|
|
948
|
+
err
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
const backoffNext = result.endedAt + backoff;
|
|
952
|
+
job.state.nextRunAtMs = job.schedule.kind === "cron" ? resolveCronNextRunWithLowerBound({
|
|
953
|
+
state,
|
|
954
|
+
job,
|
|
955
|
+
naturalNext: normalNext,
|
|
956
|
+
lowerBoundMs: backoffNext,
|
|
957
|
+
context: "error_backoff"
|
|
958
|
+
}) : normalNext !== void 0 ? Math.max(normalNext, backoffNext) : backoffNext;
|
|
959
|
+
state.deps.log.info({
|
|
960
|
+
jobId: job.id,
|
|
961
|
+
consecutiveErrors: job.state.consecutiveErrors,
|
|
962
|
+
backoffMs: backoff,
|
|
963
|
+
nextRunAtMs: job.state.nextRunAtMs
|
|
964
|
+
}, "cron: applying error backoff");
|
|
965
|
+
} else if (isJobEnabled(job)) {
|
|
966
|
+
let naturalNext;
|
|
967
|
+
try {
|
|
968
|
+
naturalNext = opts?.preserveSchedule && job.schedule.kind === "every" ? computeNextWithPreservedLastRun(result.endedAt) : computeJobNextRunAtMs(job, result.endedAt);
|
|
969
|
+
} catch (err) {
|
|
970
|
+
recordScheduleComputeError({
|
|
971
|
+
state,
|
|
972
|
+
job,
|
|
973
|
+
err
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
if (job.schedule.kind === "cron") {
|
|
977
|
+
const minNext = result.endedAt + MIN_REFIRE_GAP_MS;
|
|
978
|
+
job.state.nextRunAtMs = resolveCronNextRunWithLowerBound({
|
|
979
|
+
state,
|
|
980
|
+
job,
|
|
981
|
+
naturalNext,
|
|
982
|
+
lowerBoundMs: minNext,
|
|
983
|
+
context: "completion"
|
|
984
|
+
});
|
|
985
|
+
} else job.state.nextRunAtMs = naturalNext;
|
|
986
|
+
} else job.state.nextRunAtMs = void 0;
|
|
987
|
+
return shouldDelete;
|
|
988
|
+
}
|
|
989
|
+
function applyOutcomeToStoredJob(state, result) {
|
|
990
|
+
clearCronJobActive(result.jobId);
|
|
991
|
+
tryFinishCronTaskRun(state, result);
|
|
992
|
+
const store = state.store;
|
|
993
|
+
if (!store) return;
|
|
994
|
+
const jobs = store.jobs;
|
|
995
|
+
const job = jobs.find((entry) => entry.id === result.jobId);
|
|
996
|
+
if (!job) {
|
|
997
|
+
if (result.status === "ok") {
|
|
998
|
+
applyJobResult(state, result.job, {
|
|
999
|
+
status: result.status,
|
|
1000
|
+
error: result.error,
|
|
1001
|
+
diagnostics: result.diagnostics,
|
|
1002
|
+
delivered: result.delivered,
|
|
1003
|
+
startedAt: result.startedAt,
|
|
1004
|
+
endedAt: result.endedAt
|
|
1005
|
+
});
|
|
1006
|
+
emitJobFinished(state, result.job, result, result.startedAt);
|
|
1007
|
+
state.deps.log.info({ jobId: result.jobId }, "cron: finalized successful run after job was removed during execution");
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
state.deps.log.warn({ jobId: result.jobId }, "cron: applyOutcomeToStoredJob — job not found after forceReload, result discarded");
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
const shouldDelete = applyJobResult(state, job, {
|
|
1014
|
+
status: result.status,
|
|
1015
|
+
error: result.error,
|
|
1016
|
+
diagnostics: result.diagnostics,
|
|
1017
|
+
delivered: result.delivered,
|
|
1018
|
+
startedAt: result.startedAt,
|
|
1019
|
+
endedAt: result.endedAt
|
|
1020
|
+
});
|
|
1021
|
+
emitJobFinished(state, job, result, result.startedAt);
|
|
1022
|
+
if (shouldDelete) {
|
|
1023
|
+
store.jobs = jobs.filter((entry) => entry.id !== job.id);
|
|
1024
|
+
emit(state, {
|
|
1025
|
+
jobId: job.id,
|
|
1026
|
+
action: "removed",
|
|
1027
|
+
job
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function armTimer(state) {
|
|
1032
|
+
if (state.timer) clearTimeout(state.timer);
|
|
1033
|
+
state.timer = null;
|
|
1034
|
+
if (!state.deps.cronEnabled) {
|
|
1035
|
+
state.deps.log.debug({}, "cron: armTimer skipped - scheduler disabled");
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
const nextAt = nextWakeAtMs(state);
|
|
1039
|
+
if (!nextAt) {
|
|
1040
|
+
const jobCount = state.store?.jobs.length ?? 0;
|
|
1041
|
+
const enabledCount = state.store?.jobs.filter((j) => j.enabled).length ?? 0;
|
|
1042
|
+
const withNextRun = state.store?.jobs.filter((j) => j.enabled && hasScheduledNextRunAtMs(j.state.nextRunAtMs)).length ?? 0;
|
|
1043
|
+
if (enabledCount > 0) {
|
|
1044
|
+
armRunningRecheckTimer(state);
|
|
1045
|
+
state.deps.log.debug({
|
|
1046
|
+
jobCount,
|
|
1047
|
+
enabledCount,
|
|
1048
|
+
withNextRun,
|
|
1049
|
+
delayMs: MAX_TIMER_DELAY_MS
|
|
1050
|
+
}, "cron: timer armed for maintenance recheck");
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
state.deps.log.debug({
|
|
1054
|
+
jobCount,
|
|
1055
|
+
enabledCount,
|
|
1056
|
+
withNextRun
|
|
1057
|
+
}, "cron: armTimer skipped - no jobs with nextRunAtMs");
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
const now = state.deps.nowMs();
|
|
1061
|
+
const delay = Math.max(nextAt - now, 0);
|
|
1062
|
+
const clampedDelay = Math.min(delay === 0 ? MIN_REFIRE_GAP_MS : delay, MAX_TIMER_DELAY_MS);
|
|
1063
|
+
state.timer = setTimeout(() => {
|
|
1064
|
+
onTimer(state).catch((err) => {
|
|
1065
|
+
state.deps.log.error({ err: String(err) }, "cron: timer tick failed");
|
|
1066
|
+
});
|
|
1067
|
+
}, clampedDelay);
|
|
1068
|
+
state.deps.log.debug({
|
|
1069
|
+
nextAt,
|
|
1070
|
+
delayMs: clampedDelay,
|
|
1071
|
+
clamped: delay > MAX_TIMER_DELAY_MS
|
|
1072
|
+
}, "cron: timer armed");
|
|
1073
|
+
}
|
|
1074
|
+
function armRunningRecheckTimer(state) {
|
|
1075
|
+
if (state.timer) clearTimeout(state.timer);
|
|
1076
|
+
state.timer = setTimeout(() => {
|
|
1077
|
+
onTimer(state).catch((err) => {
|
|
1078
|
+
state.deps.log.error({ err: String(err) }, "cron: timer tick failed");
|
|
1079
|
+
});
|
|
1080
|
+
}, MAX_TIMER_DELAY_MS);
|
|
1081
|
+
}
|
|
1082
|
+
async function onTimer(state) {
|
|
1083
|
+
if (state.running) {
|
|
1084
|
+
armRunningRecheckTimer(state);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
state.running = true;
|
|
1088
|
+
armRunningRecheckTimer(state);
|
|
1089
|
+
try {
|
|
1090
|
+
const dueJobs = await locked(state, async () => {
|
|
1091
|
+
await ensureLoaded(state, {
|
|
1092
|
+
forceReload: true,
|
|
1093
|
+
skipRecompute: true
|
|
1094
|
+
});
|
|
1095
|
+
const dueCheckNow = state.deps.nowMs();
|
|
1096
|
+
const due = collectRunnableJobs(state, dueCheckNow);
|
|
1097
|
+
if (due.length === 0) {
|
|
1098
|
+
if (recomputeNextRunsForMaintenance(state, {
|
|
1099
|
+
recomputeExpired: true,
|
|
1100
|
+
nowMs: dueCheckNow
|
|
1101
|
+
})) await persist(state);
|
|
1102
|
+
return [];
|
|
1103
|
+
}
|
|
1104
|
+
const now = state.deps.nowMs();
|
|
1105
|
+
for (const job of due) {
|
|
1106
|
+
job.state.runningAtMs = now;
|
|
1107
|
+
job.state.lastError = void 0;
|
|
1108
|
+
}
|
|
1109
|
+
await persist(state);
|
|
1110
|
+
return due.map((j) => ({
|
|
1111
|
+
id: j.id,
|
|
1112
|
+
job: j
|
|
1113
|
+
}));
|
|
1114
|
+
});
|
|
1115
|
+
const runDueJob = async (params) => {
|
|
1116
|
+
const { id, job } = params;
|
|
1117
|
+
const startedAt = state.deps.nowMs();
|
|
1118
|
+
job.state.runningAtMs = startedAt;
|
|
1119
|
+
markCronJobActive(job.id);
|
|
1120
|
+
emit(state, {
|
|
1121
|
+
jobId: job.id,
|
|
1122
|
+
action: "started",
|
|
1123
|
+
job,
|
|
1124
|
+
runAtMs: startedAt
|
|
1125
|
+
});
|
|
1126
|
+
const jobTimeoutMs = resolveCronJobTimeoutMs(job);
|
|
1127
|
+
const taskRunId = tryCreateCronTaskRun({
|
|
1128
|
+
state,
|
|
1129
|
+
job,
|
|
1130
|
+
startedAt
|
|
1131
|
+
});
|
|
1132
|
+
try {
|
|
1133
|
+
return {
|
|
1134
|
+
jobId: id,
|
|
1135
|
+
job,
|
|
1136
|
+
taskRunId,
|
|
1137
|
+
...await executeJobCoreWithTimeout(state, job),
|
|
1138
|
+
startedAt,
|
|
1139
|
+
endedAt: state.deps.nowMs()
|
|
1140
|
+
};
|
|
1141
|
+
} catch (err) {
|
|
1142
|
+
const errorText = normalizeCronRunErrorText(err);
|
|
1143
|
+
state.deps.log.warn({
|
|
1144
|
+
jobId: id,
|
|
1145
|
+
jobName: job.name,
|
|
1146
|
+
timeoutMs: jobTimeoutMs ?? null
|
|
1147
|
+
}, `cron: job failed: ${errorText}`);
|
|
1148
|
+
return {
|
|
1149
|
+
jobId: id,
|
|
1150
|
+
job,
|
|
1151
|
+
taskRunId,
|
|
1152
|
+
status: "error",
|
|
1153
|
+
error: errorText,
|
|
1154
|
+
diagnostics: createCronRunDiagnosticsFromError("cron-setup", errorText, { nowMs: state.deps.nowMs }),
|
|
1155
|
+
startedAt,
|
|
1156
|
+
endedAt: state.deps.nowMs()
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
const concurrency = Math.min(resolveRunConcurrency(state), Math.max(1, dueJobs.length));
|
|
1161
|
+
const results = Array.from({ length: dueJobs.length });
|
|
1162
|
+
let cursor = 0;
|
|
1163
|
+
const workers = Array.from({ length: concurrency }, async () => {
|
|
1164
|
+
for (;;) {
|
|
1165
|
+
const index = cursor++;
|
|
1166
|
+
if (index >= dueJobs.length) return;
|
|
1167
|
+
const due = dueJobs[index];
|
|
1168
|
+
if (!due) return;
|
|
1169
|
+
results[index] = await runDueJob(due);
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
await Promise.all(workers);
|
|
1173
|
+
const completedResults = results.filter((entry) => entry !== void 0);
|
|
1174
|
+
if (completedResults.length > 0) await locked(state, async () => {
|
|
1175
|
+
await ensureLoaded(state, {
|
|
1176
|
+
forceReload: true,
|
|
1177
|
+
skipRecompute: true
|
|
1178
|
+
});
|
|
1179
|
+
for (const result of completedResults) applyOutcomeToStoredJob(state, result);
|
|
1180
|
+
recomputeNextRunsForMaintenance(state);
|
|
1181
|
+
await persist(state);
|
|
1182
|
+
});
|
|
1183
|
+
} finally {
|
|
1184
|
+
const storePaths = /* @__PURE__ */ new Set();
|
|
1185
|
+
if (state.deps.resolveSessionStorePath) {
|
|
1186
|
+
const defaultAgentId = state.deps.defaultAgentId ?? "main";
|
|
1187
|
+
if (state.store?.jobs?.length) for (const job of state.store.jobs) {
|
|
1188
|
+
const agentId = typeof job.agentId === "string" && job.agentId.trim() ? job.agentId : defaultAgentId;
|
|
1189
|
+
storePaths.add(state.deps.resolveSessionStorePath(agentId));
|
|
1190
|
+
}
|
|
1191
|
+
else storePaths.add(state.deps.resolveSessionStorePath(defaultAgentId));
|
|
1192
|
+
} else if (state.deps.sessionStorePath) storePaths.add(state.deps.sessionStorePath);
|
|
1193
|
+
if (storePaths.size > 0) {
|
|
1194
|
+
const nowMs = state.deps.nowMs();
|
|
1195
|
+
for (const storePath of storePaths) try {
|
|
1196
|
+
await sweepCronRunSessions({
|
|
1197
|
+
cronConfig: state.deps.cronConfig,
|
|
1198
|
+
sessionStorePath: storePath,
|
|
1199
|
+
nowMs,
|
|
1200
|
+
log: state.deps.log
|
|
1201
|
+
});
|
|
1202
|
+
} catch (err) {
|
|
1203
|
+
state.deps.log.warn({
|
|
1204
|
+
err: String(err),
|
|
1205
|
+
storePath
|
|
1206
|
+
}, "cron: session reaper sweep failed");
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
state.running = false;
|
|
1210
|
+
armTimer(state);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function isRunnableJob(params) {
|
|
1214
|
+
const { job, nowMs } = params;
|
|
1215
|
+
if (!job.state) job.state = {};
|
|
1216
|
+
if (!isJobEnabled(job)) return false;
|
|
1217
|
+
if (params.skipJobIds?.has(job.id)) return false;
|
|
1218
|
+
if (typeof job.state.runningAtMs === "number") return false;
|
|
1219
|
+
if (params.skipAtIfAlreadyRan && job.schedule.kind === "at" && job.state.lastStatus) {
|
|
1220
|
+
const lastRun = job.state.lastRunAtMs;
|
|
1221
|
+
const nextRun = job.state.nextRunAtMs;
|
|
1222
|
+
if (job.state.lastStatus === "error" && isJobEnabled(job) && typeof nextRun === "number" && typeof lastRun === "number" && nextRun > lastRun) return nowMs >= nextRun;
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
const next = job.state.nextRunAtMs;
|
|
1226
|
+
if (hasScheduledNextRunAtMs(next) && nowMs >= next) return true;
|
|
1227
|
+
if (hasScheduledNextRunAtMs(next) && next > nowMs && isErrorBackoffPending(job, nowMs)) return false;
|
|
1228
|
+
if (!params.allowCronMissedRunByLastRun || job.schedule.kind !== "cron") return false;
|
|
1229
|
+
let previousRunAtMs;
|
|
1230
|
+
try {
|
|
1231
|
+
previousRunAtMs = computeJobPreviousRunAtMs(job, nowMs);
|
|
1232
|
+
} catch {
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
if (typeof previousRunAtMs !== "number" || !Number.isFinite(previousRunAtMs)) return false;
|
|
1236
|
+
const lastRunAtMs = job.state.lastRunAtMs;
|
|
1237
|
+
if (typeof lastRunAtMs !== "number" || !Number.isFinite(lastRunAtMs)) return false;
|
|
1238
|
+
return previousRunAtMs > lastRunAtMs;
|
|
1239
|
+
}
|
|
1240
|
+
function isErrorBackoffPending(job, nowMs) {
|
|
1241
|
+
if (job.schedule.kind === "at" || job.state.lastStatus !== "error") return false;
|
|
1242
|
+
const lastRunAtMs = job.state.lastRunAtMs;
|
|
1243
|
+
if (typeof lastRunAtMs !== "number" || !Number.isFinite(lastRunAtMs)) return false;
|
|
1244
|
+
const consecutiveErrorsRaw = job.state.consecutiveErrors;
|
|
1245
|
+
return nowMs < lastRunAtMs + errorBackoffMs(typeof consecutiveErrorsRaw === "number" && Number.isFinite(consecutiveErrorsRaw) ? Math.max(1, Math.floor(consecutiveErrorsRaw)) : 1);
|
|
1246
|
+
}
|
|
1247
|
+
function collectRunnableJobs(state, nowMs, opts) {
|
|
1248
|
+
if (!state.store) return [];
|
|
1249
|
+
return state.store.jobs.filter((job) => isRunnableJob({
|
|
1250
|
+
job,
|
|
1251
|
+
nowMs,
|
|
1252
|
+
skipJobIds: opts?.skipJobIds,
|
|
1253
|
+
skipAtIfAlreadyRan: opts?.skipAtIfAlreadyRan,
|
|
1254
|
+
allowCronMissedRunByLastRun: opts?.allowCronMissedRunByLastRun
|
|
1255
|
+
}));
|
|
1256
|
+
}
|
|
1257
|
+
async function runMissedJobs(state, opts) {
|
|
1258
|
+
const plan = await planStartupCatchup(state, opts);
|
|
1259
|
+
if (plan.candidates.length === 0 && plan.deferredJobs.length === 0) return;
|
|
1260
|
+
await applyStartupCatchupOutcomes(state, plan, await executeStartupCatchupPlan(state, plan));
|
|
1261
|
+
}
|
|
1262
|
+
async function planStartupCatchup(state, opts) {
|
|
1263
|
+
const maxImmediate = Math.max(0, state.deps.maxMissedJobsPerRestart ?? DEFAULT_MAX_MISSED_JOBS_PER_RESTART);
|
|
1264
|
+
return locked(state, async () => {
|
|
1265
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
1266
|
+
if (!state.store) return {
|
|
1267
|
+
candidates: [],
|
|
1268
|
+
deferredJobs: []
|
|
1269
|
+
};
|
|
1270
|
+
const now = state.deps.nowMs();
|
|
1271
|
+
const missed = collectRunnableJobs(state, now, {
|
|
1272
|
+
skipJobIds: opts?.skipJobIds,
|
|
1273
|
+
skipAtIfAlreadyRan: true,
|
|
1274
|
+
allowCronMissedRunByLastRun: true
|
|
1275
|
+
});
|
|
1276
|
+
if (missed.length === 0) return {
|
|
1277
|
+
candidates: [],
|
|
1278
|
+
deferredJobs: []
|
|
1279
|
+
};
|
|
1280
|
+
const sorted = missed.toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0));
|
|
1281
|
+
const deferredAgentJobs = opts?.deferAgentTurnJobs ? sorted.filter((job) => job.payload.kind === "agentTurn") : [];
|
|
1282
|
+
const startupEligible = opts?.deferAgentTurnJobs ? sorted.filter((job) => job.payload.kind !== "agentTurn") : sorted;
|
|
1283
|
+
const startupCandidates = startupEligible.slice(0, maxImmediate);
|
|
1284
|
+
const deferredOverflow = startupEligible.slice(maxImmediate);
|
|
1285
|
+
const deferredAgentDelayMs = Math.max(0, state.deps.startupDeferredMissedAgentJobDelayMs ?? DEFAULT_STARTUP_DEFERRED_MISSED_AGENT_JOB_DELAY_MS);
|
|
1286
|
+
const deferred = [...deferredOverflow.map((job) => ({ jobId: job.id })), ...deferredAgentJobs.map((job) => ({
|
|
1287
|
+
jobId: job.id,
|
|
1288
|
+
delayMs: deferredAgentDelayMs
|
|
1289
|
+
}))];
|
|
1290
|
+
if (deferred.length > 0) state.deps.log.info({
|
|
1291
|
+
immediateCount: startupCandidates.length,
|
|
1292
|
+
deferredCount: deferred.length,
|
|
1293
|
+
totalMissed: missed.length
|
|
1294
|
+
}, "cron: staggering missed jobs to prevent gateway overload");
|
|
1295
|
+
if (deferredAgentJobs.length > 0) state.deps.log.info({
|
|
1296
|
+
count: deferredAgentJobs.length,
|
|
1297
|
+
jobIds: deferredAgentJobs.map((job) => job.id),
|
|
1298
|
+
delayMs: deferredAgentDelayMs
|
|
1299
|
+
}, "cron: deferring missed agent jobs until after gateway startup");
|
|
1300
|
+
if (startupCandidates.length > 0) state.deps.log.info({
|
|
1301
|
+
count: startupCandidates.length,
|
|
1302
|
+
jobIds: startupCandidates.map((j) => j.id)
|
|
1303
|
+
}, "cron: running missed jobs after restart");
|
|
1304
|
+
for (const job of startupCandidates) {
|
|
1305
|
+
job.state.runningAtMs = now;
|
|
1306
|
+
job.state.lastError = void 0;
|
|
1307
|
+
}
|
|
1308
|
+
await persist(state);
|
|
1309
|
+
return {
|
|
1310
|
+
candidates: startupCandidates.map((job) => ({
|
|
1311
|
+
jobId: job.id,
|
|
1312
|
+
job
|
|
1313
|
+
})),
|
|
1314
|
+
deferredJobs: deferred
|
|
1315
|
+
};
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
async function executeStartupCatchupPlan(state, plan) {
|
|
1319
|
+
const outcomes = [];
|
|
1320
|
+
for (const candidate of plan.candidates) outcomes.push(await runStartupCatchupCandidate(state, candidate));
|
|
1321
|
+
return outcomes;
|
|
1322
|
+
}
|
|
1323
|
+
async function runStartupCatchupCandidate(state, candidate) {
|
|
1324
|
+
const startedAt = state.deps.nowMs();
|
|
1325
|
+
const taskRunId = tryCreateCronTaskRun({
|
|
1326
|
+
state,
|
|
1327
|
+
job: candidate.job,
|
|
1328
|
+
startedAt
|
|
1329
|
+
});
|
|
1330
|
+
emit(state, {
|
|
1331
|
+
jobId: candidate.job.id,
|
|
1332
|
+
action: "started",
|
|
1333
|
+
job: candidate.job,
|
|
1334
|
+
runAtMs: startedAt
|
|
1335
|
+
});
|
|
1336
|
+
try {
|
|
1337
|
+
const result = await executeJobCoreWithTimeout(state, candidate.job);
|
|
1338
|
+
return {
|
|
1339
|
+
jobId: candidate.jobId,
|
|
1340
|
+
job: candidate.job,
|
|
1341
|
+
taskRunId,
|
|
1342
|
+
status: result.status,
|
|
1343
|
+
error: result.error,
|
|
1344
|
+
summary: result.summary,
|
|
1345
|
+
diagnostics: result.diagnostics,
|
|
1346
|
+
delivered: result.delivered,
|
|
1347
|
+
sessionId: result.sessionId,
|
|
1348
|
+
sessionKey: result.sessionKey,
|
|
1349
|
+
model: result.model,
|
|
1350
|
+
provider: result.provider,
|
|
1351
|
+
usage: result.usage,
|
|
1352
|
+
startedAt,
|
|
1353
|
+
endedAt: state.deps.nowMs()
|
|
1354
|
+
};
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
return {
|
|
1357
|
+
jobId: candidate.jobId,
|
|
1358
|
+
job: candidate.job,
|
|
1359
|
+
taskRunId,
|
|
1360
|
+
status: "error",
|
|
1361
|
+
error: normalizeCronRunErrorText(err),
|
|
1362
|
+
diagnostics: createCronRunDiagnosticsFromError("cron-setup", normalizeCronRunErrorText(err), { nowMs: state.deps.nowMs }),
|
|
1363
|
+
startedAt,
|
|
1364
|
+
endedAt: state.deps.nowMs()
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
async function applyStartupCatchupOutcomes(state, plan, outcomes) {
|
|
1369
|
+
const staggerMs = Math.max(0, state.deps.missedJobStaggerMs ?? DEFAULT_MISSED_JOB_STAGGER_MS);
|
|
1370
|
+
await locked(state, async () => {
|
|
1371
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
1372
|
+
if (!state.store) return;
|
|
1373
|
+
for (const result of outcomes) applyOutcomeToStoredJob(state, result);
|
|
1374
|
+
if (plan.deferredJobs.length > 0) {
|
|
1375
|
+
const baseNow = state.deps.nowMs();
|
|
1376
|
+
let offset = staggerMs;
|
|
1377
|
+
for (const deferred of plan.deferredJobs) {
|
|
1378
|
+
const jobId = deferred.jobId;
|
|
1379
|
+
const job = state.store.jobs.find((entry) => entry.id === jobId);
|
|
1380
|
+
if (!job || !isJobEnabled(job)) continue;
|
|
1381
|
+
if (typeof deferred.delayMs === "number") {
|
|
1382
|
+
job.state.nextRunAtMs = baseNow + deferred.delayMs + offset - staggerMs;
|
|
1383
|
+
offset += staggerMs;
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
job.state.nextRunAtMs = baseNow + offset;
|
|
1387
|
+
offset += staggerMs;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
recomputeNextRunsForMaintenance(state, { repairFutureCronNextRunAtMs: false });
|
|
1391
|
+
await persist(state);
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
async function executeJobCore(state, job, abortSignal, options) {
|
|
1395
|
+
const resolveAbortError = () => ({
|
|
1396
|
+
status: "error",
|
|
1397
|
+
error: abortErrorMessage(abortSignal)
|
|
1398
|
+
});
|
|
1399
|
+
const waitWithAbort = async (ms) => {
|
|
1400
|
+
if (!abortSignal) {
|
|
1401
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
if (abortSignal.aborted) return;
|
|
1405
|
+
await new Promise((resolve) => {
|
|
1406
|
+
const timer = setTimeout(() => {
|
|
1407
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
1408
|
+
resolve();
|
|
1409
|
+
}, ms);
|
|
1410
|
+
const onAbort = () => {
|
|
1411
|
+
clearTimeout(timer);
|
|
1412
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
1413
|
+
resolve();
|
|
1414
|
+
};
|
|
1415
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1416
|
+
});
|
|
1417
|
+
};
|
|
1418
|
+
if (abortSignal?.aborted) return resolveAbortError();
|
|
1419
|
+
if (job.sessionTarget === "main") return await executeMainSessionCronJob(state, job, abortSignal, waitWithAbort);
|
|
1420
|
+
return await executeDetachedCronJob(state, job, abortSignal, resolveAbortError, options);
|
|
1421
|
+
}
|
|
1422
|
+
async function executeMainSessionCronJob(state, job, abortSignal, waitWithAbort) {
|
|
1423
|
+
const text = resolveJobPayloadTextForMain(job);
|
|
1424
|
+
if (!text) return {
|
|
1425
|
+
status: "skipped",
|
|
1426
|
+
error: job.payload.kind === "systemEvent" ? "main job requires non-empty systemEvent text" : "main job requires payload.kind=\"systemEvent\""
|
|
1427
|
+
};
|
|
1428
|
+
const cronRunSessionKey = resolveMainSessionCronRunSessionKey(job, typeof job.state.runningAtMs === "number" ? job.state.runningAtMs : state.deps.nowMs());
|
|
1429
|
+
const deliveryContext = resolveMainSessionCronDeliveryContext(state, job);
|
|
1430
|
+
state.deps.enqueueSystemEvent(text, {
|
|
1431
|
+
agentId: job.agentId,
|
|
1432
|
+
sessionKey: cronRunSessionKey,
|
|
1433
|
+
contextKey: `cron:${job.id}`,
|
|
1434
|
+
...deliveryContext ? { deliveryContext } : {}
|
|
1435
|
+
});
|
|
1436
|
+
if (job.wakeMode === "now" && state.deps.runHeartbeatOnce) {
|
|
1437
|
+
const reason = `cron:${job.id}`;
|
|
1438
|
+
const maxWaitMs = state.deps.wakeNowHeartbeatBusyMaxWaitMs ?? 2 * 6e4;
|
|
1439
|
+
const retryDelayMs = state.deps.wakeNowHeartbeatBusyRetryDelayMs ?? 250;
|
|
1440
|
+
const waitStartedAt = state.deps.nowMs();
|
|
1441
|
+
let heartbeatResult;
|
|
1442
|
+
for (;;) {
|
|
1443
|
+
if (abortSignal?.aborted) return {
|
|
1444
|
+
status: "error",
|
|
1445
|
+
error: timeoutErrorMessage()
|
|
1446
|
+
};
|
|
1447
|
+
heartbeatResult = await state.deps.runHeartbeatOnce({
|
|
1448
|
+
source: "cron",
|
|
1449
|
+
intent: "immediate",
|
|
1450
|
+
reason,
|
|
1451
|
+
agentId: job.agentId,
|
|
1452
|
+
sessionKey: cronRunSessionKey,
|
|
1453
|
+
heartbeat: { target: "last" }
|
|
1454
|
+
});
|
|
1455
|
+
if (heartbeatResult.status !== "skipped" || !isRetryableHeartbeatBusySkipReason(heartbeatResult.reason)) break;
|
|
1456
|
+
if (heartbeatResult.reason === "cron-in-progress") {
|
|
1457
|
+
state.deps.requestHeartbeat({
|
|
1458
|
+
source: "cron",
|
|
1459
|
+
intent: "immediate",
|
|
1460
|
+
reason,
|
|
1461
|
+
agentId: job.agentId,
|
|
1462
|
+
sessionKey: cronRunSessionKey,
|
|
1463
|
+
heartbeat: { target: "last" }
|
|
1464
|
+
});
|
|
1465
|
+
return {
|
|
1466
|
+
status: "ok",
|
|
1467
|
+
summary: text,
|
|
1468
|
+
sessionKey: cronRunSessionKey
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
if (abortSignal?.aborted) return {
|
|
1472
|
+
status: "error",
|
|
1473
|
+
error: timeoutErrorMessage()
|
|
1474
|
+
};
|
|
1475
|
+
if (state.deps.nowMs() - waitStartedAt > maxWaitMs) {
|
|
1476
|
+
if (abortSignal?.aborted) return {
|
|
1477
|
+
status: "error",
|
|
1478
|
+
error: timeoutErrorMessage()
|
|
1479
|
+
};
|
|
1480
|
+
state.deps.requestHeartbeat({
|
|
1481
|
+
source: "cron",
|
|
1482
|
+
intent: "immediate",
|
|
1483
|
+
reason,
|
|
1484
|
+
agentId: job.agentId,
|
|
1485
|
+
sessionKey: cronRunSessionKey,
|
|
1486
|
+
heartbeat: { target: "last" }
|
|
1487
|
+
});
|
|
1488
|
+
return {
|
|
1489
|
+
status: "ok",
|
|
1490
|
+
summary: text,
|
|
1491
|
+
sessionKey: cronRunSessionKey
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
await waitWithAbort(retryDelayMs);
|
|
1495
|
+
}
|
|
1496
|
+
if (heartbeatResult.status === "ran") return {
|
|
1497
|
+
status: "ok",
|
|
1498
|
+
summary: text,
|
|
1499
|
+
sessionKey: cronRunSessionKey
|
|
1500
|
+
};
|
|
1501
|
+
if (heartbeatResult.status === "skipped") return {
|
|
1502
|
+
status: "skipped",
|
|
1503
|
+
error: heartbeatResult.reason,
|
|
1504
|
+
summary: text,
|
|
1505
|
+
sessionKey: cronRunSessionKey
|
|
1506
|
+
};
|
|
1507
|
+
return {
|
|
1508
|
+
status: "error",
|
|
1509
|
+
error: heartbeatResult.reason,
|
|
1510
|
+
summary: text,
|
|
1511
|
+
sessionKey: cronRunSessionKey
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
if (abortSignal?.aborted) return {
|
|
1515
|
+
status: "error",
|
|
1516
|
+
error: timeoutErrorMessage()
|
|
1517
|
+
};
|
|
1518
|
+
state.deps.requestHeartbeat({
|
|
1519
|
+
source: "cron",
|
|
1520
|
+
intent: job.wakeMode === "now" ? "immediate" : "event",
|
|
1521
|
+
reason: `cron:${job.id}`,
|
|
1522
|
+
agentId: job.agentId,
|
|
1523
|
+
sessionKey: cronRunSessionKey,
|
|
1524
|
+
heartbeat: { target: "last" }
|
|
1525
|
+
});
|
|
1526
|
+
return {
|
|
1527
|
+
status: "ok",
|
|
1528
|
+
summary: text,
|
|
1529
|
+
sessionKey: cronRunSessionKey
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
async function executeDetachedCronJob(state, job, abortSignal, resolveAbortError, options) {
|
|
1533
|
+
if (job.payload.kind !== "agentTurn") {
|
|
1534
|
+
const error = "isolated job requires payload.kind=agentTurn";
|
|
1535
|
+
return {
|
|
1536
|
+
status: "skipped",
|
|
1537
|
+
error,
|
|
1538
|
+
diagnostics: createCronRunDiagnosticsFromError("cron-preflight", error, {
|
|
1539
|
+
severity: "warn",
|
|
1540
|
+
nowMs: state.deps.nowMs
|
|
1541
|
+
})
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
if (abortSignal?.aborted) {
|
|
1545
|
+
const aborted = resolveAbortError();
|
|
1546
|
+
return {
|
|
1547
|
+
...aborted,
|
|
1548
|
+
diagnostics: createCronRunDiagnosticsFromError("cron-setup", aborted.error, { nowMs: state.deps.nowMs })
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
const res = await state.deps.runIsolatedAgentJob({
|
|
1552
|
+
job,
|
|
1553
|
+
message: job.payload.message,
|
|
1554
|
+
abortSignal,
|
|
1555
|
+
onExecutionStarted: options?.onExecutionStarted,
|
|
1556
|
+
onExecutionPhase: options?.onExecutionPhase
|
|
1557
|
+
});
|
|
1558
|
+
if (abortSignal?.aborted) {
|
|
1559
|
+
const error = abortErrorMessage(abortSignal);
|
|
1560
|
+
return {
|
|
1561
|
+
status: "error",
|
|
1562
|
+
error,
|
|
1563
|
+
diagnostics: createCronRunDiagnosticsFromError("cron-setup", error, { nowMs: state.deps.nowMs })
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
return {
|
|
1567
|
+
status: res.status,
|
|
1568
|
+
error: res.error,
|
|
1569
|
+
summary: res.summary,
|
|
1570
|
+
delivered: res.delivered,
|
|
1571
|
+
deliveryAttempted: res.deliveryAttempted,
|
|
1572
|
+
delivery: res.delivery,
|
|
1573
|
+
sessionId: res.sessionId,
|
|
1574
|
+
sessionKey: res.sessionKey,
|
|
1575
|
+
diagnostics: res.diagnostics,
|
|
1576
|
+
model: res.model,
|
|
1577
|
+
provider: res.provider,
|
|
1578
|
+
usage: res.usage
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
function emitJobFinished(state, job, result, runAtMs) {
|
|
1582
|
+
emit(state, {
|
|
1583
|
+
jobId: job.id,
|
|
1584
|
+
action: "finished",
|
|
1585
|
+
job,
|
|
1586
|
+
status: result.status,
|
|
1587
|
+
error: result.error,
|
|
1588
|
+
summary: result.summary,
|
|
1589
|
+
diagnostics: result.diagnostics,
|
|
1590
|
+
delivered: job.state.lastDelivered,
|
|
1591
|
+
deliveryStatus: job.state.lastDeliveryStatus,
|
|
1592
|
+
deliveryError: job.state.lastDeliveryError,
|
|
1593
|
+
failureNotificationDelivery: failureNotificationDeliveryFromJobState(job),
|
|
1594
|
+
delivery: result.delivery,
|
|
1595
|
+
sessionId: result.sessionId,
|
|
1596
|
+
sessionKey: result.sessionKey,
|
|
1597
|
+
runAtMs,
|
|
1598
|
+
durationMs: job.state.lastDurationMs,
|
|
1599
|
+
nextRunAtMs: job.state.nextRunAtMs,
|
|
1600
|
+
model: result.model,
|
|
1601
|
+
provider: result.provider,
|
|
1602
|
+
usage: result.usage
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
function wake(state, opts) {
|
|
1606
|
+
const text = opts.text.trim();
|
|
1607
|
+
if (!text) return { ok: false };
|
|
1608
|
+
const sessionKey = opts.sessionKey?.trim() || void 0;
|
|
1609
|
+
if (sessionKey && isSubagentSessionKey(sessionKey)) return {
|
|
1610
|
+
ok: false,
|
|
1611
|
+
reason: "unwakeable-session-key"
|
|
1612
|
+
};
|
|
1613
|
+
state.deps.enqueueSystemEvent(text, sessionKey ? { sessionKey } : void 0);
|
|
1614
|
+
if (opts.mode === "now") state.deps.requestHeartbeat({
|
|
1615
|
+
source: "manual",
|
|
1616
|
+
intent: "immediate",
|
|
1617
|
+
reason: "wake",
|
|
1618
|
+
...sessionKey ? { sessionKey } : {}
|
|
1619
|
+
});
|
|
1620
|
+
else if (sessionKey) state.deps.requestHeartbeat({
|
|
1621
|
+
source: "manual",
|
|
1622
|
+
intent: "immediate",
|
|
1623
|
+
reason: "wake",
|
|
1624
|
+
sessionKey
|
|
1625
|
+
});
|
|
1626
|
+
return { ok: true };
|
|
1627
|
+
}
|
|
1628
|
+
function stopTimer(state) {
|
|
1629
|
+
if (state.timer) clearTimeout(state.timer);
|
|
1630
|
+
state.timer = null;
|
|
1631
|
+
}
|
|
1632
|
+
function emit(state, evt) {
|
|
1633
|
+
try {
|
|
1634
|
+
state.deps.onEvent?.(evt);
|
|
1635
|
+
} catch {}
|
|
1636
|
+
}
|
|
1637
|
+
//#endregion
|
|
1638
|
+
//#region src/cron/service/ops.ts
|
|
1639
|
+
const STARTUP_INTERRUPTED_ERROR = "cron: job interrupted by gateway restart";
|
|
1640
|
+
function resolveInterruptedStartupFailureNotificationStatus(params) {
|
|
1641
|
+
if (params.job.delivery?.bestEffort === true) return "not-requested";
|
|
1642
|
+
if (resolveFailureDestination(params.job, params.state.deps.cronConfig?.failureDestination)) return "unknown";
|
|
1643
|
+
const primaryPlan = resolveCronDeliveryPlan(params.job);
|
|
1644
|
+
return primaryPlan.mode === "announce" && primaryPlan.requested ? "unknown" : "not-requested";
|
|
1645
|
+
}
|
|
1646
|
+
function markInterruptedStartupRun(params) {
|
|
1647
|
+
const { job, runningAtMs, nowMs } = params;
|
|
1648
|
+
const failureNotificationStatus = resolveInterruptedStartupFailureNotificationStatus({
|
|
1649
|
+
state: params.state,
|
|
1650
|
+
job
|
|
1651
|
+
});
|
|
1652
|
+
const previousErrors = typeof job.state.consecutiveErrors === "number" && Number.isFinite(job.state.consecutiveErrors) ? Math.max(0, Math.floor(job.state.consecutiveErrors)) : 0;
|
|
1653
|
+
params.state.deps.log.warn({
|
|
1654
|
+
jobId: job.id,
|
|
1655
|
+
runningAtMs
|
|
1656
|
+
}, "cron: marking interrupted running job failed on startup");
|
|
1657
|
+
job.state.runningAtMs = void 0;
|
|
1658
|
+
job.state.lastRunAtMs = runningAtMs;
|
|
1659
|
+
job.state.lastRunStatus = "error";
|
|
1660
|
+
job.state.lastStatus = "error";
|
|
1661
|
+
job.state.lastError = STARTUP_INTERRUPTED_ERROR;
|
|
1662
|
+
job.state.lastDurationMs = Math.max(0, nowMs - runningAtMs);
|
|
1663
|
+
job.state.consecutiveErrors = previousErrors + 1;
|
|
1664
|
+
job.state.lastDelivered = false;
|
|
1665
|
+
job.state.lastDeliveryStatus = "unknown";
|
|
1666
|
+
job.state.lastDeliveryError = STARTUP_INTERRUPTED_ERROR;
|
|
1667
|
+
job.state.lastFailureNotificationDelivered = void 0;
|
|
1668
|
+
job.state.lastFailureNotificationDeliveryStatus = failureNotificationStatus;
|
|
1669
|
+
job.state.lastFailureNotificationDeliveryError = void 0;
|
|
1670
|
+
job.state.nextRunAtMs = void 0;
|
|
1671
|
+
job.updatedAtMs = nowMs;
|
|
1672
|
+
if (job.schedule.kind === "at") job.enabled = false;
|
|
1673
|
+
return {
|
|
1674
|
+
jobId: job.id,
|
|
1675
|
+
runAtMs: runningAtMs,
|
|
1676
|
+
durationMs: job.state.lastDurationMs
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
function mergeManualRunSnapshotAfterReload(params) {
|
|
1680
|
+
if (!params.state.store) return;
|
|
1681
|
+
if (params.removed) {
|
|
1682
|
+
params.state.store.jobs = params.state.store.jobs.filter((job) => job.id !== params.jobId);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
if (!params.snapshot) return;
|
|
1686
|
+
const reloaded = params.state.store.jobs.find((job) => job.id === params.jobId);
|
|
1687
|
+
if (!reloaded) return;
|
|
1688
|
+
reloaded.enabled = params.snapshot.enabled;
|
|
1689
|
+
reloaded.updatedAtMs = params.snapshot.updatedAtMs;
|
|
1690
|
+
reloaded.state = params.snapshot.state;
|
|
1691
|
+
}
|
|
1692
|
+
async function ensureLoadedForRead(state) {
|
|
1693
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
1694
|
+
if (!state.store) return;
|
|
1695
|
+
if (recomputeNextRunsForMaintenance(state)) await persist(state);
|
|
1696
|
+
}
|
|
1697
|
+
async function start(state) {
|
|
1698
|
+
if (!state.deps.cronEnabled) {
|
|
1699
|
+
state.deps.log.info({ enabled: false }, "cron: disabled");
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
const interruptedJobIds = /* @__PURE__ */ new Set();
|
|
1703
|
+
const interruptedRuns = [];
|
|
1704
|
+
let markedAnyInterruptedRun = false;
|
|
1705
|
+
await locked(state, async () => {
|
|
1706
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
1707
|
+
const jobs = state.store?.jobs ?? [];
|
|
1708
|
+
for (const job of jobs) {
|
|
1709
|
+
job.state ??= {};
|
|
1710
|
+
if (typeof job.state.runningAtMs === "number") {
|
|
1711
|
+
const nowMs = state.deps.nowMs();
|
|
1712
|
+
const interrupted = markInterruptedStartupRun({
|
|
1713
|
+
state,
|
|
1714
|
+
job,
|
|
1715
|
+
runningAtMs: job.state.runningAtMs,
|
|
1716
|
+
nowMs
|
|
1717
|
+
});
|
|
1718
|
+
interruptedJobIds.add(job.id);
|
|
1719
|
+
interruptedRuns.push(interrupted);
|
|
1720
|
+
markedAnyInterruptedRun = true;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
if (markedAnyInterruptedRun || jobs.length > 0) await persist(state, markedAnyInterruptedRun ? void 0 : { stateOnly: true });
|
|
1724
|
+
});
|
|
1725
|
+
await runMissedJobs(state, {
|
|
1726
|
+
skipJobIds: interruptedJobIds.size > 0 ? interruptedJobIds : void 0,
|
|
1727
|
+
deferAgentTurnJobs: true
|
|
1728
|
+
});
|
|
1729
|
+
await locked(state, async () => {
|
|
1730
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
1731
|
+
if (recomputeNextRunsForMaintenance(state, { recomputeExpired: true })) await persist(state);
|
|
1732
|
+
for (const interrupted of interruptedRuns) {
|
|
1733
|
+
const job = state.store?.jobs.find((entry) => entry.id === interrupted.jobId);
|
|
1734
|
+
emit(state, {
|
|
1735
|
+
jobId: interrupted.jobId,
|
|
1736
|
+
action: "finished",
|
|
1737
|
+
job,
|
|
1738
|
+
status: "error",
|
|
1739
|
+
error: STARTUP_INTERRUPTED_ERROR,
|
|
1740
|
+
delivered: false,
|
|
1741
|
+
deliveryStatus: "unknown",
|
|
1742
|
+
deliveryError: STARTUP_INTERRUPTED_ERROR,
|
|
1743
|
+
failureNotificationDelivery: job ? failureNotificationDeliveryFromJobState(job) : void 0,
|
|
1744
|
+
runAtMs: interrupted.runAtMs,
|
|
1745
|
+
durationMs: interrupted.durationMs,
|
|
1746
|
+
nextRunAtMs: job?.state.nextRunAtMs
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
armTimer(state);
|
|
1750
|
+
state.deps.log.info({
|
|
1751
|
+
enabled: true,
|
|
1752
|
+
jobs: state.store?.jobs.length ?? 0,
|
|
1753
|
+
nextWakeAtMs: nextWakeAtMs(state) ?? null
|
|
1754
|
+
}, "cron: started");
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
function stop(state) {
|
|
1758
|
+
stopTimer(state);
|
|
1759
|
+
}
|
|
1760
|
+
async function status(state) {
|
|
1761
|
+
return await locked(state, async () => {
|
|
1762
|
+
await ensureLoadedForRead(state);
|
|
1763
|
+
return {
|
|
1764
|
+
enabled: state.deps.cronEnabled,
|
|
1765
|
+
storePath: state.deps.storePath,
|
|
1766
|
+
jobs: state.store?.jobs.length ?? 0,
|
|
1767
|
+
nextWakeAtMs: state.deps.cronEnabled ? nextWakeAtMs(state) ?? null : null
|
|
1768
|
+
};
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
async function list(state, opts) {
|
|
1772
|
+
return await locked(state, async () => {
|
|
1773
|
+
await ensureLoadedForRead(state);
|
|
1774
|
+
const includeDisabled = opts?.includeDisabled === true;
|
|
1775
|
+
return (state.store?.jobs ?? []).filter((j) => includeDisabled || isJobEnabled(j)).toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0));
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
async function readJob(state, id) {
|
|
1779
|
+
return await locked(state, async () => {
|
|
1780
|
+
await ensureLoadedForRead(state);
|
|
1781
|
+
return state.store?.jobs.find((job) => job.id === id);
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
function resolveEnabledFilter(opts) {
|
|
1785
|
+
if (opts?.enabled === "all" || opts?.enabled === "enabled" || opts?.enabled === "disabled") return opts.enabled;
|
|
1786
|
+
return opts?.includeDisabled ? "all" : "enabled";
|
|
1787
|
+
}
|
|
1788
|
+
function sortJobs(jobs, sortBy, sortDir) {
|
|
1789
|
+
const dir = sortDir === "desc" ? -1 : 1;
|
|
1790
|
+
return jobs.toSorted((a, b) => {
|
|
1791
|
+
let cmp = 0;
|
|
1792
|
+
if (sortBy === "name") {
|
|
1793
|
+
const aName = typeof a.name === "string" ? a.name : "";
|
|
1794
|
+
const bName = typeof b.name === "string" ? b.name : "";
|
|
1795
|
+
cmp = aName.localeCompare(bName, void 0, { sensitivity: "base" });
|
|
1796
|
+
} else if (sortBy === "updatedAtMs") cmp = a.updatedAtMs - b.updatedAtMs;
|
|
1797
|
+
else {
|
|
1798
|
+
const aNext = a.state.nextRunAtMs;
|
|
1799
|
+
const bNext = b.state.nextRunAtMs;
|
|
1800
|
+
if (typeof aNext === "number" && typeof bNext === "number") cmp = aNext - bNext;
|
|
1801
|
+
else if (typeof aNext === "number") cmp = -1;
|
|
1802
|
+
else if (typeof bNext === "number") cmp = 1;
|
|
1803
|
+
else cmp = 0;
|
|
1804
|
+
}
|
|
1805
|
+
if (cmp !== 0) return cmp * dir;
|
|
1806
|
+
const aId = typeof a.id === "string" ? a.id : "";
|
|
1807
|
+
const bId = typeof b.id === "string" ? b.id : "";
|
|
1808
|
+
return aId.localeCompare(bId);
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
function resolveEffectiveJobAgentId(job, defaultAgentId) {
|
|
1812
|
+
return normalizeOptionalAgentId(job.agentId) ?? normalizeOptionalAgentId(defaultAgentId) ?? "main";
|
|
1813
|
+
}
|
|
1814
|
+
async function listPage(state, opts) {
|
|
1815
|
+
return await locked(state, async () => {
|
|
1816
|
+
await ensureLoadedForRead(state);
|
|
1817
|
+
const query = normalizeLowercaseStringOrEmpty(opts?.query);
|
|
1818
|
+
const enabledFilter = resolveEnabledFilter(opts);
|
|
1819
|
+
const sortBy = opts?.sortBy ?? "nextRunAtMs";
|
|
1820
|
+
const sortDir = opts?.sortDir ?? "asc";
|
|
1821
|
+
const requestedAgentId = normalizeOptionalAgentId(opts?.agentId);
|
|
1822
|
+
const sorted = sortJobs((state.store?.jobs ?? []).filter((job) => {
|
|
1823
|
+
if (enabledFilter === "enabled" && !isJobEnabled(job)) return false;
|
|
1824
|
+
if (enabledFilter === "disabled" && isJobEnabled(job)) return false;
|
|
1825
|
+
if (requestedAgentId && resolveEffectiveJobAgentId(job, state.deps.defaultAgentId) !== requestedAgentId) return false;
|
|
1826
|
+
if (!query) return true;
|
|
1827
|
+
return normalizeLowercaseStringOrEmpty([
|
|
1828
|
+
job.name,
|
|
1829
|
+
job.description ?? "",
|
|
1830
|
+
job.agentId ?? ""
|
|
1831
|
+
].join(" ")).includes(query);
|
|
1832
|
+
}), sortBy, sortDir);
|
|
1833
|
+
const total = sorted.length;
|
|
1834
|
+
const offset = Math.max(0, Math.min(total, Math.floor(opts?.offset ?? 0)));
|
|
1835
|
+
const defaultLimit = total === 0 ? 50 : total;
|
|
1836
|
+
const limit = Math.max(1, Math.min(200, Math.floor(opts?.limit ?? defaultLimit)));
|
|
1837
|
+
const jobs = sorted.slice(offset, offset + limit);
|
|
1838
|
+
const nextOffset = offset + jobs.length;
|
|
1839
|
+
return {
|
|
1840
|
+
jobs,
|
|
1841
|
+
total,
|
|
1842
|
+
offset,
|
|
1843
|
+
limit,
|
|
1844
|
+
hasMore: nextOffset < total,
|
|
1845
|
+
nextOffset: nextOffset < total ? nextOffset : null
|
|
1846
|
+
};
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
async function add(state, input) {
|
|
1850
|
+
return await locked(state, async () => {
|
|
1851
|
+
warnIfDisabled(state, "add");
|
|
1852
|
+
await ensureLoaded(state);
|
|
1853
|
+
const job = createJob(state, input);
|
|
1854
|
+
state.store?.jobs.push(job);
|
|
1855
|
+
recomputeNextRuns(state);
|
|
1856
|
+
await persist(state);
|
|
1857
|
+
armTimer(state);
|
|
1858
|
+
state.deps.log.info({
|
|
1859
|
+
jobId: job.id,
|
|
1860
|
+
jobName: job.name,
|
|
1861
|
+
nextRunAtMs: job.state.nextRunAtMs,
|
|
1862
|
+
schedulerNextWakeAtMs: nextWakeAtMs(state) ?? null,
|
|
1863
|
+
timerArmed: state.timer !== null,
|
|
1864
|
+
cronEnabled: state.deps.cronEnabled
|
|
1865
|
+
}, "cron: job added");
|
|
1866
|
+
emit(state, {
|
|
1867
|
+
jobId: job.id,
|
|
1868
|
+
action: "added",
|
|
1869
|
+
job,
|
|
1870
|
+
nextRunAtMs: job.state.nextRunAtMs
|
|
1871
|
+
});
|
|
1872
|
+
return job;
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
async function update(state, id, patch) {
|
|
1876
|
+
return await locked(state, async () => {
|
|
1877
|
+
warnIfDisabled(state, "update");
|
|
1878
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
1879
|
+
const job = findJobOrThrow(state, id);
|
|
1880
|
+
const now = state.deps.nowMs();
|
|
1881
|
+
const nextJob = structuredClone(job);
|
|
1882
|
+
applyJobPatch(nextJob, patch, { defaultAgentId: state.deps.defaultAgentId });
|
|
1883
|
+
if (nextJob.schedule.kind === "every") {
|
|
1884
|
+
const anchor = nextJob.schedule.anchorMs;
|
|
1885
|
+
if (typeof anchor !== "number" || !Number.isFinite(anchor)) {
|
|
1886
|
+
const fallbackAnchorMs = patch.schedule?.kind === "every" ? now : typeof nextJob.createdAtMs === "number" && Number.isFinite(nextJob.createdAtMs) ? nextJob.createdAtMs : now;
|
|
1887
|
+
nextJob.schedule = {
|
|
1888
|
+
...nextJob.schedule,
|
|
1889
|
+
anchorMs: Math.max(0, Math.floor(fallbackAnchorMs))
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
const scheduleChanged = patch.schedule !== void 0;
|
|
1894
|
+
const enabledChanged = patch.enabled !== void 0;
|
|
1895
|
+
if (scheduleChanged && nextJob.schedule.kind === "cron" && !isJobEnabled(nextJob)) computeJobNextRunAtMs({
|
|
1896
|
+
...nextJob,
|
|
1897
|
+
enabled: true
|
|
1898
|
+
}, now);
|
|
1899
|
+
nextJob.updatedAtMs = now;
|
|
1900
|
+
if (scheduleChanged || enabledChanged) if (isJobEnabled(nextJob)) nextJob.state.nextRunAtMs = computeJobNextRunAtMs(nextJob, now);
|
|
1901
|
+
else {
|
|
1902
|
+
nextJob.state.nextRunAtMs = void 0;
|
|
1903
|
+
nextJob.state.runningAtMs = void 0;
|
|
1904
|
+
}
|
|
1905
|
+
else if (isJobEnabled(nextJob) && !hasScheduledNextRunAtMs(nextJob.state.nextRunAtMs)) nextJob.state.nextRunAtMs = computeJobNextRunAtMs(nextJob, now);
|
|
1906
|
+
if (state.store) {
|
|
1907
|
+
const index = state.store.jobs.findIndex((entry) => entry.id === id);
|
|
1908
|
+
if (index >= 0) state.store.jobs[index] = nextJob;
|
|
1909
|
+
}
|
|
1910
|
+
await persist(state);
|
|
1911
|
+
armTimer(state);
|
|
1912
|
+
emit(state, {
|
|
1913
|
+
jobId: id,
|
|
1914
|
+
action: "updated",
|
|
1915
|
+
job: nextJob,
|
|
1916
|
+
nextRunAtMs: nextJob.state.nextRunAtMs
|
|
1917
|
+
});
|
|
1918
|
+
return nextJob;
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
async function remove(state, id) {
|
|
1922
|
+
return await locked(state, async () => {
|
|
1923
|
+
warnIfDisabled(state, "remove");
|
|
1924
|
+
await ensureLoaded(state);
|
|
1925
|
+
const before = state.store?.jobs.length ?? 0;
|
|
1926
|
+
if (!state.store) return {
|
|
1927
|
+
ok: false,
|
|
1928
|
+
removed: false
|
|
1929
|
+
};
|
|
1930
|
+
const removedJob = state.store.jobs.find((j) => j.id === id);
|
|
1931
|
+
state.store.jobs = state.store.jobs.filter((j) => j.id !== id);
|
|
1932
|
+
const removed = (state.store.jobs.length ?? 0) !== before;
|
|
1933
|
+
await persist(state);
|
|
1934
|
+
armTimer(state);
|
|
1935
|
+
if (removed) emit(state, {
|
|
1936
|
+
jobId: id,
|
|
1937
|
+
action: "removed",
|
|
1938
|
+
job: removedJob
|
|
1939
|
+
});
|
|
1940
|
+
return {
|
|
1941
|
+
ok: true,
|
|
1942
|
+
removed
|
|
1943
|
+
};
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
let nextManualRunId = 1;
|
|
1947
|
+
async function skipInvalidPersistedManualRun(params) {
|
|
1948
|
+
const endedAt = params.state.deps.nowMs();
|
|
1949
|
+
const errorText = normalizeCronRunErrorText(params.error);
|
|
1950
|
+
const diagnostics = createCronRunDiagnosticsFromError("cron-preflight", errorText, {
|
|
1951
|
+
severity: "warn",
|
|
1952
|
+
nowMs: params.state.deps.nowMs
|
|
1953
|
+
});
|
|
1954
|
+
const shouldDelete = applyJobResult(params.state, params.job, {
|
|
1955
|
+
status: "skipped",
|
|
1956
|
+
error: errorText,
|
|
1957
|
+
diagnostics,
|
|
1958
|
+
startedAt: endedAt,
|
|
1959
|
+
endedAt
|
|
1960
|
+
}, { preserveSchedule: params.mode === "force" });
|
|
1961
|
+
emit(params.state, {
|
|
1962
|
+
jobId: params.job.id,
|
|
1963
|
+
action: "finished",
|
|
1964
|
+
status: "skipped",
|
|
1965
|
+
error: errorText,
|
|
1966
|
+
diagnostics,
|
|
1967
|
+
runAtMs: endedAt,
|
|
1968
|
+
durationMs: params.job.state.lastDurationMs,
|
|
1969
|
+
nextRunAtMs: params.job.state.nextRunAtMs,
|
|
1970
|
+
deliveryStatus: params.job.state.lastDeliveryStatus,
|
|
1971
|
+
deliveryError: params.job.state.lastDeliveryError,
|
|
1972
|
+
failureNotificationDelivery: failureNotificationDeliveryFromJobState(params.job)
|
|
1973
|
+
});
|
|
1974
|
+
if (shouldDelete && params.state.store) {
|
|
1975
|
+
params.state.store.jobs = params.state.store.jobs.filter((entry) => entry.id !== params.job.id);
|
|
1976
|
+
emit(params.state, {
|
|
1977
|
+
jobId: params.job.id,
|
|
1978
|
+
action: "removed"
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
recomputeNextRunsForMaintenance(params.state, { recomputeExpired: true });
|
|
1982
|
+
await persist(params.state);
|
|
1983
|
+
armTimer(params.state);
|
|
1984
|
+
}
|
|
1985
|
+
function tryCreateManualTaskRun(params) {
|
|
1986
|
+
const runId = createCronExecutionId(params.job.id, params.startedAt);
|
|
1987
|
+
try {
|
|
1988
|
+
createRunningTaskRun({
|
|
1989
|
+
runtime: "cron",
|
|
1990
|
+
sourceId: params.job.id,
|
|
1991
|
+
ownerKey: "",
|
|
1992
|
+
scopeKind: "system",
|
|
1993
|
+
childSessionKey: params.job.sessionKey,
|
|
1994
|
+
agentId: params.job.agentId,
|
|
1995
|
+
runId,
|
|
1996
|
+
label: params.job.name,
|
|
1997
|
+
task: params.job.name || params.job.id,
|
|
1998
|
+
deliveryStatus: "not_applicable",
|
|
1999
|
+
notifyPolicy: "silent",
|
|
2000
|
+
startedAt: params.startedAt,
|
|
2001
|
+
lastEventAt: params.startedAt
|
|
2002
|
+
});
|
|
2003
|
+
return runId;
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
params.state.deps.log.warn({
|
|
2006
|
+
jobId: params.job.id,
|
|
2007
|
+
error
|
|
2008
|
+
}, "cron: failed to create task ledger record");
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
function tryFinishManualTaskRun(state, params) {
|
|
2013
|
+
if (!params.taskRunId) return;
|
|
2014
|
+
try {
|
|
2015
|
+
if (params.coreResult.status === "ok" || params.coreResult.status === "skipped") {
|
|
2016
|
+
completeTaskRunByRunId({
|
|
2017
|
+
runId: params.taskRunId,
|
|
2018
|
+
runtime: "cron",
|
|
2019
|
+
endedAt: params.endedAt,
|
|
2020
|
+
lastEventAt: params.endedAt,
|
|
2021
|
+
terminalSummary: params.coreResult.summary ?? void 0
|
|
2022
|
+
});
|
|
2023
|
+
return;
|
|
2024
|
+
}
|
|
2025
|
+
failTaskRunByRunId({
|
|
2026
|
+
runId: params.taskRunId,
|
|
2027
|
+
runtime: "cron",
|
|
2028
|
+
status: normalizeCronRunErrorText(params.coreResult.error) === "cron: job execution timed out" ? "timed_out" : "failed",
|
|
2029
|
+
endedAt: params.endedAt,
|
|
2030
|
+
lastEventAt: params.endedAt,
|
|
2031
|
+
error: params.coreResult.status === "error" ? normalizeCronRunErrorText(params.coreResult.error) : void 0,
|
|
2032
|
+
terminalSummary: params.coreResult.summary ?? void 0
|
|
2033
|
+
});
|
|
2034
|
+
} catch (error) {
|
|
2035
|
+
state.deps.log.warn({
|
|
2036
|
+
runId: params.taskRunId,
|
|
2037
|
+
jobStatus: params.coreResult.status,
|
|
2038
|
+
error
|
|
2039
|
+
}, "cron: failed to update task ledger record");
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
async function inspectManualRunPreflight(state, id, mode) {
|
|
2043
|
+
return await locked(state, async () => {
|
|
2044
|
+
warnIfDisabled(state, "run");
|
|
2045
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
2046
|
+
recomputeNextRunsForMaintenance(state);
|
|
2047
|
+
const job = findJobOrThrow(state, id);
|
|
2048
|
+
try {
|
|
2049
|
+
assertSupportedJobSpec(job);
|
|
2050
|
+
} catch (error) {
|
|
2051
|
+
await skipInvalidPersistedManualRun({
|
|
2052
|
+
state,
|
|
2053
|
+
job,
|
|
2054
|
+
mode,
|
|
2055
|
+
error
|
|
2056
|
+
});
|
|
2057
|
+
return {
|
|
2058
|
+
ok: true,
|
|
2059
|
+
ran: false,
|
|
2060
|
+
reason: "invalid-spec"
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
if (typeof job.state.runningAtMs === "number") return {
|
|
2064
|
+
ok: true,
|
|
2065
|
+
ran: false,
|
|
2066
|
+
reason: "already-running"
|
|
2067
|
+
};
|
|
2068
|
+
const now = state.deps.nowMs();
|
|
2069
|
+
if (!isJobDue(job, now, { forced: mode === "force" })) return {
|
|
2070
|
+
ok: true,
|
|
2071
|
+
ran: false,
|
|
2072
|
+
reason: "not-due"
|
|
2073
|
+
};
|
|
2074
|
+
return {
|
|
2075
|
+
ok: true,
|
|
2076
|
+
runnable: true,
|
|
2077
|
+
job,
|
|
2078
|
+
now
|
|
2079
|
+
};
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
async function inspectManualRunDisposition(state, id, mode) {
|
|
2083
|
+
const result = await inspectManualRunPreflight(state, id, mode);
|
|
2084
|
+
if (!result.ok) return result;
|
|
2085
|
+
if ("reason" in result) return result;
|
|
2086
|
+
return {
|
|
2087
|
+
ok: true,
|
|
2088
|
+
runnable: true
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
async function prepareManualRun(state, id, mode, opts) {
|
|
2092
|
+
const preflight = await inspectManualRunPreflight(state, id, mode);
|
|
2093
|
+
if (!preflight.ok) return preflight;
|
|
2094
|
+
if ("reason" in preflight) return {
|
|
2095
|
+
ok: true,
|
|
2096
|
+
ran: false,
|
|
2097
|
+
reason: preflight.reason
|
|
2098
|
+
};
|
|
2099
|
+
return await locked(state, async () => {
|
|
2100
|
+
const job = findJobOrThrow(state, id);
|
|
2101
|
+
if (typeof job.state.runningAtMs === "number") return {
|
|
2102
|
+
ok: true,
|
|
2103
|
+
ran: false,
|
|
2104
|
+
reason: "already-running"
|
|
2105
|
+
};
|
|
2106
|
+
job.state.runningAtMs = preflight.now;
|
|
2107
|
+
job.state.lastError = void 0;
|
|
2108
|
+
await persist(state);
|
|
2109
|
+
emit(state, {
|
|
2110
|
+
jobId: job.id,
|
|
2111
|
+
action: "started",
|
|
2112
|
+
job,
|
|
2113
|
+
runAtMs: preflight.now
|
|
2114
|
+
});
|
|
2115
|
+
const taskRunId = tryCreateManualTaskRun({
|
|
2116
|
+
state,
|
|
2117
|
+
job,
|
|
2118
|
+
startedAt: preflight.now
|
|
2119
|
+
});
|
|
2120
|
+
markCronJobActive(job.id);
|
|
2121
|
+
const executionJob = structuredClone(job);
|
|
2122
|
+
return {
|
|
2123
|
+
ok: true,
|
|
2124
|
+
ran: true,
|
|
2125
|
+
jobId: job.id,
|
|
2126
|
+
runId: opts?.runId ?? taskRunId,
|
|
2127
|
+
taskRunId,
|
|
2128
|
+
startedAt: preflight.now,
|
|
2129
|
+
executionJob
|
|
2130
|
+
};
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
async function finishPreparedManualRun(state, prepared, mode) {
|
|
2134
|
+
const executionJob = prepared.executionJob;
|
|
2135
|
+
const startedAt = prepared.startedAt;
|
|
2136
|
+
const jobId = prepared.jobId;
|
|
2137
|
+
const taskRunId = prepared.taskRunId;
|
|
2138
|
+
const runId = prepared.runId;
|
|
2139
|
+
try {
|
|
2140
|
+
let coreResult;
|
|
2141
|
+
try {
|
|
2142
|
+
coreResult = await executeJobCoreWithTimeout(state, executionJob);
|
|
2143
|
+
} catch (err) {
|
|
2144
|
+
coreResult = {
|
|
2145
|
+
status: "error",
|
|
2146
|
+
error: normalizeCronRunErrorText(err)
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
const endedAt = state.deps.nowMs();
|
|
2150
|
+
tryFinishManualTaskRun(state, {
|
|
2151
|
+
taskRunId,
|
|
2152
|
+
coreResult,
|
|
2153
|
+
endedAt
|
|
2154
|
+
});
|
|
2155
|
+
await locked(state, async () => {
|
|
2156
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
2157
|
+
const job = state.store?.jobs.find((entry) => entry.id === jobId);
|
|
2158
|
+
if (!job) return;
|
|
2159
|
+
const shouldDelete = applyJobResult(state, job, {
|
|
2160
|
+
status: coreResult.status,
|
|
2161
|
+
error: coreResult.error,
|
|
2162
|
+
diagnostics: coreResult.diagnostics,
|
|
2163
|
+
delivered: coreResult.delivered,
|
|
2164
|
+
startedAt,
|
|
2165
|
+
endedAt
|
|
2166
|
+
}, { preserveSchedule: mode === "force" });
|
|
2167
|
+
emit(state, {
|
|
2168
|
+
jobId: job.id,
|
|
2169
|
+
action: "finished",
|
|
2170
|
+
job,
|
|
2171
|
+
status: coreResult.status,
|
|
2172
|
+
error: coreResult.error,
|
|
2173
|
+
summary: coreResult.summary,
|
|
2174
|
+
diagnostics: coreResult.diagnostics,
|
|
2175
|
+
delivered: job.state.lastDelivered,
|
|
2176
|
+
deliveryStatus: job.state.lastDeliveryStatus,
|
|
2177
|
+
deliveryError: job.state.lastDeliveryError,
|
|
2178
|
+
failureNotificationDelivery: failureNotificationDeliveryFromJobState(job),
|
|
2179
|
+
delivery: coreResult.delivery,
|
|
2180
|
+
sessionId: coreResult.sessionId,
|
|
2181
|
+
sessionKey: coreResult.sessionKey,
|
|
2182
|
+
runId,
|
|
2183
|
+
runAtMs: startedAt,
|
|
2184
|
+
durationMs: job.state.lastDurationMs,
|
|
2185
|
+
nextRunAtMs: job.state.nextRunAtMs,
|
|
2186
|
+
model: coreResult.model,
|
|
2187
|
+
provider: coreResult.provider,
|
|
2188
|
+
usage: coreResult.usage
|
|
2189
|
+
});
|
|
2190
|
+
if (shouldDelete && state.store) {
|
|
2191
|
+
state.store.jobs = state.store.jobs.filter((entry) => entry.id !== job.id);
|
|
2192
|
+
emit(state, {
|
|
2193
|
+
jobId: job.id,
|
|
2194
|
+
action: "removed",
|
|
2195
|
+
job
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
const postRunSnapshot = shouldDelete ? null : {
|
|
2199
|
+
enabled: job.enabled,
|
|
2200
|
+
updatedAtMs: job.updatedAtMs,
|
|
2201
|
+
state: structuredClone(job.state)
|
|
2202
|
+
};
|
|
2203
|
+
const postRunRemoved = shouldDelete;
|
|
2204
|
+
await ensureLoaded(state, {
|
|
2205
|
+
forceReload: true,
|
|
2206
|
+
skipRecompute: true
|
|
2207
|
+
});
|
|
2208
|
+
mergeManualRunSnapshotAfterReload({
|
|
2209
|
+
state,
|
|
2210
|
+
jobId,
|
|
2211
|
+
snapshot: postRunSnapshot,
|
|
2212
|
+
removed: postRunRemoved
|
|
2213
|
+
});
|
|
2214
|
+
recomputeNextRunsForMaintenance(state, { recomputeExpired: true });
|
|
2215
|
+
await persist(state);
|
|
2216
|
+
armTimer(state);
|
|
2217
|
+
});
|
|
2218
|
+
} finally {
|
|
2219
|
+
clearCronJobActive(jobId);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
async function run(state, id, mode, opts) {
|
|
2223
|
+
const prepared = await prepareManualRun(state, id, mode, opts);
|
|
2224
|
+
if (!prepared.ok || !prepared.ran) return prepared;
|
|
2225
|
+
await finishPreparedManualRun(state, prepared, mode);
|
|
2226
|
+
return {
|
|
2227
|
+
ok: true,
|
|
2228
|
+
ran: true
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
async function enqueueRun(state, id, mode) {
|
|
2232
|
+
const disposition = await inspectManualRunDisposition(state, id, mode);
|
|
2233
|
+
if (!disposition.ok || !("runnable" in disposition && disposition.runnable)) return disposition;
|
|
2234
|
+
const runId = `manual:${id}:${state.deps.nowMs()}:${nextManualRunId++}`;
|
|
2235
|
+
enqueueCommandInLane("cron", async () => {
|
|
2236
|
+
const result = await run(state, id, mode, { runId });
|
|
2237
|
+
if (result.ok && "ran" in result && !result.ran) state.deps.log.info({
|
|
2238
|
+
jobId: id,
|
|
2239
|
+
runId,
|
|
2240
|
+
reason: result.reason
|
|
2241
|
+
}, "cron: queued manual run skipped before execution");
|
|
2242
|
+
return result;
|
|
2243
|
+
}, {
|
|
2244
|
+
warnAfterMs: 5e3,
|
|
2245
|
+
onWait: (waitMs, queuedAhead) => {
|
|
2246
|
+
state.deps.log.warn({
|
|
2247
|
+
jobId: id,
|
|
2248
|
+
runId,
|
|
2249
|
+
waitMs,
|
|
2250
|
+
queuedAhead
|
|
2251
|
+
}, "cron: queued manual run waiting for an execution slot");
|
|
2252
|
+
}
|
|
2253
|
+
}).catch((err) => {
|
|
2254
|
+
state.deps.log.error({
|
|
2255
|
+
jobId: id,
|
|
2256
|
+
runId,
|
|
2257
|
+
err: String(err)
|
|
2258
|
+
}, "cron: queued manual run background execution failed");
|
|
2259
|
+
});
|
|
2260
|
+
return {
|
|
2261
|
+
ok: true,
|
|
2262
|
+
enqueued: true,
|
|
2263
|
+
runId
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
function wakeNow(state, opts) {
|
|
2267
|
+
return wake(state, opts);
|
|
2268
|
+
}
|
|
2269
|
+
//#endregion
|
|
2270
|
+
//#region src/cron/service/state.ts
|
|
2271
|
+
function createCronServiceState(deps) {
|
|
2272
|
+
return {
|
|
2273
|
+
deps: {
|
|
2274
|
+
...deps,
|
|
2275
|
+
nowMs: deps.nowMs ?? (() => Date.now())
|
|
2276
|
+
},
|
|
2277
|
+
store: null,
|
|
2278
|
+
timer: null,
|
|
2279
|
+
running: false,
|
|
2280
|
+
op: Promise.resolve(),
|
|
2281
|
+
warnedDisabled: false,
|
|
2282
|
+
warnedMissingSessionTargetJobIds: /* @__PURE__ */ new Set(),
|
|
2283
|
+
warnedInvalidPersistedJobKeys: /* @__PURE__ */ new Set(),
|
|
2284
|
+
storeLoadedAtMs: null,
|
|
2285
|
+
storeFileMtimeMs: null
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
//#endregion
|
|
2289
|
+
//#region src/cron/service.ts
|
|
2290
|
+
var CronService = class {
|
|
2291
|
+
constructor(deps) {
|
|
2292
|
+
this.state = createCronServiceState(deps);
|
|
2293
|
+
}
|
|
2294
|
+
async start() {
|
|
2295
|
+
await start(this.state);
|
|
2296
|
+
}
|
|
2297
|
+
stop() {
|
|
2298
|
+
stop(this.state);
|
|
2299
|
+
}
|
|
2300
|
+
async status() {
|
|
2301
|
+
return await status(this.state);
|
|
2302
|
+
}
|
|
2303
|
+
async list(opts) {
|
|
2304
|
+
return await list(this.state, opts);
|
|
2305
|
+
}
|
|
2306
|
+
async listPage(opts) {
|
|
2307
|
+
return await listPage(this.state, opts);
|
|
2308
|
+
}
|
|
2309
|
+
async add(input) {
|
|
2310
|
+
return await add(this.state, input);
|
|
2311
|
+
}
|
|
2312
|
+
async update(id, patch) {
|
|
2313
|
+
return await update(this.state, id, patch);
|
|
2314
|
+
}
|
|
2315
|
+
async remove(id) {
|
|
2316
|
+
return await remove(this.state, id);
|
|
2317
|
+
}
|
|
2318
|
+
async run(id, mode) {
|
|
2319
|
+
return await run(this.state, id, mode);
|
|
2320
|
+
}
|
|
2321
|
+
async enqueueRun(id, mode) {
|
|
2322
|
+
const result = await enqueueRun(this.state, id, mode);
|
|
2323
|
+
if (result.ok && "runnable" in result) throw new Error("cron enqueueRun returned unresolved runnable disposition");
|
|
2324
|
+
return result;
|
|
2325
|
+
}
|
|
2326
|
+
getJob(id) {
|
|
2327
|
+
return this.state.store?.jobs.find((job) => job.id === id);
|
|
2328
|
+
}
|
|
2329
|
+
async readJob(id) {
|
|
2330
|
+
return await readJob(this.state, id);
|
|
2331
|
+
}
|
|
2332
|
+
getDefaultAgentId() {
|
|
2333
|
+
return this.state.deps.defaultAgentId;
|
|
2334
|
+
}
|
|
2335
|
+
wake(opts) {
|
|
2336
|
+
return wakeNow(this.state, opts);
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
//#endregion
|
|
2340
|
+
//#region src/cron/delivery.ts
|
|
2341
|
+
const FAILURE_NOTIFICATION_TIMEOUT_MS = 3e4;
|
|
2342
|
+
const cronDeliveryLogger = getChildLogger({ subsystem: "cron-delivery" });
|
|
2343
|
+
async function resolveCronAnnounceDelivery(params) {
|
|
2344
|
+
const resolvedTarget = await resolveDeliveryTarget(params.cfg, params.agentId, {
|
|
2345
|
+
channel: params.target.channel,
|
|
2346
|
+
to: params.target.to,
|
|
2347
|
+
accountId: params.target.accountId,
|
|
2348
|
+
sessionKey: params.target.sessionKey
|
|
2349
|
+
});
|
|
2350
|
+
if (!resolvedTarget.ok) return {
|
|
2351
|
+
ok: false,
|
|
2352
|
+
error: resolvedTarget.error
|
|
2353
|
+
};
|
|
2354
|
+
const identity = resolveAgentOutboundIdentity(params.cfg, params.agentId);
|
|
2355
|
+
return {
|
|
2356
|
+
ok: true,
|
|
2357
|
+
resolvedTarget,
|
|
2358
|
+
session: buildOutboundSessionContext({
|
|
2359
|
+
cfg: params.cfg,
|
|
2360
|
+
agentId: params.agentId,
|
|
2361
|
+
sessionKey: resolveCronNotificationSessionKey({
|
|
2362
|
+
jobId: params.jobId,
|
|
2363
|
+
sessionKey: params.target.sessionKey
|
|
2364
|
+
})
|
|
2365
|
+
}),
|
|
2366
|
+
identity
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
async function deliverCronAnnouncePayload(params) {
|
|
2370
|
+
const send = await sendDurableMessageBatch({
|
|
2371
|
+
cfg: params.cfg,
|
|
2372
|
+
channel: params.delivery.resolvedTarget.channel,
|
|
2373
|
+
to: params.delivery.resolvedTarget.to,
|
|
2374
|
+
accountId: params.delivery.resolvedTarget.accountId,
|
|
2375
|
+
threadId: params.delivery.resolvedTarget.threadId,
|
|
2376
|
+
payloads: [{ text: params.message }],
|
|
2377
|
+
session: params.delivery.session,
|
|
2378
|
+
identity: params.delivery.identity,
|
|
2379
|
+
bestEffort: false,
|
|
2380
|
+
deps: createOutboundSendDeps(params.deps),
|
|
2381
|
+
signal: params.abortSignal
|
|
2382
|
+
});
|
|
2383
|
+
if (send.status === "failed" || send.status === "partial_failed") throw send.error;
|
|
2384
|
+
}
|
|
2385
|
+
async function sendCronAnnouncePayloadStrict(params) {
|
|
2386
|
+
const delivery = await resolveCronAnnounceDelivery(params);
|
|
2387
|
+
if (!delivery.ok) throw delivery.error;
|
|
2388
|
+
await deliverCronAnnouncePayload({
|
|
2389
|
+
deps: params.deps,
|
|
2390
|
+
cfg: params.cfg,
|
|
2391
|
+
delivery,
|
|
2392
|
+
message: params.message,
|
|
2393
|
+
abortSignal: params.abortSignal
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
async function sendFailureNotificationAnnounce(deps, cfg, agentId, jobId, target, message) {
|
|
2397
|
+
const delivery = await resolveCronAnnounceDelivery({
|
|
2398
|
+
cfg,
|
|
2399
|
+
agentId,
|
|
2400
|
+
jobId,
|
|
2401
|
+
target
|
|
2402
|
+
});
|
|
2403
|
+
if (!delivery.ok) {
|
|
2404
|
+
cronDeliveryLogger.warn({ error: delivery.error.message }, "cron: failed to resolve failure destination target");
|
|
2405
|
+
return;
|
|
2406
|
+
}
|
|
2407
|
+
const abortController = new AbortController();
|
|
2408
|
+
const timeout = setTimeout(() => {
|
|
2409
|
+
abortController.abort();
|
|
2410
|
+
}, FAILURE_NOTIFICATION_TIMEOUT_MS);
|
|
2411
|
+
try {
|
|
2412
|
+
await deliverCronAnnouncePayload({
|
|
2413
|
+
deps,
|
|
2414
|
+
cfg,
|
|
2415
|
+
delivery,
|
|
2416
|
+
message,
|
|
2417
|
+
abortSignal: abortController.signal
|
|
2418
|
+
});
|
|
2419
|
+
} catch (err) {
|
|
2420
|
+
cronDeliveryLogger.warn({
|
|
2421
|
+
err: formatErrorMessage(err),
|
|
2422
|
+
channel: delivery.resolvedTarget.channel,
|
|
2423
|
+
to: delivery.resolvedTarget.to
|
|
2424
|
+
}, "cron: failure destination announce failed");
|
|
2425
|
+
} finally {
|
|
2426
|
+
clearTimeout(timeout);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
//#endregion
|
|
2430
|
+
//#region src/gateway/server-cron-notifications.ts
|
|
2431
|
+
const CRON_WEBHOOK_TIMEOUT_MS = 1e4;
|
|
2432
|
+
function redactWebhookUrl(url) {
|
|
2433
|
+
try {
|
|
2434
|
+
const parsed = new URL(url);
|
|
2435
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
2436
|
+
} catch {
|
|
2437
|
+
return "<invalid-webhook-url>";
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
function resolveCronWebhookTarget(params) {
|
|
2441
|
+
if (normalizeOptionalLowercaseString(params.delivery?.mode) === "webhook") {
|
|
2442
|
+
const url = normalizeHttpWebhookUrl(params.delivery?.to);
|
|
2443
|
+
return url ? {
|
|
2444
|
+
url,
|
|
2445
|
+
source: "delivery"
|
|
2446
|
+
} : null;
|
|
2447
|
+
}
|
|
2448
|
+
if (params.legacyNotify) {
|
|
2449
|
+
const legacyUrl = normalizeHttpWebhookUrl(params.legacyWebhook);
|
|
2450
|
+
if (legacyUrl) return {
|
|
2451
|
+
url: legacyUrl,
|
|
2452
|
+
source: "legacy"
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
return null;
|
|
2456
|
+
}
|
|
2457
|
+
function buildCronWebhookHeaders(webhookToken) {
|
|
2458
|
+
const headers = { "Content-Type": "application/json" };
|
|
2459
|
+
if (webhookToken) headers.Authorization = `Bearer ${webhookToken}`;
|
|
2460
|
+
return headers;
|
|
2461
|
+
}
|
|
2462
|
+
async function postCronWebhook(params) {
|
|
2463
|
+
const abortController = new AbortController();
|
|
2464
|
+
const timeout = setTimeout(() => {
|
|
2465
|
+
abortController.abort();
|
|
2466
|
+
}, CRON_WEBHOOK_TIMEOUT_MS);
|
|
2467
|
+
try {
|
|
2468
|
+
await (await fetchWithSsrFGuard({
|
|
2469
|
+
url: params.webhookUrl,
|
|
2470
|
+
init: {
|
|
2471
|
+
method: "POST",
|
|
2472
|
+
headers: buildCronWebhookHeaders(params.webhookToken),
|
|
2473
|
+
body: JSON.stringify(params.payload),
|
|
2474
|
+
signal: abortController.signal
|
|
2475
|
+
}
|
|
2476
|
+
})).release();
|
|
2477
|
+
} catch (err) {
|
|
2478
|
+
if (err instanceof SsrFBlockedError) params.logger.warn({
|
|
2479
|
+
...params.logContext,
|
|
2480
|
+
reason: formatErrorMessage(err),
|
|
2481
|
+
webhookUrl: redactWebhookUrl(params.webhookUrl)
|
|
2482
|
+
}, params.blockedLog);
|
|
2483
|
+
else params.logger.warn({
|
|
2484
|
+
...params.logContext,
|
|
2485
|
+
err: formatErrorMessage(err),
|
|
2486
|
+
webhookUrl: redactWebhookUrl(params.webhookUrl)
|
|
2487
|
+
}, params.failedLog);
|
|
2488
|
+
} finally {
|
|
2489
|
+
clearTimeout(timeout);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
async function sendGatewayCronFailureAlert(params) {
|
|
2493
|
+
const { agentId, cfg: runtimeConfig } = params.resolveCronAgent(params.job.agentId);
|
|
2494
|
+
const webhookToken = normalizeOptionalString(params.webhookToken);
|
|
2495
|
+
if (params.mode === "webhook" && !params.to) {
|
|
2496
|
+
params.logger.warn({ jobId: params.job.id }, "cron: failure alert webhook mode requires URL, skipping");
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
if (params.mode === "webhook" && params.to) {
|
|
2500
|
+
const webhookUrl = normalizeHttpWebhookUrl(params.to);
|
|
2501
|
+
if (webhookUrl) await postCronWebhook({
|
|
2502
|
+
webhookUrl,
|
|
2503
|
+
webhookToken,
|
|
2504
|
+
payload: {
|
|
2505
|
+
jobId: params.job.id,
|
|
2506
|
+
jobName: params.job.name,
|
|
2507
|
+
message: params.text
|
|
2508
|
+
},
|
|
2509
|
+
logContext: { jobId: params.job.id },
|
|
2510
|
+
blockedLog: "cron: failure alert webhook blocked by SSRF guard",
|
|
2511
|
+
failedLog: "cron: failure alert webhook failed",
|
|
2512
|
+
logger: params.logger
|
|
2513
|
+
});
|
|
2514
|
+
else params.logger.warn({
|
|
2515
|
+
jobId: params.job.id,
|
|
2516
|
+
webhookUrl: redactWebhookUrl(params.to)
|
|
2517
|
+
}, "cron: failure alert webhook URL is invalid, skipping");
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
const abortController = new AbortController();
|
|
2521
|
+
await sendCronAnnouncePayloadStrict({
|
|
2522
|
+
deps: params.deps,
|
|
2523
|
+
cfg: runtimeConfig,
|
|
2524
|
+
agentId,
|
|
2525
|
+
jobId: params.job.id,
|
|
2526
|
+
target: {
|
|
2527
|
+
channel: params.channel,
|
|
2528
|
+
to: params.to,
|
|
2529
|
+
accountId: params.accountId,
|
|
2530
|
+
sessionKey: resolveCronDeliverySessionKey(params.job)
|
|
2531
|
+
},
|
|
2532
|
+
message: params.text,
|
|
2533
|
+
abortSignal: abortController.signal
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
function dispatchGatewayCronFinishedNotifications(params) {
|
|
2537
|
+
const webhookToken = normalizeOptionalString(params.webhookToken);
|
|
2538
|
+
const legacyWebhook = normalizeOptionalString(params.legacyWebhook);
|
|
2539
|
+
const legacyNotify = params.job?.notify === true;
|
|
2540
|
+
const webhookTarget = resolveCronWebhookTarget({
|
|
2541
|
+
delivery: params.job?.delivery && typeof params.job.delivery.mode === "string" ? {
|
|
2542
|
+
mode: params.job.delivery.mode,
|
|
2543
|
+
to: params.job.delivery.to
|
|
2544
|
+
} : void 0,
|
|
2545
|
+
legacyNotify,
|
|
2546
|
+
legacyWebhook
|
|
2547
|
+
});
|
|
2548
|
+
if (!webhookTarget && params.job?.delivery?.mode === "webhook") params.logger.warn({
|
|
2549
|
+
jobId: params.evt.jobId,
|
|
2550
|
+
deliveryTo: params.job.delivery.to
|
|
2551
|
+
}, "cron: skipped webhook delivery, delivery.to must be a valid http(s) URL");
|
|
2552
|
+
if (webhookTarget?.source === "legacy" && !params.warnedLegacyWebhookJobs.has(params.evt.jobId)) {
|
|
2553
|
+
params.warnedLegacyWebhookJobs.add(params.evt.jobId);
|
|
2554
|
+
params.logger.warn({
|
|
2555
|
+
jobId: params.evt.jobId,
|
|
2556
|
+
legacyWebhook: redactWebhookUrl(webhookTarget.url)
|
|
2557
|
+
}, "cron: deprecated notify+cron.webhook fallback in use, migrate to delivery.mode=webhook with delivery.to");
|
|
2558
|
+
}
|
|
2559
|
+
if (webhookTarget && params.evt.summary) (async () => {
|
|
2560
|
+
await postCronWebhook({
|
|
2561
|
+
webhookUrl: webhookTarget.url,
|
|
2562
|
+
webhookToken,
|
|
2563
|
+
payload: params.evt,
|
|
2564
|
+
logContext: { jobId: params.evt.jobId },
|
|
2565
|
+
blockedLog: "cron: webhook delivery blocked by SSRF guard",
|
|
2566
|
+
failedLog: "cron: webhook delivery failed",
|
|
2567
|
+
logger: params.logger
|
|
2568
|
+
});
|
|
2569
|
+
})();
|
|
2570
|
+
dispatchCronFailureDestinationNotifications({
|
|
2571
|
+
evt: params.evt,
|
|
2572
|
+
job: params.job,
|
|
2573
|
+
deps: params.deps,
|
|
2574
|
+
logger: params.logger,
|
|
2575
|
+
resolveCronAgent: params.resolveCronAgent,
|
|
2576
|
+
webhookToken,
|
|
2577
|
+
globalFailureDestination: params.globalFailureDestination
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
function dispatchCronFailureDestinationNotifications(params) {
|
|
2581
|
+
if (params.evt.status !== "error" || !params.job || params.job.delivery?.bestEffort === true) return;
|
|
2582
|
+
const failureMessage = `Cron job "${params.job.name}" failed: ${params.evt.error ?? "unknown error"}`;
|
|
2583
|
+
const failureDest = resolveFailureDestination(params.job, params.globalFailureDestination);
|
|
2584
|
+
const deliverySessionKey = resolveCronDeliverySessionKey(params.job);
|
|
2585
|
+
if (failureDest) {
|
|
2586
|
+
const failurePayload = {
|
|
2587
|
+
jobId: params.job.id,
|
|
2588
|
+
jobName: params.job.name,
|
|
2589
|
+
message: failureMessage,
|
|
2590
|
+
status: params.evt.status,
|
|
2591
|
+
error: params.evt.error,
|
|
2592
|
+
runAtMs: params.evt.runAtMs,
|
|
2593
|
+
durationMs: params.evt.durationMs,
|
|
2594
|
+
nextRunAtMs: params.evt.nextRunAtMs
|
|
2595
|
+
};
|
|
2596
|
+
if (failureDest.mode === "webhook" && failureDest.to) {
|
|
2597
|
+
const webhookUrl = normalizeHttpWebhookUrl(failureDest.to);
|
|
2598
|
+
if (webhookUrl) (async () => {
|
|
2599
|
+
await postCronWebhook({
|
|
2600
|
+
webhookUrl,
|
|
2601
|
+
webhookToken: params.webhookToken,
|
|
2602
|
+
payload: failurePayload,
|
|
2603
|
+
logContext: { jobId: params.evt.jobId },
|
|
2604
|
+
blockedLog: "cron: failure destination webhook blocked by SSRF guard",
|
|
2605
|
+
failedLog: "cron: failure destination webhook failed",
|
|
2606
|
+
logger: params.logger
|
|
2607
|
+
});
|
|
2608
|
+
})();
|
|
2609
|
+
else params.logger.warn({
|
|
2610
|
+
jobId: params.evt.jobId,
|
|
2611
|
+
webhookUrl: redactWebhookUrl(failureDest.to)
|
|
2612
|
+
}, "cron: failure destination webhook URL is invalid, skipping");
|
|
2613
|
+
return;
|
|
2614
|
+
}
|
|
2615
|
+
if (failureDest.mode === "announce") {
|
|
2616
|
+
const { agentId, cfg: runtimeConfig } = params.resolveCronAgent(params.job.agentId);
|
|
2617
|
+
sendFailureNotificationAnnounce(params.deps, runtimeConfig, agentId, params.job.id, {
|
|
2618
|
+
channel: failureDest.channel,
|
|
2619
|
+
to: failureDest.to,
|
|
2620
|
+
accountId: failureDest.accountId,
|
|
2621
|
+
sessionKey: deliverySessionKey
|
|
2622
|
+
}, `⚠️ ${failureMessage}`);
|
|
2623
|
+
}
|
|
2624
|
+
return;
|
|
2625
|
+
}
|
|
2626
|
+
const primaryPlan = resolveCronDeliveryPlan(params.job);
|
|
2627
|
+
if (primaryPlan.mode !== "announce" || !primaryPlan.requested) return;
|
|
2628
|
+
const { agentId, cfg: runtimeConfig } = params.resolveCronAgent(params.job.agentId);
|
|
2629
|
+
sendFailureNotificationAnnounce(params.deps, runtimeConfig, agentId, params.job.id, {
|
|
2630
|
+
channel: primaryPlan.channel,
|
|
2631
|
+
to: primaryPlan.to,
|
|
2632
|
+
accountId: primaryPlan.accountId,
|
|
2633
|
+
sessionKey: deliverySessionKey
|
|
2634
|
+
}, `⚠️ ${failureMessage}`);
|
|
2635
|
+
}
|
|
2636
|
+
//#endregion
|
|
2637
|
+
//#region src/gateway/server-cron.ts
|
|
2638
|
+
/** Pick only the keys whose values are not `undefined` from an object. */
|
|
2639
|
+
function pickDefined(obj, keys) {
|
|
2640
|
+
const result = {};
|
|
2641
|
+
for (const k of keys) if (obj[k] !== void 0) result[k] = obj[k];
|
|
2642
|
+
return result;
|
|
2643
|
+
}
|
|
2644
|
+
function omitExplicitHeartbeatDestination(heartbeat) {
|
|
2645
|
+
if (!heartbeat) return;
|
|
2646
|
+
return {
|
|
2647
|
+
...heartbeat,
|
|
2648
|
+
to: void 0,
|
|
2649
|
+
accountId: void 0
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
function sanitizeCronHeartbeatOverride(heartbeat) {
|
|
2653
|
+
return heartbeat?.target === "last" ? omitExplicitHeartbeatDestination(heartbeat) : heartbeat;
|
|
2654
|
+
}
|
|
2655
|
+
/** Map internal CronJob to the public plugin SDK shape. */
|
|
2656
|
+
function toPluginCronJob(job) {
|
|
2657
|
+
return {
|
|
2658
|
+
id: job.id,
|
|
2659
|
+
agentId: job.agentId,
|
|
2660
|
+
name: job.name,
|
|
2661
|
+
description: job.description,
|
|
2662
|
+
enabled: job.enabled,
|
|
2663
|
+
schedule: job.schedule ? structuredClone(job.schedule) : void 0,
|
|
2664
|
+
sessionTarget: job.sessionTarget,
|
|
2665
|
+
wakeMode: job.wakeMode,
|
|
2666
|
+
payload: job.payload ? structuredClone(job.payload) : void 0,
|
|
2667
|
+
state: {
|
|
2668
|
+
nextRunAtMs: job.state.nextRunAtMs,
|
|
2669
|
+
runningAtMs: job.state.runningAtMs,
|
|
2670
|
+
lastRunAtMs: job.state.lastRunAtMs,
|
|
2671
|
+
lastRunStatus: job.state.lastRunStatus,
|
|
2672
|
+
lastError: job.state.lastError,
|
|
2673
|
+
lastDurationMs: job.state.lastDurationMs,
|
|
2674
|
+
lastDelivered: job.state.lastDelivered,
|
|
2675
|
+
lastDeliveryStatus: job.state.lastDeliveryStatus,
|
|
2676
|
+
lastDeliveryError: job.state.lastDeliveryError,
|
|
2677
|
+
lastFailureNotificationDelivered: job.state.lastFailureNotificationDelivered,
|
|
2678
|
+
lastFailureNotificationDeliveryStatus: job.state.lastFailureNotificationDeliveryStatus,
|
|
2679
|
+
lastFailureNotificationDeliveryError: job.state.lastFailureNotificationDeliveryError
|
|
2680
|
+
},
|
|
2681
|
+
createdAtMs: job.createdAtMs,
|
|
2682
|
+
updatedAtMs: job.updatedAtMs
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
function buildGatewayCronService(params) {
|
|
2686
|
+
const cronLogger = getChildLogger({ module: "cron" });
|
|
2687
|
+
const storePath = resolveCronStorePath(params.cfg.cron?.store);
|
|
2688
|
+
const cronEnabled = process.env.DAOCORE_SKIP_CRON !== "1" && params.cfg.cron?.enabled !== false;
|
|
2689
|
+
const findAgentEntry = (cfg, agentId) => Array.isArray(cfg.agents?.list) ? cfg.agents.list.find((entry) => entry && typeof entry.id === "string" && normalizeAgentId(entry.id) === agentId) : void 0;
|
|
2690
|
+
const hasConfiguredAgent = (cfg, agentId) => Boolean(findAgentEntry(cfg, agentId));
|
|
2691
|
+
const mergeRuntimeAgentConfig = (runtimeConfig, requestedAgentId) => {
|
|
2692
|
+
if (hasConfiguredAgent(runtimeConfig, requestedAgentId)) return runtimeConfig;
|
|
2693
|
+
const fallbackAgentEntry = findAgentEntry(params.cfg, requestedAgentId);
|
|
2694
|
+
if (!fallbackAgentEntry) return runtimeConfig;
|
|
2695
|
+
const startupAgents = params.cfg.agents;
|
|
2696
|
+
const runtimeAgents = runtimeConfig.agents;
|
|
2697
|
+
return {
|
|
2698
|
+
...runtimeConfig,
|
|
2699
|
+
agents: {
|
|
2700
|
+
...startupAgents,
|
|
2701
|
+
...runtimeAgents,
|
|
2702
|
+
defaults: {
|
|
2703
|
+
...startupAgents?.defaults,
|
|
2704
|
+
...runtimeAgents?.defaults
|
|
2705
|
+
},
|
|
2706
|
+
list: [...runtimeAgents?.list ?? [], fallbackAgentEntry]
|
|
2707
|
+
}
|
|
2708
|
+
};
|
|
2709
|
+
};
|
|
2710
|
+
const resolveCronAgent = (requested) => {
|
|
2711
|
+
const runtimeConfig = getRuntimeConfig();
|
|
2712
|
+
const normalized = typeof requested === "string" && requested.trim() ? normalizeAgentId(requested) : void 0;
|
|
2713
|
+
const effectiveConfig = normalized !== void 0 ? mergeRuntimeAgentConfig(runtimeConfig, normalized) : runtimeConfig;
|
|
2714
|
+
return {
|
|
2715
|
+
agentId: normalized !== void 0 && hasConfiguredAgent(effectiveConfig, normalized) ? normalized : resolveDefaultAgentId(effectiveConfig),
|
|
2716
|
+
cfg: effectiveConfig
|
|
2717
|
+
};
|
|
2718
|
+
};
|
|
2719
|
+
const resolveCronSessionKey = (params) => {
|
|
2720
|
+
const requested = params.requestedSessionKey?.trim();
|
|
2721
|
+
if (!requested) return resolveAgentMainSessionKey({
|
|
2722
|
+
cfg: params.runtimeConfig,
|
|
2723
|
+
agentId: params.agentId
|
|
2724
|
+
});
|
|
2725
|
+
const candidate = toAgentStoreSessionKey({
|
|
2726
|
+
agentId: params.agentId,
|
|
2727
|
+
requestKey: requested,
|
|
2728
|
+
mainKey: params.runtimeConfig.session?.mainKey
|
|
2729
|
+
});
|
|
2730
|
+
const canonical = canonicalizeMainSessionAlias({
|
|
2731
|
+
cfg: params.runtimeConfig,
|
|
2732
|
+
agentId: params.agentId,
|
|
2733
|
+
sessionKey: candidate
|
|
2734
|
+
});
|
|
2735
|
+
if (canonical !== "global") {
|
|
2736
|
+
if (normalizeAgentId(resolveAgentIdFromSessionKey(canonical)) !== normalizeAgentId(params.agentId)) return resolveAgentMainSessionKey({
|
|
2737
|
+
cfg: params.runtimeConfig,
|
|
2738
|
+
agentId: params.agentId
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
return resolveMainScopedEventSessionKey({
|
|
2742
|
+
cfg: params.runtimeConfig,
|
|
2743
|
+
sessionKey: canonical,
|
|
2744
|
+
agentId: params.agentId
|
|
2745
|
+
}) ?? canonical;
|
|
2746
|
+
};
|
|
2747
|
+
const resolveCronTarget = (opts) => {
|
|
2748
|
+
const requestedAgentId = typeof opts?.agentId === "string" && opts.agentId.trim() ? normalizeAgentId(opts.agentId) : void 0;
|
|
2749
|
+
const requestedSessionKey = typeof opts?.sessionKey === "string" && opts.sessionKey.trim() ? opts.sessionKey : void 0;
|
|
2750
|
+
if (opts?.preserveUntargeted && !requestedAgentId && !requestedSessionKey) return {
|
|
2751
|
+
runtimeConfig: getRuntimeConfig(),
|
|
2752
|
+
agentId: void 0,
|
|
2753
|
+
sessionKey: void 0
|
|
2754
|
+
};
|
|
2755
|
+
const derivedAgentId = requestedSessionKey && parseAgentSessionKey(requestedSessionKey) ? resolveAgentIdFromSessionKey(requestedSessionKey) : void 0;
|
|
2756
|
+
const { agentId: resolvedAgentId, cfg: runtimeConfig } = resolveCronAgent(requestedAgentId ?? derivedAgentId);
|
|
2757
|
+
const agentId = resolvedAgentId || void 0;
|
|
2758
|
+
const resolvedSessionKey = agentId ? resolveCronSessionKey({
|
|
2759
|
+
runtimeConfig,
|
|
2760
|
+
agentId,
|
|
2761
|
+
requestedSessionKey
|
|
2762
|
+
}) : void 0;
|
|
2763
|
+
return {
|
|
2764
|
+
runtimeConfig,
|
|
2765
|
+
agentId,
|
|
2766
|
+
sessionKey: resolvedSessionKey && runtimeConfig.session?.scope === "global" ? resolveEventSessionKey(resolvedSessionKey, runtimeConfig.session?.mainKey, runtimeConfig.session?.scope) : resolvedSessionKey
|
|
2767
|
+
};
|
|
2768
|
+
};
|
|
2769
|
+
const resolveCronHeartbeatOverride = (params) => {
|
|
2770
|
+
if (!params.heartbeat) return;
|
|
2771
|
+
const agentEntry = params.agentId !== void 0 ? findAgentEntry(params.runtimeConfig, params.agentId) : void 0;
|
|
2772
|
+
const agentHeartbeat = agentEntry && typeof agentEntry === "object" ? agentEntry.heartbeat : void 0;
|
|
2773
|
+
return sanitizeCronHeartbeatOverride({
|
|
2774
|
+
...params.runtimeConfig.agents?.defaults?.heartbeat,
|
|
2775
|
+
...agentHeartbeat,
|
|
2776
|
+
...params.heartbeat
|
|
2777
|
+
});
|
|
2778
|
+
};
|
|
2779
|
+
const defaultAgentId = resolveDefaultAgentId(params.cfg);
|
|
2780
|
+
const runLogPrune = resolveCronRunLogPruneOptions(params.cfg.cron?.runLog);
|
|
2781
|
+
const resolveSessionStorePath = (agentId) => resolveStorePath(params.cfg.session?.store, { agentId: agentId ?? defaultAgentId });
|
|
2782
|
+
const sessionStorePath = resolveSessionStorePath(defaultAgentId);
|
|
2783
|
+
const warnedLegacyWebhookJobs = /* @__PURE__ */ new Set();
|
|
2784
|
+
const runCronChangedHook = (evt) => {
|
|
2785
|
+
const hookRunner = getGlobalHookRunner();
|
|
2786
|
+
if (!hookRunner?.hasHooks("cron_changed")) return;
|
|
2787
|
+
const hookCtx = {
|
|
2788
|
+
config: getRuntimeConfig(),
|
|
2789
|
+
getCron: () => cron
|
|
2790
|
+
};
|
|
2791
|
+
hookRunner.runCronChanged(evt, hookCtx).catch((err) => {
|
|
2792
|
+
cronLogger.warn({
|
|
2793
|
+
err: formatErrorMessage(err),
|
|
2794
|
+
jobId: evt.jobId
|
|
2795
|
+
}, "cron_changed hook failed");
|
|
2796
|
+
});
|
|
2797
|
+
};
|
|
2798
|
+
const cron = new CronService({
|
|
2799
|
+
storePath,
|
|
2800
|
+
cronEnabled,
|
|
2801
|
+
cronConfig: params.cfg.cron,
|
|
2802
|
+
defaultAgentId,
|
|
2803
|
+
resolveSessionStorePath,
|
|
2804
|
+
sessionStorePath,
|
|
2805
|
+
enqueueSystemEvent: (text, opts) => {
|
|
2806
|
+
const { sessionKey } = resolveCronTarget(opts);
|
|
2807
|
+
if (!sessionKey) throw new Error("Cron system event target did not resolve a session key.");
|
|
2808
|
+
enqueueSystemEvent(text, {
|
|
2809
|
+
sessionKey,
|
|
2810
|
+
contextKey: opts?.contextKey,
|
|
2811
|
+
deliveryContext: opts?.deliveryContext
|
|
2812
|
+
});
|
|
2813
|
+
},
|
|
2814
|
+
requestHeartbeat: (opts) => {
|
|
2815
|
+
const { agentId, sessionKey } = resolveCronTarget({
|
|
2816
|
+
...opts,
|
|
2817
|
+
preserveUntargeted: true
|
|
2818
|
+
});
|
|
2819
|
+
requestHeartbeat({
|
|
2820
|
+
source: opts?.source ?? "cron",
|
|
2821
|
+
intent: opts?.intent ?? "event",
|
|
2822
|
+
reason: opts?.reason,
|
|
2823
|
+
agentId,
|
|
2824
|
+
sessionKey,
|
|
2825
|
+
heartbeat: sanitizeCronHeartbeatOverride(opts?.heartbeat)
|
|
2826
|
+
});
|
|
2827
|
+
},
|
|
2828
|
+
runHeartbeatOnce: async (opts) => {
|
|
2829
|
+
const { runtimeConfig, agentId, sessionKey } = resolveCronTarget({
|
|
2830
|
+
...opts,
|
|
2831
|
+
preserveUntargeted: true
|
|
2832
|
+
});
|
|
2833
|
+
return await runHeartbeatOnce({
|
|
2834
|
+
cfg: runtimeConfig,
|
|
2835
|
+
source: opts?.source ?? "cron",
|
|
2836
|
+
intent: opts?.intent ?? "event",
|
|
2837
|
+
reason: opts?.reason,
|
|
2838
|
+
agentId,
|
|
2839
|
+
sessionKey,
|
|
2840
|
+
heartbeat: resolveCronHeartbeatOverride({
|
|
2841
|
+
runtimeConfig,
|
|
2842
|
+
agentId,
|
|
2843
|
+
heartbeat: opts?.heartbeat
|
|
2844
|
+
}),
|
|
2845
|
+
deps: {
|
|
2846
|
+
...params.deps,
|
|
2847
|
+
runtime: defaultRuntime
|
|
2848
|
+
}
|
|
2849
|
+
});
|
|
2850
|
+
},
|
|
2851
|
+
runIsolatedAgentJob: async ({ job, message, abortSignal, onExecutionStarted, onExecutionPhase }) => {
|
|
2852
|
+
const { agentId, cfg: runtimeConfig } = resolveCronAgent(job.agentId);
|
|
2853
|
+
const sessionKey = resolveCronSessionTargetSessionKey(job.sessionTarget) ?? `cron:${job.id}`;
|
|
2854
|
+
try {
|
|
2855
|
+
return await runCronIsolatedAgentTurn({
|
|
2856
|
+
cfg: runtimeConfig,
|
|
2857
|
+
deps: params.deps,
|
|
2858
|
+
job,
|
|
2859
|
+
message,
|
|
2860
|
+
abortSignal,
|
|
2861
|
+
onExecutionStarted,
|
|
2862
|
+
onExecutionPhase,
|
|
2863
|
+
agentId,
|
|
2864
|
+
sessionKey,
|
|
2865
|
+
lane: "cron"
|
|
2866
|
+
});
|
|
2867
|
+
} finally {
|
|
2868
|
+
await cleanupBrowserSessionsForLifecycleEnd({
|
|
2869
|
+
sessionKeys: [sessionKey],
|
|
2870
|
+
onWarn: (msg) => cronLogger.warn({ jobId: job.id }, msg)
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2873
|
+
},
|
|
2874
|
+
cleanupTimedOutAgentRun: async ({ job, execution }) => {
|
|
2875
|
+
if (!execution?.sessionId) return;
|
|
2876
|
+
const result = await abortAndDrainEmbeddedPiRun({
|
|
2877
|
+
sessionId: execution.sessionId,
|
|
2878
|
+
sessionKey: execution.sessionKey,
|
|
2879
|
+
settleMs: 15e3,
|
|
2880
|
+
forceClear: true,
|
|
2881
|
+
reason: "cron_timeout"
|
|
2882
|
+
});
|
|
2883
|
+
cronLogger.warn({
|
|
2884
|
+
jobId: job.id,
|
|
2885
|
+
sessionId: execution.sessionId,
|
|
2886
|
+
sessionKey: execution.sessionKey,
|
|
2887
|
+
aborted: result.aborted,
|
|
2888
|
+
drained: result.drained,
|
|
2889
|
+
forceCleared: result.forceCleared
|
|
2890
|
+
}, "cron: cleaned up timed-out agent run");
|
|
2891
|
+
},
|
|
2892
|
+
sendCronFailureAlert: async ({ job, text, channel, to, mode, accountId }) => await sendGatewayCronFailureAlert({
|
|
2893
|
+
deps: params.deps,
|
|
2894
|
+
logger: cronLogger,
|
|
2895
|
+
resolveCronAgent,
|
|
2896
|
+
webhookToken: params.cfg.cron?.webhookToken,
|
|
2897
|
+
job,
|
|
2898
|
+
text,
|
|
2899
|
+
channel,
|
|
2900
|
+
to,
|
|
2901
|
+
mode,
|
|
2902
|
+
accountId
|
|
2903
|
+
}),
|
|
2904
|
+
log: getChildLogger({
|
|
2905
|
+
module: "cron",
|
|
2906
|
+
storePath
|
|
2907
|
+
}),
|
|
2908
|
+
onEvent: (evt) => {
|
|
2909
|
+
params.broadcast("cron", evt, { dropIfSlow: true });
|
|
2910
|
+
const jobSnapshot = evt.job ?? cron.getJob(evt.jobId);
|
|
2911
|
+
const pluginJob = jobSnapshot ? toPluginCronJob(jobSnapshot) : void 0;
|
|
2912
|
+
runCronChangedHook({
|
|
2913
|
+
action: evt.action,
|
|
2914
|
+
jobId: evt.jobId,
|
|
2915
|
+
...pluginJob ? { job: pluginJob } : {},
|
|
2916
|
+
sessionTarget: jobSnapshot?.sessionTarget,
|
|
2917
|
+
agentId: jobSnapshot?.agentId,
|
|
2918
|
+
...pickDefined(evt, [
|
|
2919
|
+
"runAtMs",
|
|
2920
|
+
"durationMs",
|
|
2921
|
+
"status",
|
|
2922
|
+
"error",
|
|
2923
|
+
"summary",
|
|
2924
|
+
"delivered",
|
|
2925
|
+
"deliveryStatus",
|
|
2926
|
+
"deliveryError",
|
|
2927
|
+
"sessionId",
|
|
2928
|
+
"sessionKey",
|
|
2929
|
+
"runId",
|
|
2930
|
+
"nextRunAtMs",
|
|
2931
|
+
"model",
|
|
2932
|
+
"provider"
|
|
2933
|
+
])
|
|
2934
|
+
});
|
|
2935
|
+
if (evt.action === "finished") {
|
|
2936
|
+
dispatchGatewayCronFinishedNotifications({
|
|
2937
|
+
evt,
|
|
2938
|
+
job: evt.job ?? cron.getJob(evt.jobId),
|
|
2939
|
+
deps: params.deps,
|
|
2940
|
+
logger: cronLogger,
|
|
2941
|
+
resolveCronAgent,
|
|
2942
|
+
webhookToken: params.cfg.cron?.webhookToken,
|
|
2943
|
+
legacyWebhook: params.cfg.cron?.webhook,
|
|
2944
|
+
globalFailureDestination: params.cfg.cron?.failureDestination,
|
|
2945
|
+
warnedLegacyWebhookJobs
|
|
2946
|
+
});
|
|
2947
|
+
const logPath = resolveCronRunLogPath({
|
|
2948
|
+
storePath,
|
|
2949
|
+
jobId: evt.jobId
|
|
2950
|
+
});
|
|
2951
|
+
appendCronRunLog(logPath, {
|
|
2952
|
+
ts: Date.now(),
|
|
2953
|
+
jobId: evt.jobId,
|
|
2954
|
+
action: "finished",
|
|
2955
|
+
status: evt.status,
|
|
2956
|
+
error: evt.error,
|
|
2957
|
+
summary: evt.summary,
|
|
2958
|
+
diagnostics: evt.diagnostics,
|
|
2959
|
+
delivered: evt.delivered,
|
|
2960
|
+
deliveryStatus: evt.deliveryStatus,
|
|
2961
|
+
deliveryError: evt.deliveryError,
|
|
2962
|
+
failureNotificationDelivery: evt.failureNotificationDelivery,
|
|
2963
|
+
delivery: evt.delivery,
|
|
2964
|
+
sessionId: evt.sessionId,
|
|
2965
|
+
sessionKey: evt.sessionKey,
|
|
2966
|
+
runId: evt.runId,
|
|
2967
|
+
runAtMs: evt.runAtMs,
|
|
2968
|
+
durationMs: evt.durationMs,
|
|
2969
|
+
nextRunAtMs: evt.nextRunAtMs,
|
|
2970
|
+
model: evt.model,
|
|
2971
|
+
provider: evt.provider,
|
|
2972
|
+
usage: evt.usage
|
|
2973
|
+
}, runLogPrune).catch((err) => {
|
|
2974
|
+
cronLogger.warn({
|
|
2975
|
+
err: String(err),
|
|
2976
|
+
logPath
|
|
2977
|
+
}, "cron: run log append failed");
|
|
2978
|
+
});
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
});
|
|
2982
|
+
return {
|
|
2983
|
+
cron,
|
|
2984
|
+
storePath,
|
|
2985
|
+
cronEnabled
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
//#endregion
|
|
2989
|
+
export { buildGatewayCronService as t };
|