@poolzin/pool-bot 2026.2.21 → 2026.2.22
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/CHANGELOG.md +17 -0
- package/dist/agents/api-key-rotation.js +47 -0
- package/dist/agents/apply-patch-update.js +19 -9
- package/dist/agents/apply-patch.js +72 -47
- package/dist/agents/bash-tools.exec.js +141 -559
- package/dist/agents/cli-backends.js +49 -6
- package/dist/agents/cli-runner/helpers.js +69 -152
- package/dist/agents/cli-runner.js +70 -19
- package/dist/agents/identity.js +20 -1
- package/dist/agents/image-sanitization.js +9 -0
- package/dist/agents/live-auth-keys.js +123 -26
- package/dist/agents/live-model-filter.js +13 -4
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-forward-compat.js +60 -23
- package/dist/agents/model-selection.js +134 -41
- package/dist/agents/pi-auth-json.js +2 -2
- package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
- package/dist/agents/pi-embedded-helpers/errors.js +140 -15
- package/dist/agents/pi-embedded-helpers/images.js +22 -12
- package/dist/agents/pi-embedded-helpers.js +2 -2
- package/dist/agents/pi-embedded-runner/abort.js +10 -3
- package/dist/agents/pi-embedded-runner/compact.js +230 -32
- package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
- package/dist/agents/pi-embedded-runner/google.js +109 -19
- package/dist/agents/pi-embedded-runner/history.js +35 -17
- package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
- package/dist/agents/pi-embedded-runner/run/images.js +81 -55
- package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
- package/dist/agents/pi-embedded-runner/run.js +193 -25
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
- package/dist/agents/pi-embedded-runner/runs.js +17 -8
- package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
- package/dist/agents/pi-embedded-runner.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
- package/dist/agents/pi-embedded-subscribe.js +37 -0
- package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
- package/dist/agents/pi-model-discovery.js +9 -2
- package/dist/agents/pi-tool-definition-adapter.js +60 -8
- package/dist/agents/pi-tools.before-tool-call.js +1 -1
- package/dist/agents/pi-tools.js +113 -94
- package/dist/agents/pi-tools.read.js +337 -38
- package/dist/agents/poolbot-tools.js +14 -5
- package/dist/agents/sandbox/docker.js +10 -5
- package/dist/agents/sandbox/registry.js +96 -46
- package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
- package/dist/agents/sandbox-paths.js +43 -10
- package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
- package/dist/agents/session-tool-result-guard.js +39 -39
- package/dist/agents/session-transcript-repair.js +36 -33
- package/dist/agents/session-write-lock.js +62 -44
- package/dist/agents/skills/frontmatter.js +49 -88
- package/dist/agents/skills/workspace.js +335 -28
- package/dist/agents/subagent-announce.js +508 -174
- package/dist/agents/subagent-registry.js +45 -4
- package/dist/agents/subagent-spawn.js +16 -33
- package/dist/agents/system-prompt-report.js +27 -10
- package/dist/agents/system-prompt.js +26 -32
- package/dist/agents/tool-call-id.js +69 -17
- package/dist/agents/tool-display-common.js +1 -1
- package/dist/agents/tool-images.js +64 -31
- package/dist/agents/tools/canvas-tool.js +17 -11
- package/dist/agents/tools/common.js +37 -19
- package/dist/agents/tools/cron-tool.js +40 -38
- package/dist/agents/tools/gateway.js +70 -2
- package/dist/agents/tools/message-tool.js +181 -40
- package/dist/agents/tools/nodes-tool.js +128 -36
- package/dist/agents/tools/nodes-utils.js +12 -38
- package/dist/agents/tools/session-status-tool.js +24 -71
- package/dist/agents/tools/sessions-helpers.js +38 -210
- package/dist/agents/tools/sessions-spawn-tool.js +28 -198
- package/dist/agents/tools/telegram-actions.js +58 -7
- package/dist/agents/tools/web-fetch-utils.js +112 -7
- package/dist/agents/tools/web-fetch.js +279 -175
- package/dist/agents/tools/web-shared.js +71 -8
- package/dist/agents/usage.js +25 -16
- package/dist/auto-reply/commands-registry.data.js +85 -11
- package/dist/auto-reply/dispatch.js +40 -21
- package/dist/auto-reply/reply/abort.js +102 -33
- package/dist/auto-reply/reply/commands-core.js +82 -33
- package/dist/auto-reply/reply/commands-export-session.js +1 -1
- package/dist/auto-reply/reply/commands-info.js +41 -12
- package/dist/auto-reply/reply/commands-subagents.js +352 -100
- package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
- package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
- package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
- package/dist/auto-reply/reply/inbound-meta.js +12 -1
- package/dist/auto-reply/reply/mentions.js +18 -11
- package/dist/auto-reply/reply/normalize-reply.js +17 -8
- package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
- package/dist/auto-reply/reply/session.js +102 -21
- package/dist/auto-reply/reply/streaming-directives.js +16 -5
- package/dist/auto-reply/status.js +73 -50
- package/dist/browser/extension-relay.js +3 -3
- package/dist/browser/http-auth.js +1 -1
- package/dist/browser/paths.js +2 -2
- package/dist/build-info.json +3 -3
- package/dist/channels/allowlist-match.js +20 -0
- package/dist/channels/allowlists/resolve-utils.js +65 -2
- package/dist/channels/chat-type.js +8 -4
- package/dist/channels/dock.js +127 -35
- package/dist/channels/draft-stream-loop.js +6 -2
- package/dist/channels/plugins/actions/telegram.js +42 -18
- package/dist/channels/plugins/allowlist-match.js +1 -1
- package/dist/channels/plugins/group-mentions.js +51 -41
- package/dist/channels/plugins/message-action-names.js +2 -0
- package/dist/channels/plugins/message-actions.js +24 -5
- package/dist/channels/plugins/normalize/discord.js +26 -4
- package/dist/channels/plugins/normalize/signal.js +35 -22
- package/dist/channels/plugins/onboarding/helpers.js +8 -26
- package/dist/channels/plugins/outbound/imessage.js +15 -14
- package/dist/channels/registry.js +20 -7
- package/dist/cli/acp-cli.js +7 -5
- package/dist/cli/browser-cli-extension.js +25 -12
- package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
- package/dist/cli/browser-cli-state.js +101 -145
- package/dist/cli/command-options.js +28 -0
- package/dist/cli/completion-cli.js +6 -6
- package/dist/cli/cron-cli/register.cron-add.js +25 -1
- package/dist/cli/cron-cli/register.cron-edit.js +44 -0
- package/dist/cli/cron-cli/shared.js +7 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
- package/dist/cli/daemon-cli/lifecycle.js +23 -247
- package/dist/cli/daemon-cli/register-service-commands.js +25 -4
- package/dist/cli/daemon-cli.js +1 -0
- package/dist/cli/devices-cli.js +33 -20
- package/dist/cli/gateway-cli/register.js +37 -105
- package/dist/cli/gateway-cli/run.js +49 -11
- package/dist/cli/nodes-camera.js +59 -4
- package/dist/cli/nodes-cli/register.camera.js +27 -24
- package/dist/cli/nodes-cli/rpc.js +21 -38
- package/dist/cli/qr-cli.js +2 -2
- package/dist/cli/skills-cli.format.js +2 -2
- package/dist/cli/update-cli/progress.js +2 -2
- package/dist/cli/update-cli/restart-helper.js +28 -7
- package/dist/cli/update-cli/shared.js +7 -7
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +14 -8
- package/dist/cli/update-cli/wizard.js +2 -2
- package/dist/cli/update-cli.js +21 -1027
- package/dist/commands/auth-choice.apply.anthropic.js +10 -2
- package/dist/commands/channels/add-mutators.js +3 -35
- package/dist/commands/channels/add.js +39 -51
- package/dist/commands/config-validation.js +1 -1
- package/dist/commands/configure.gateway-auth.js +52 -15
- package/dist/commands/configure.gateway.js +84 -40
- package/dist/commands/doctor-completion.js +3 -3
- package/dist/commands/doctor-config-flow.js +536 -16
- package/dist/commands/doctor-gateway-services.js +103 -79
- package/dist/commands/doctor-memory-search.js +9 -9
- package/dist/commands/doctor-platform-notes.js +57 -30
- package/dist/commands/doctor-prompter.js +26 -15
- package/dist/commands/doctor-session-locks.js +1 -1
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/model-picker.js +120 -95
- package/dist/commands/models/set.js +2 -21
- package/dist/commands/models/shared.js +65 -37
- package/dist/commands/onboard-helpers.js +81 -39
- package/dist/commands/openai-codex-oauth.js +1 -1
- package/dist/commands/sessions.js +52 -53
- package/dist/commands/status.summary.js +52 -34
- package/dist/commands/test-wizard-helpers.js +2 -2
- package/dist/config/defaults.js +79 -42
- package/dist/config/group-policy.js +50 -18
- package/dist/config/includes.js +37 -10
- package/dist/config/schema.help.js +5 -4
- package/dist/config/schema.hints.js +2 -2
- package/dist/config/schema.labels.js +1 -0
- package/dist/config/sessions/group.js +12 -11
- package/dist/config/sessions/paths.js +137 -11
- package/dist/config/sessions/store.js +185 -65
- package/dist/config/sessions/types.js +15 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/telegram-custom-commands.js +3 -2
- package/dist/config/types.js +2 -0
- package/dist/config/zod-schema.agent-defaults.js +6 -27
- package/dist/config/zod-schema.agent-runtime.js +171 -79
- package/dist/config/zod-schema.providers-core.js +138 -65
- package/dist/config/zod-schema.session.js +49 -22
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/run.js +224 -57
- package/dist/cron/normalize.js +48 -45
- package/dist/cron/run-log.js +14 -0
- package/dist/cron/service/jobs.js +190 -28
- package/dist/cron/service/normalize.js +29 -11
- package/dist/cron/service/store.js +30 -44
- package/dist/cron/service/timer.js +182 -96
- package/dist/cron/service.js +3 -0
- package/dist/cron/stagger.js +37 -0
- package/dist/daemon/inspect.js +132 -92
- package/dist/daemon/runtime-paths.js +25 -4
- package/dist/daemon/service-audit.js +47 -16
- package/dist/discord/accounts.js +23 -20
- package/dist/discord/monitor/agent-components.js +1115 -219
- package/dist/discord/monitor/allow-list.js +114 -34
- package/dist/discord/monitor/listeners.js +204 -97
- package/dist/discord/monitor/message-handler.js +21 -10
- package/dist/discord/monitor/message-handler.preflight.js +195 -101
- package/dist/discord/monitor/message-handler.process.js +384 -123
- package/dist/discord/monitor/message-utils.js +86 -23
- package/dist/discord/monitor/native-command.js +77 -57
- package/dist/discord/monitor/provider.js +122 -117
- package/dist/discord/monitor/reply-context.js +20 -16
- package/dist/discord/monitor/reply-delivery.js +40 -8
- package/dist/discord/monitor/rest-fetch.js +22 -0
- package/dist/discord/monitor/threading.js +117 -24
- package/dist/discord/send.js +2 -1
- package/dist/discord/send.outbound.js +124 -11
- package/dist/discord/send.shared.js +112 -72
- package/dist/discord/voice-message.js +3 -3
- package/dist/gateway/auth.js +119 -44
- package/dist/gateway/call.js +76 -34
- package/dist/gateway/channel-health-monitor.js +57 -50
- package/dist/gateway/client.js +63 -29
- package/dist/gateway/control-ui-contract.js +1 -1
- package/dist/gateway/gateway-config-prompts.shared.js +2 -2
- package/dist/gateway/net.js +109 -1
- package/dist/gateway/protocol/index.js +5 -8
- package/dist/gateway/protocol/schema/agent.js +19 -1
- package/dist/gateway/protocol/schema/channels.js +21 -0
- package/dist/gateway/protocol/schema/cron.js +43 -30
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
- package/dist/gateway/protocol/schema/sessions.js +5 -1
- package/dist/gateway/protocol/schema.js +0 -1
- package/dist/gateway/server/presence-events.js +12 -0
- package/dist/gateway/server/ws-connection/message-handler.js +203 -212
- package/dist/gateway/server/ws-connection.js +58 -21
- package/dist/gateway/server-broadcast.js +18 -13
- package/dist/gateway/server-cron.js +177 -10
- package/dist/gateway/server-methods/agent-job.js +131 -38
- package/dist/gateway/server-methods/send.js +60 -14
- package/dist/gateway/server-methods/sessions.js +160 -96
- package/dist/gateway/server-methods/system.js +5 -7
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +24 -8
- package/dist/gateway/server-node-events.js +278 -68
- package/dist/gateway/session-utils.fs.js +316 -75
- package/dist/gateway/session-utils.js +224 -70
- package/dist/gateway/sessions-patch.js +63 -20
- package/dist/gateway/test-temp-config.js +1 -1
- package/dist/gateway/tools-invoke-http.js +118 -70
- package/dist/gateway/ws-log.js +135 -107
- package/dist/hooks/frontmatter.js +36 -82
- package/dist/hooks/install.js +149 -139
- package/dist/hooks/internal-hooks.js +29 -4
- package/dist/hooks/plugin-hooks.js +2 -1
- package/dist/imessage/monitor/deliver.js +10 -4
- package/dist/imessage/monitor/monitor-provider.js +138 -375
- package/dist/imessage/monitor/runtime.js +4 -8
- package/dist/imessage/send.js +65 -19
- package/dist/infra/exec-approvals-allowlist.js +7 -0
- package/dist/infra/exec-approvals.js +35 -920
- package/dist/infra/exec-safe-bin-trust.js +64 -0
- package/dist/infra/heartbeat-runner.js +207 -134
- package/dist/infra/heartbeat-wake.js +183 -22
- package/dist/infra/install-source-utils.js +47 -0
- package/dist/infra/net/ssrf.js +170 -36
- package/dist/infra/outbound/deliver.js +224 -58
- package/dist/infra/outbound/message-action-spec.js +12 -5
- package/dist/infra/outbound/outbound-session.js +27 -25
- package/dist/infra/poolbot-root.js +32 -22
- package/dist/infra/ports.js +14 -11
- package/dist/infra/skills-remote.js +48 -37
- package/dist/infra/system-events.js +25 -11
- package/dist/infra/system-presence.js +26 -33
- package/dist/infra/tmp-poolbot-dir.js +81 -2
- package/dist/infra/wsl.js +37 -1
- package/dist/line/bot-message-context.js +163 -191
- package/dist/logging/subsystem.js +59 -22
- package/dist/markdown/ir.js +124 -50
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/runner.entries.js +42 -25
- package/dist/media-understanding/runner.js +53 -488
- package/dist/memory/embeddings-gemini.js +53 -38
- package/dist/memory/manager-embedding-ops.js +48 -69
- package/dist/pairing/pairing-store.js +178 -119
- package/dist/plugin-sdk/index.js +34 -6
- package/dist/plugins/hooks.js +135 -14
- package/dist/plugins/install.js +190 -152
- package/dist/polls.js +11 -0
- package/dist/routing/resolve-route.js +190 -56
- package/dist/routing/session-key.js +38 -22
- package/dist/runtime.js +35 -9
- package/dist/security/audit-channel.js +1 -1
- package/dist/sessions/session-key-utils.js +29 -11
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-types.js +1 -0
- package/dist/shared/string-normalization.js +15 -0
- package/dist/signal/monitor/event-handler.js +68 -36
- package/dist/signal/send.js +29 -37
- package/dist/slack/monitor/allow-list.js +10 -11
- package/dist/slack/monitor/commands.js +14 -3
- package/dist/slack/monitor/events/interactions.js +4 -4
- package/dist/slack/monitor/media.js +224 -16
- package/dist/slack/monitor/message-handler/dispatch.js +247 -13
- package/dist/slack/monitor/message-handler/prepare.js +128 -45
- package/dist/slack/monitor/slash.js +357 -144
- package/dist/slack/streaming.js +77 -0
- package/dist/telegram/accounts.js +40 -13
- package/dist/telegram/allowed-updates.js +3 -0
- package/dist/telegram/bot/delivery.js +129 -66
- package/dist/telegram/bot/helpers.js +136 -122
- package/dist/telegram/bot-handlers.js +600 -339
- package/dist/telegram/bot-message-context.js +115 -73
- package/dist/telegram/bot-message-dispatch.js +235 -104
- package/dist/telegram/bot-native-command-menu.js +3 -1
- package/dist/telegram/bot-native-commands.js +213 -193
- package/dist/telegram/bot.js +24 -132
- package/dist/telegram/draft-stream.js +84 -75
- package/dist/telegram/format.js +150 -6
- package/dist/telegram/send.js +415 -255
- package/dist/telegram/targets.js +21 -2
- package/dist/telegram/update-offset-store.js +19 -3
- package/dist/terminal/restore.js +5 -2
- package/dist/test-utils/fetch-mock.js +5 -0
- package/dist/version.js +18 -5
- package/dist/web/auto-reply/monitor/broadcast.js +7 -3
- package/dist/web/auto-reply/monitor/on-message.js +6 -3
- package/dist/web/inbound/media.js +34 -8
- package/dist/web/inbound/monitor.js +34 -17
- package/dist/web/inbound/send-api.js +18 -17
- package/dist/web/outbound.js +12 -5
- package/dist/wizard/clack-prompter.js +40 -7
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/skills/apple-reminders/SKILL.md +100 -49
- package/skills/coding-agent/SKILL.md +34 -28
- package/skills/github/SKILL.md +131 -16
- package/skills/imsg/SKILL.md +112 -15
- package/skills/openhue/SKILL.md +101 -19
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
package/dist/cron/run-log.js
CHANGED
|
@@ -68,6 +68,9 @@ export async function readCronRunLogEntries(filePath, opts) {
|
|
|
68
68
|
if (jobId && obj.jobId !== jobId) {
|
|
69
69
|
continue;
|
|
70
70
|
}
|
|
71
|
+
const usage = obj.usage && typeof obj.usage === "object"
|
|
72
|
+
? obj.usage
|
|
73
|
+
: undefined;
|
|
71
74
|
const entry = {
|
|
72
75
|
ts: obj.ts,
|
|
73
76
|
jobId: obj.jobId,
|
|
@@ -78,6 +81,17 @@ export async function readCronRunLogEntries(filePath, opts) {
|
|
|
78
81
|
runAtMs: obj.runAtMs,
|
|
79
82
|
durationMs: obj.durationMs,
|
|
80
83
|
nextRunAtMs: obj.nextRunAtMs,
|
|
84
|
+
model: typeof obj.model === "string" && obj.model.trim() ? obj.model : undefined,
|
|
85
|
+
provider: typeof obj.provider === "string" && obj.provider.trim() ? obj.provider : undefined,
|
|
86
|
+
usage: usage
|
|
87
|
+
? {
|
|
88
|
+
input_tokens: typeof usage.input_tokens === "number" ? usage.input_tokens : undefined,
|
|
89
|
+
output_tokens: typeof usage.output_tokens === "number" ? usage.output_tokens : undefined,
|
|
90
|
+
total_tokens: typeof usage.total_tokens === "number" ? usage.total_tokens : undefined,
|
|
91
|
+
cache_read_tokens: typeof usage.cache_read_tokens === "number" ? usage.cache_read_tokens : undefined,
|
|
92
|
+
cache_write_tokens: typeof usage.cache_write_tokens === "number" ? usage.cache_write_tokens : undefined,
|
|
93
|
+
}
|
|
94
|
+
: undefined,
|
|
81
95
|
};
|
|
82
96
|
if (typeof obj.sessionId === "string" && obj.sessionId.trim().length > 0) {
|
|
83
97
|
entry.sessionId = obj.sessionId;
|
|
@@ -1,8 +1,42 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { parseAbsoluteTimeMs } from "../parse.js";
|
|
3
3
|
import { computeNextRunAtMs } from "../schedule.js";
|
|
4
|
-
import {
|
|
4
|
+
import { normalizeCronStaggerMs, resolveCronStaggerMs, resolveDefaultCronStaggerMs, } from "../stagger.js";
|
|
5
|
+
import { normalizeHttpWebhookUrl } from "../webhook-url.js";
|
|
6
|
+
import { normalizeOptionalAgentId, normalizeOptionalSessionKey, normalizeOptionalText, normalizePayloadToSystemText, normalizeRequiredName, } from "./normalize.js";
|
|
5
7
|
const STUCK_RUN_MS = 2 * 60 * 60 * 1000;
|
|
8
|
+
function resolveStableCronOffsetMs(jobId, staggerMs) {
|
|
9
|
+
if (staggerMs <= 1) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
const digest = crypto.createHash("sha256").update(jobId).digest();
|
|
13
|
+
return digest.readUInt32BE(0) % staggerMs;
|
|
14
|
+
}
|
|
15
|
+
function computeStaggeredCronNextRunAtMs(job, nowMs) {
|
|
16
|
+
if (job.schedule.kind !== "cron") {
|
|
17
|
+
return computeNextRunAtMs(job.schedule, nowMs);
|
|
18
|
+
}
|
|
19
|
+
const staggerMs = resolveCronStaggerMs(job.schedule);
|
|
20
|
+
const offsetMs = resolveStableCronOffsetMs(job.id, staggerMs);
|
|
21
|
+
if (offsetMs <= 0) {
|
|
22
|
+
return computeNextRunAtMs(job.schedule, nowMs);
|
|
23
|
+
}
|
|
24
|
+
// Shift the schedule cursor backwards by the per-job offset so we can still
|
|
25
|
+
// target the current schedule window if its staggered slot has not passed yet.
|
|
26
|
+
let cursorMs = Math.max(0, nowMs - offsetMs);
|
|
27
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
28
|
+
const baseNext = computeNextRunAtMs(job.schedule, cursorMs);
|
|
29
|
+
if (baseNext === undefined) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const shifted = baseNext + offsetMs;
|
|
33
|
+
if (shifted > nowMs) {
|
|
34
|
+
return shifted;
|
|
35
|
+
}
|
|
36
|
+
cursorMs = Math.max(cursorMs + 1, baseNext + 1_000);
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
6
40
|
function resolveEveryAnchorMs(params) {
|
|
7
41
|
const raw = params.schedule.anchorMs;
|
|
8
42
|
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
@@ -19,8 +53,19 @@ export function assertSupportedJobSpec(job) {
|
|
|
19
53
|
}
|
|
20
54
|
}
|
|
21
55
|
function assertDeliverySupport(job) {
|
|
22
|
-
if (job.delivery
|
|
23
|
-
|
|
56
|
+
if (!job.delivery) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (job.delivery.mode === "webhook") {
|
|
60
|
+
const target = normalizeHttpWebhookUrl(job.delivery.to);
|
|
61
|
+
if (!target) {
|
|
62
|
+
throw new Error("cron webhook delivery requires delivery.to to be a valid http(s) URL");
|
|
63
|
+
}
|
|
64
|
+
job.delivery.to = target;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (job.sessionTarget !== "isolated") {
|
|
68
|
+
throw new Error('cron channel delivery config is only supported for sessionTarget="isolated"');
|
|
24
69
|
}
|
|
25
70
|
}
|
|
26
71
|
export function findJobOrThrow(state, id) {
|
|
@@ -59,50 +104,134 @@ export function computeJobNextRunAtMs(job, nowMs) {
|
|
|
59
104
|
: null;
|
|
60
105
|
return atMs !== null ? atMs : undefined;
|
|
61
106
|
}
|
|
62
|
-
|
|
107
|
+
const next = computeStaggeredCronNextRunAtMs(job, nowMs);
|
|
108
|
+
if (next === undefined && job.schedule.kind === "cron") {
|
|
109
|
+
const nextSecondMs = Math.floor(nowMs / 1000) * 1000 + 1000;
|
|
110
|
+
return computeStaggeredCronNextRunAtMs(job, nextSecondMs);
|
|
111
|
+
}
|
|
112
|
+
return next;
|
|
63
113
|
}
|
|
64
|
-
|
|
114
|
+
/** Maximum consecutive schedule errors before auto-disabling a job. */
|
|
115
|
+
const MAX_SCHEDULE_ERRORS = 3;
|
|
116
|
+
function recordScheduleComputeError(params) {
|
|
117
|
+
const { state, job, err } = params;
|
|
118
|
+
const errorCount = (job.state.scheduleErrorCount ?? 0) + 1;
|
|
119
|
+
const errText = String(err);
|
|
120
|
+
job.state.scheduleErrorCount = errorCount;
|
|
121
|
+
job.state.nextRunAtMs = undefined;
|
|
122
|
+
job.state.lastError = `schedule error: ${errText}`;
|
|
123
|
+
if (errorCount >= MAX_SCHEDULE_ERRORS) {
|
|
124
|
+
job.enabled = false;
|
|
125
|
+
state.deps.log.error({ jobId: job.id, name: job.name, errorCount, err: errText }, "cron: auto-disabled job after repeated schedule errors");
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
state.deps.log.warn({ jobId: job.id, name: job.name, errorCount, err: errText }, "cron: failed to compute next run for job (skipping)");
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
function normalizeJobTickState(params) {
|
|
133
|
+
const { state, job, nowMs } = params;
|
|
134
|
+
let changed = false;
|
|
135
|
+
if (!job.state) {
|
|
136
|
+
job.state = {};
|
|
137
|
+
changed = true;
|
|
138
|
+
}
|
|
139
|
+
if (!job.enabled) {
|
|
140
|
+
if (job.state.nextRunAtMs !== undefined) {
|
|
141
|
+
job.state.nextRunAtMs = undefined;
|
|
142
|
+
changed = true;
|
|
143
|
+
}
|
|
144
|
+
if (job.state.runningAtMs !== undefined) {
|
|
145
|
+
job.state.runningAtMs = undefined;
|
|
146
|
+
changed = true;
|
|
147
|
+
}
|
|
148
|
+
return { changed, skip: true };
|
|
149
|
+
}
|
|
150
|
+
const runningAt = job.state.runningAtMs;
|
|
151
|
+
if (typeof runningAt === "number" && nowMs - runningAt > STUCK_RUN_MS) {
|
|
152
|
+
state.deps.log.warn({ jobId: job.id, runningAtMs: runningAt }, "cron: clearing stuck running marker");
|
|
153
|
+
job.state.runningAtMs = undefined;
|
|
154
|
+
changed = true;
|
|
155
|
+
}
|
|
156
|
+
return { changed, skip: false };
|
|
157
|
+
}
|
|
158
|
+
function walkSchedulableJobs(state, fn) {
|
|
65
159
|
if (!state.store) {
|
|
66
160
|
return false;
|
|
67
161
|
}
|
|
68
162
|
let changed = false;
|
|
69
163
|
const now = state.deps.nowMs();
|
|
70
164
|
for (const job of state.store.jobs) {
|
|
71
|
-
|
|
72
|
-
|
|
165
|
+
const tick = normalizeJobTickState({ state, job, nowMs: now });
|
|
166
|
+
if (tick.changed) {
|
|
73
167
|
changed = true;
|
|
74
168
|
}
|
|
75
|
-
if (
|
|
76
|
-
if (job.state.nextRunAtMs !== undefined) {
|
|
77
|
-
job.state.nextRunAtMs = undefined;
|
|
78
|
-
changed = true;
|
|
79
|
-
}
|
|
80
|
-
if (job.state.runningAtMs !== undefined) {
|
|
81
|
-
job.state.runningAtMs = undefined;
|
|
82
|
-
changed = true;
|
|
83
|
-
}
|
|
169
|
+
if (tick.skip) {
|
|
84
170
|
continue;
|
|
85
171
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
172
|
+
if (fn({ job, nowMs: now })) {
|
|
173
|
+
changed = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return changed;
|
|
177
|
+
}
|
|
178
|
+
function recomputeJobNextRunAtMs(params) {
|
|
179
|
+
let changed = false;
|
|
180
|
+
try {
|
|
181
|
+
const newNext = computeJobNextRunAtMs(params.job, params.nowMs);
|
|
182
|
+
if (params.job.state.nextRunAtMs !== newNext) {
|
|
183
|
+
params.job.state.nextRunAtMs = newNext;
|
|
90
184
|
changed = true;
|
|
91
185
|
}
|
|
186
|
+
// Clear schedule error count on successful computation.
|
|
187
|
+
if (params.job.state.scheduleErrorCount) {
|
|
188
|
+
params.job.state.scheduleErrorCount = undefined;
|
|
189
|
+
changed = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
if (recordScheduleComputeError({ state: params.state, job: params.job, err })) {
|
|
194
|
+
changed = true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return changed;
|
|
198
|
+
}
|
|
199
|
+
export function recomputeNextRuns(state) {
|
|
200
|
+
return walkSchedulableJobs(state, ({ job, nowMs: now }) => {
|
|
201
|
+
let changed = false;
|
|
92
202
|
// Only recompute if nextRunAtMs is missing or already past-due.
|
|
93
203
|
// Preserving a still-future nextRunAtMs avoids accidentally advancing
|
|
94
204
|
// a job that hasn't fired yet (e.g. during restart recovery).
|
|
95
205
|
const nextRun = job.state.nextRunAtMs;
|
|
96
206
|
const isDueOrMissing = nextRun === undefined || now >= nextRun;
|
|
97
207
|
if (isDueOrMissing) {
|
|
98
|
-
|
|
99
|
-
if (job.state.nextRunAtMs !== newNext) {
|
|
100
|
-
job.state.nextRunAtMs = newNext;
|
|
208
|
+
if (recomputeJobNextRunAtMs({ state, job, nowMs: now })) {
|
|
101
209
|
changed = true;
|
|
102
210
|
}
|
|
103
211
|
}
|
|
104
|
-
|
|
105
|
-
|
|
212
|
+
return changed;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Maintenance-only version of recomputeNextRuns that handles disabled jobs
|
|
217
|
+
* and stuck markers, but does NOT recompute nextRunAtMs for enabled jobs
|
|
218
|
+
* with existing values. Used during timer ticks when no due jobs were found
|
|
219
|
+
* to prevent silently advancing past-due nextRunAtMs values without execution
|
|
220
|
+
* (see #13992).
|
|
221
|
+
*/
|
|
222
|
+
export function recomputeNextRunsForMaintenance(state) {
|
|
223
|
+
return walkSchedulableJobs(state, ({ job, nowMs: now }) => {
|
|
224
|
+
let changed = false;
|
|
225
|
+
// Only compute missing nextRunAtMs, do NOT recompute existing ones.
|
|
226
|
+
// If a job was past-due but not found by findDueJobs, recomputing would
|
|
227
|
+
// cause it to be silently skipped.
|
|
228
|
+
if (job.state.nextRunAtMs === undefined) {
|
|
229
|
+
if (recomputeJobNextRunAtMs({ state, job, nowMs: now })) {
|
|
230
|
+
changed = true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return changed;
|
|
234
|
+
});
|
|
106
235
|
}
|
|
107
236
|
export function nextWakeAtMs(state) {
|
|
108
237
|
const jobs = state.store?.jobs ?? [];
|
|
@@ -123,7 +252,18 @@ export function createJob(state, input) {
|
|
|
123
252
|
fallbackAnchorMs: now,
|
|
124
253
|
}),
|
|
125
254
|
}
|
|
126
|
-
: input.schedule
|
|
255
|
+
: input.schedule.kind === "cron"
|
|
256
|
+
? (() => {
|
|
257
|
+
const explicitStaggerMs = normalizeCronStaggerMs(input.schedule.staggerMs);
|
|
258
|
+
if (explicitStaggerMs !== undefined) {
|
|
259
|
+
return { ...input.schedule, staggerMs: explicitStaggerMs };
|
|
260
|
+
}
|
|
261
|
+
const defaultStaggerMs = resolveDefaultCronStaggerMs(input.schedule.expr);
|
|
262
|
+
return defaultStaggerMs !== undefined
|
|
263
|
+
? { ...input.schedule, staggerMs: defaultStaggerMs }
|
|
264
|
+
: input.schedule;
|
|
265
|
+
})()
|
|
266
|
+
: input.schedule;
|
|
127
267
|
const deleteAfterRun = typeof input.deleteAfterRun === "boolean"
|
|
128
268
|
? input.deleteAfterRun
|
|
129
269
|
: schedule.kind === "at"
|
|
@@ -133,6 +273,7 @@ export function createJob(state, input) {
|
|
|
133
273
|
const job = {
|
|
134
274
|
id,
|
|
135
275
|
agentId: normalizeOptionalAgentId(input.agentId),
|
|
276
|
+
sessionKey: normalizeOptionalSessionKey(input.sessionKey),
|
|
136
277
|
name: normalizeRequiredName(input.name),
|
|
137
278
|
description: normalizeOptionalText(input.description),
|
|
138
279
|
enabled,
|
|
@@ -167,7 +308,25 @@ export function applyJobPatch(job, patch) {
|
|
|
167
308
|
job.deleteAfterRun = patch.deleteAfterRun;
|
|
168
309
|
}
|
|
169
310
|
if (patch.schedule) {
|
|
170
|
-
|
|
311
|
+
if (patch.schedule.kind === "cron") {
|
|
312
|
+
const explicitStaggerMs = normalizeCronStaggerMs(patch.schedule.staggerMs);
|
|
313
|
+
if (explicitStaggerMs !== undefined) {
|
|
314
|
+
job.schedule = { ...patch.schedule, staggerMs: explicitStaggerMs };
|
|
315
|
+
}
|
|
316
|
+
else if (job.schedule.kind === "cron") {
|
|
317
|
+
job.schedule = { ...patch.schedule, staggerMs: job.schedule.staggerMs };
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
const defaultStaggerMs = resolveDefaultCronStaggerMs(patch.schedule.expr);
|
|
321
|
+
job.schedule =
|
|
322
|
+
defaultStaggerMs !== undefined
|
|
323
|
+
? { ...patch.schedule, staggerMs: defaultStaggerMs }
|
|
324
|
+
: patch.schedule;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
job.schedule = patch.schedule;
|
|
329
|
+
}
|
|
171
330
|
}
|
|
172
331
|
if (patch.sessionTarget) {
|
|
173
332
|
job.sessionTarget = patch.sessionTarget;
|
|
@@ -190,7 +349,7 @@ export function applyJobPatch(job, patch) {
|
|
|
190
349
|
if (patch.delivery) {
|
|
191
350
|
job.delivery = mergeCronDelivery(job.delivery, patch.delivery);
|
|
192
351
|
}
|
|
193
|
-
if (job.sessionTarget === "main" && job.delivery) {
|
|
352
|
+
if (job.sessionTarget === "main" && job.delivery?.mode !== "webhook") {
|
|
194
353
|
job.delivery = undefined;
|
|
195
354
|
}
|
|
196
355
|
if (patch.state) {
|
|
@@ -199,6 +358,9 @@ export function applyJobPatch(job, patch) {
|
|
|
199
358
|
if ("agentId" in patch) {
|
|
200
359
|
job.agentId = normalizeOptionalAgentId(patch.agentId);
|
|
201
360
|
}
|
|
361
|
+
if ("sessionKey" in patch) {
|
|
362
|
+
job.sessionKey = normalizeOptionalSessionKey(patch.sessionKey);
|
|
363
|
+
}
|
|
202
364
|
assertSupportedJobSpec(job);
|
|
203
365
|
assertDeliverySupport(job);
|
|
204
366
|
}
|
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
import { normalizeAgentId } from "../../routing/session-key.js";
|
|
2
2
|
import { truncateUtf16Safe } from "../../utils.js";
|
|
3
3
|
export function normalizeRequiredName(raw) {
|
|
4
|
-
if (typeof raw !== "string")
|
|
4
|
+
if (typeof raw !== "string") {
|
|
5
5
|
throw new Error("cron job name is required");
|
|
6
|
+
}
|
|
6
7
|
const name = raw.trim();
|
|
7
|
-
if (!name)
|
|
8
|
+
if (!name) {
|
|
8
9
|
throw new Error("cron job name is required");
|
|
10
|
+
}
|
|
9
11
|
return name;
|
|
10
12
|
}
|
|
11
13
|
export function normalizeOptionalText(raw) {
|
|
12
|
-
if (typeof raw !== "string")
|
|
14
|
+
if (typeof raw !== "string") {
|
|
13
15
|
return undefined;
|
|
16
|
+
}
|
|
14
17
|
const trimmed = raw.trim();
|
|
15
18
|
return trimmed ? trimmed : undefined;
|
|
16
19
|
}
|
|
17
20
|
function truncateText(input, maxLen) {
|
|
18
|
-
if (input.length <= maxLen)
|
|
21
|
+
if (input.length <= maxLen) {
|
|
19
22
|
return input;
|
|
23
|
+
}
|
|
20
24
|
return `${truncateUtf16Safe(input, Math.max(0, maxLen - 1)).trimEnd()}…`;
|
|
21
25
|
}
|
|
22
26
|
export function normalizeOptionalAgentId(raw) {
|
|
23
|
-
if (typeof raw !== "string")
|
|
27
|
+
if (typeof raw !== "string") {
|
|
24
28
|
return undefined;
|
|
29
|
+
}
|
|
25
30
|
const trimmed = raw.trim();
|
|
26
|
-
if (!trimmed)
|
|
31
|
+
if (!trimmed) {
|
|
27
32
|
return undefined;
|
|
33
|
+
}
|
|
28
34
|
return normalizeAgentId(trimmed);
|
|
29
35
|
}
|
|
36
|
+
export function normalizeOptionalSessionKey(raw) {
|
|
37
|
+
if (typeof raw !== "string") {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const trimmed = raw.trim();
|
|
41
|
+
return trimmed || undefined;
|
|
42
|
+
}
|
|
30
43
|
export function inferLegacyName(job) {
|
|
31
44
|
const text = job?.payload?.kind === "systemEvent" && typeof job.payload.text === "string"
|
|
32
45
|
? job.payload.text
|
|
@@ -37,19 +50,24 @@ export function inferLegacyName(job) {
|
|
|
37
50
|
.split("\n")
|
|
38
51
|
.map((l) => l.trim())
|
|
39
52
|
.find(Boolean) ?? "";
|
|
40
|
-
if (firstLine)
|
|
53
|
+
if (firstLine) {
|
|
41
54
|
return truncateText(firstLine, 60);
|
|
55
|
+
}
|
|
42
56
|
const kind = typeof job?.schedule?.kind === "string" ? job.schedule.kind : "";
|
|
43
|
-
if (kind === "cron" && typeof job?.schedule?.expr === "string")
|
|
57
|
+
if (kind === "cron" && typeof job?.schedule?.expr === "string") {
|
|
44
58
|
return `Cron: ${truncateText(job.schedule.expr, 52)}`;
|
|
45
|
-
|
|
59
|
+
}
|
|
60
|
+
if (kind === "every" && typeof job?.schedule?.everyMs === "number") {
|
|
46
61
|
return `Every: ${job.schedule.everyMs}ms`;
|
|
47
|
-
|
|
62
|
+
}
|
|
63
|
+
if (kind === "at") {
|
|
48
64
|
return "One-shot";
|
|
65
|
+
}
|
|
49
66
|
return "Cron job";
|
|
50
67
|
}
|
|
51
68
|
export function normalizePayloadToSystemText(payload) {
|
|
52
|
-
if (payload.kind === "systemEvent")
|
|
69
|
+
if (payload.kind === "systemEvent") {
|
|
53
70
|
return payload.text.trim();
|
|
71
|
+
}
|
|
54
72
|
return payload.message.trim();
|
|
55
73
|
}
|
|
@@ -1,38 +1,11 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { buildDeliveryFromLegacyPayload, hasLegacyDeliveryHints, stripLegacyDeliveryFields, } from "../legacy-delivery.js";
|
|
2
3
|
import { parseAbsoluteTimeMs } from "../parse.js";
|
|
3
4
|
import { migrateLegacyCronPayload } from "../payload-migration.js";
|
|
5
|
+
import { normalizeCronStaggerMs, resolveDefaultCronStaggerMs } from "../stagger.js";
|
|
4
6
|
import { loadCronStore, saveCronStore } from "../store.js";
|
|
5
7
|
import { recomputeNextRuns } from "./jobs.js";
|
|
6
8
|
import { inferLegacyName, normalizeOptionalText } from "./normalize.js";
|
|
7
|
-
function hasLegacyDeliveryHints(payload) {
|
|
8
|
-
if (typeof payload.deliver === "boolean") {
|
|
9
|
-
return true;
|
|
10
|
-
}
|
|
11
|
-
if (typeof payload.bestEffortDeliver === "boolean") {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
if (typeof payload.to === "string" && payload.to.trim()) {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
function buildDeliveryFromLegacyPayload(payload) {
|
|
20
|
-
const deliver = payload.deliver;
|
|
21
|
-
const mode = deliver === false ? "none" : "announce";
|
|
22
|
-
const channelRaw = typeof payload.channel === "string" ? payload.channel.trim().toLowerCase() : "";
|
|
23
|
-
const toRaw = typeof payload.to === "string" ? payload.to.trim() : "";
|
|
24
|
-
const next = { mode };
|
|
25
|
-
if (channelRaw) {
|
|
26
|
-
next.channel = channelRaw;
|
|
27
|
-
}
|
|
28
|
-
if (toRaw) {
|
|
29
|
-
next.to = toRaw;
|
|
30
|
-
}
|
|
31
|
-
if (typeof payload.bestEffortDeliver === "boolean") {
|
|
32
|
-
next.bestEffort = payload.bestEffortDeliver;
|
|
33
|
-
}
|
|
34
|
-
return next;
|
|
35
|
-
}
|
|
36
9
|
function buildDeliveryPatchFromLegacyPayload(payload) {
|
|
37
10
|
const deliver = payload.deliver;
|
|
38
11
|
const channelRaw = typeof payload.channel === "string" ? payload.channel.trim().toLowerCase() : "";
|
|
@@ -86,20 +59,6 @@ function mergeLegacyDeliveryInto(delivery, payload) {
|
|
|
86
59
|
}
|
|
87
60
|
return { delivery: next, mutated };
|
|
88
61
|
}
|
|
89
|
-
function stripLegacyDeliveryFields(payload) {
|
|
90
|
-
if ("deliver" in payload) {
|
|
91
|
-
delete payload.deliver;
|
|
92
|
-
}
|
|
93
|
-
if ("channel" in payload) {
|
|
94
|
-
delete payload.channel;
|
|
95
|
-
}
|
|
96
|
-
if ("to" in payload) {
|
|
97
|
-
delete payload.to;
|
|
98
|
-
}
|
|
99
|
-
if ("bestEffortDeliver" in payload) {
|
|
100
|
-
delete payload.bestEffortDeliver;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
62
|
function normalizePayloadKind(payload) {
|
|
104
63
|
const raw = typeof payload.kind === "string" ? payload.kind.trim().toLowerCase() : "";
|
|
105
64
|
if (raw === "agentturn") {
|
|
@@ -143,7 +102,7 @@ function copyTopLevelAgentTurnFields(raw, payload) {
|
|
|
143
102
|
if (typeof payload.timeoutSeconds !== "number" &&
|
|
144
103
|
typeof raw.timeoutSeconds === "number" &&
|
|
145
104
|
Number.isFinite(raw.timeoutSeconds)) {
|
|
146
|
-
payload.timeoutSeconds = Math.max(
|
|
105
|
+
payload.timeoutSeconds = Math.max(0, Math.floor(raw.timeoutSeconds));
|
|
147
106
|
mutated = true;
|
|
148
107
|
}
|
|
149
108
|
if (typeof payload.allowUnsafeExternalContent !== "boolean" &&
|
|
@@ -256,6 +215,13 @@ export async function ensureLoaded(state, opts) {
|
|
|
256
215
|
raw.description = desc;
|
|
257
216
|
mutated = true;
|
|
258
217
|
}
|
|
218
|
+
if ("sessionKey" in raw) {
|
|
219
|
+
const sessionKey = typeof raw.sessionKey === "string" ? normalizeOptionalText(raw.sessionKey) : undefined;
|
|
220
|
+
if (raw.sessionKey !== sessionKey) {
|
|
221
|
+
raw.sessionKey = sessionKey;
|
|
222
|
+
mutated = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
259
225
|
if (typeof raw.enabled !== "boolean") {
|
|
260
226
|
raw.enabled = true;
|
|
261
227
|
mutated = true;
|
|
@@ -350,6 +316,26 @@ export async function ensureLoaded(state, opts) {
|
|
|
350
316
|
mutated = true;
|
|
351
317
|
}
|
|
352
318
|
}
|
|
319
|
+
const exprRaw = typeof sched.expr === "string" ? sched.expr.trim() : "";
|
|
320
|
+
if (typeof sched.expr === "string" && sched.expr !== exprRaw) {
|
|
321
|
+
sched.expr = exprRaw;
|
|
322
|
+
mutated = true;
|
|
323
|
+
}
|
|
324
|
+
if ((kind === "cron" || sched.kind === "cron") && exprRaw) {
|
|
325
|
+
const explicitStaggerMs = normalizeCronStaggerMs(sched.staggerMs);
|
|
326
|
+
const defaultStaggerMs = resolveDefaultCronStaggerMs(exprRaw);
|
|
327
|
+
const targetStaggerMs = explicitStaggerMs ?? defaultStaggerMs;
|
|
328
|
+
if (targetStaggerMs === undefined) {
|
|
329
|
+
if ("staggerMs" in sched) {
|
|
330
|
+
delete sched.staggerMs;
|
|
331
|
+
mutated = true;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else if (sched.staggerMs !== targetStaggerMs) {
|
|
335
|
+
sched.staggerMs = targetStaggerMs;
|
|
336
|
+
mutated = true;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
353
339
|
}
|
|
354
340
|
const delivery = raw.delivery;
|
|
355
341
|
if (delivery && typeof delivery === "object" && !Array.isArray(delivery)) {
|