@poolzin/pool-bot 2026.2.24 → 2026.2.26
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 +21 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/event-mapper.js +87 -22
- package/dist/acp/meta.js +12 -6
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-paths.js +8 -9
- package/dist/agents/agent-scope.js +17 -5
- package/dist/agents/auth-profiles/oauth.js +148 -64
- package/dist/agents/auth-profiles/session-override.js +13 -7
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-runtime.js +2 -25
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/bedrock-discovery.js +3 -1
- package/dist/agents/byteplus-models.js +97 -0
- package/dist/agents/chutes-oauth.js +1 -0
- package/dist/agents/cli-runner/helpers.js +4 -0
- package/dist/agents/compaction.js +41 -14
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/doubao-models.js +121 -0
- package/dist/agents/failover-error.js +2 -0
- package/dist/agents/huggingface-models.js +5 -3
- package/dist/agents/live-model-filter.js +5 -0
- package/dist/agents/minimax-vlm.js +10 -8
- package/dist/agents/model-auth.js +6 -0
- package/dist/agents/model-catalog.js +3 -1
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/model-selection.js +7 -1
- package/dist/agents/models-config.providers.js +364 -165
- package/dist/agents/ollama-stream.js +117 -4
- package/dist/agents/opencode-zen-models.js +22 -11
- package/dist/agents/pi-embedded-helpers/errors.js +55 -33
- package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
- package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/compact.js +29 -7
- package/dist/agents/pi-embedded-runner/extensions.js +28 -26
- package/dist/agents/pi-embedded-runner/google.js +20 -8
- package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
- package/dist/agents/pi-embedded-runner/run.js +71 -12
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
- package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
- package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
- package/dist/agents/pi-embedded-runner/thinking.js +42 -0
- package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
- package/dist/agents/pi-embedded-runner/utils.js +7 -10
- package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
- package/dist/agents/pi-embedded-subscribe.js +9 -4
- package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
- package/dist/agents/pi-embedded-utils.js +3 -0
- package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
- package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
- package/dist/agents/pi-settings.js +40 -0
- package/dist/agents/pi-tools.policy.js +2 -1
- package/dist/agents/provider/config-loader.js +1 -1
- package/dist/agents/sandbox/browser.js +170 -33
- package/dist/agents/sandbox/config-hash.js +14 -27
- package/dist/agents/sandbox/config.js +21 -2
- package/dist/agents/sandbox/constants.js +2 -0
- package/dist/agents/sandbox/docker.js +16 -2
- package/dist/agents/sandbox/novnc-auth.js +62 -0
- package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
- package/dist/agents/sandbox/shared.js +10 -6
- package/dist/agents/sandbox-paths.js +24 -11
- package/dist/agents/schema/clean-for-gemini.js +132 -85
- package/dist/agents/session-slug.js +10 -5
- package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
- package/dist/agents/session-tool-result-guard.js +3 -1
- package/dist/agents/session-transcript-repair.js +40 -6
- package/dist/agents/skills/bundled-dir.js +19 -5
- package/dist/agents/skills/env-overrides.js +124 -43
- package/dist/agents/skills/frontmatter.js +6 -6
- package/dist/agents/skills/plugin-skills.js +14 -7
- package/dist/agents/skills/workspace.js +1 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-announce.js +251 -49
- package/dist/agents/subagent-lifecycle-events.js +19 -0
- package/dist/agents/subagent-registry-cleanup.js +31 -0
- package/dist/agents/subagent-registry-completion.js +68 -0
- package/dist/agents/subagent-registry-queries.js +117 -0
- package/dist/agents/subagent-registry-state.js +46 -0
- package/dist/agents/subagent-registry.js +252 -221
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/subagent-registry.store.js +1 -0
- package/dist/agents/subagent-registry.types.js +1 -0
- package/dist/agents/subagent-spawn.js +195 -7
- package/dist/agents/system-prompt.js +22 -6
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
- package/dist/agents/test-helpers/fast-core-tools.js +1 -17
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/timeout.js +18 -6
- package/dist/agents/tool-call-id.js +1 -1
- package/dist/agents/tool-display-common.js +162 -29
- package/dist/agents/tool-images.js +82 -9
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tool-policy.js +51 -26
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/canvas-tool.js +27 -1
- package/dist/agents/tools/common.js +45 -0
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-guild.js +4 -1
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/gateway-tool.js +3 -1
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/nodes-utils.js +1 -10
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/tools/sessions-send-helpers.js +12 -6
- package/dist/agents/tools/sessions-spawn-tool.js +8 -2
- package/dist/agents/tools/subagents-tool.js +2 -1
- package/dist/agents/tools/whatsapp-actions.js +10 -2
- package/dist/agents/tools/whatsapp-target-auth.js +18 -0
- package/dist/agents/transcript-policy.js +22 -8
- package/dist/agents/venice-models.js +11 -3
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.data.js +51 -0
- package/dist/auto-reply/commands-registry.js +19 -21
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/group-activation.js +10 -5
- package/dist/auto-reply/inbound-debounce.js +10 -5
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/abort.js +1 -1
- package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/bash-command.js +41 -39
- package/dist/auto-reply/reply/command-gates.js +25 -0
- package/dist/auto-reply/reply/commands-allowlist.js +111 -72
- package/dist/auto-reply/reply/commands-bash.js +6 -5
- package/dist/auto-reply/reply/commands-config.js +30 -28
- package/dist/auto-reply/reply/commands-core.js +2 -1
- package/dist/auto-reply/reply/commands-info.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +65 -14
- package/dist/auto-reply/reply/commands-session.js +237 -82
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/reply/commands-setunset.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
- package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
- package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
- package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
- package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
- package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
- package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
- package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
- package/dist/auto-reply/reply/commands-subagents.js +51 -587
- package/dist/auto-reply/reply/commands-tts.js +10 -5
- package/dist/auto-reply/reply/config-value.js +10 -5
- package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
- package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
- package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
- package/dist/auto-reply/reply/followup-runner.js +1 -0
- package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
- package/dist/auto-reply/reply/get-reply-directives.js +17 -28
- package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
- package/dist/auto-reply/reply/get-reply.js +71 -12
- package/dist/auto-reply/reply/model-selection.js +80 -39
- package/dist/auto-reply/reply/queue/enqueue.js +10 -5
- package/dist/auto-reply/reply/queue/state.js +13 -12
- package/dist/auto-reply/reply/reply-payloads.js +67 -36
- package/dist/auto-reply/reply/reply-reference.js +9 -8
- package/dist/auto-reply/reply/route-reply.js +15 -8
- package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
- package/dist/auto-reply/reply/session.js +22 -6
- package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
- package/dist/auto-reply/reply/subagents-utils.js +56 -30
- package/dist/auto-reply/reply/typing.js +46 -21
- package/dist/auto-reply/send-policy.js +14 -7
- package/dist/auto-reply/status.js +140 -16
- package/dist/auto-reply/templating.js +10 -5
- package/dist/auto-reply/thinking.js +7 -16
- package/dist/auto-reply/tokens.js +21 -5
- package/dist/browser/bridge-server.js +36 -20
- package/dist/browser/cdp.helpers.js +7 -14
- package/dist/browser/cdp.js +35 -15
- package/dist/browser/chrome.profile-decoration.js +7 -4
- package/dist/browser/config.js +30 -0
- package/dist/browser/extension-relay-auth.js +55 -0
- package/dist/browser/extension-relay.js +74 -29
- package/dist/browser/navigation-guard.js +39 -0
- package/dist/browser/paths.js +77 -0
- package/dist/browser/profiles.js +13 -8
- package/dist/browser/pw-ai-module.js +10 -5
- package/dist/browser/pw-session.js +76 -39
- package/dist/browser/pw-tools-core.interactions.js +14 -7
- package/dist/browser/pw-tools-core.state.js +12 -6
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-context.js +7 -0
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +26 -0
- package/dist/channels/allowlists/resolve-utils.js +43 -19
- package/dist/channels/channel-config.js +14 -7
- package/dist/channels/draft-stream-loop.js +7 -0
- package/dist/channels/model-overrides.js +82 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/plugins/normalize/imessage.js +14 -7
- package/dist/channels/plugins/normalize/slack.js +10 -5
- package/dist/channels/plugins/normalize/telegram.js +14 -7
- package/dist/channels/plugins/outbound/discord.js +80 -8
- package/dist/channels/plugins/outbound/signal.js +11 -11
- package/dist/channels/plugins/setup-helpers.js +10 -5
- package/dist/channels/sender-label.js +14 -7
- package/dist/channels/session.js +4 -2
- package/dist/channels/status-reactions.js +297 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +3 -2
- package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
- package/dist/cli/cli-name.js +11 -11
- package/dist/cli/cli-utils.js +13 -3
- package/dist/cli/command-format.js +1 -1
- package/dist/cli/config-cli.js +1 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
- package/dist/cli/daemon-cli/lifecycle.js +64 -2
- package/dist/cli/daemon-cli/restart-health.js +126 -0
- package/dist/cli/daemon-cli/status.gather.js +9 -13
- package/dist/cli/daemon-cli/status.print.js +2 -10
- package/dist/cli/deps.js +27 -22
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/gateway-cli/run-loop.js +23 -5
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/node-cli/register.js +14 -5
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +26 -0
- package/dist/cli/outbound-send-deps.js +2 -9
- package/dist/cli/outbound-send-mapping.js +11 -0
- package/dist/cli/pairing-cli.js +40 -14
- package/dist/cli/plugins-cli.js +250 -73
- package/dist/cli/ports.js +11 -10
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +214 -136
- package/dist/cli/program/command-tree.js +16 -0
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/program/preaction.js +13 -9
- package/dist/cli/program/register.configure.js +3 -18
- package/dist/cli/program/register.maintenance.js +2 -2
- package/dist/cli/program/register.onboard.js +2 -0
- package/dist/cli/program/register.status-health-sessions.js +16 -17
- package/dist/cli/program/register.subclis.js +93 -52
- package/dist/cli/route.js +12 -8
- package/dist/cli/system-cli.js +36 -46
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/cli/update-cli/shared.js +22 -9
- package/dist/cli/update-cli/update-command.js +89 -14
- package/dist/cli/update-cli/wizard.js +6 -12
- package/dist/commands/agent/run-context.js +18 -5
- package/dist/commands/agent/session-store.js +17 -4
- package/dist/commands/agent.js +185 -89
- package/dist/commands/agents.bindings.js +14 -7
- package/dist/commands/agents.commands.add.js +13 -9
- package/dist/commands/agents.commands.identity.js +12 -6
- package/dist/commands/agents.commands.list.js +11 -6
- package/dist/commands/agents.config.js +8 -10
- package/dist/commands/agents.providers.js +12 -6
- package/dist/commands/auth-choice-options.js +103 -75
- package/dist/commands/auth-choice.apply.byteplus.js +55 -0
- package/dist/commands/auth-choice.apply.js +4 -0
- package/dist/commands/auth-choice.apply.minimax.js +61 -13
- package/dist/commands/auth-choice.apply.openai.js +3 -1
- package/dist/commands/auth-choice.apply.volcengine.js +55 -0
- package/dist/commands/auth-choice.preferred-provider.js +2 -0
- package/dist/commands/channels/remove.js +13 -6
- package/dist/commands/channels/shared.js +4 -14
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/configure.commands.js +14 -0
- package/dist/commands/configure.gateway.js +2 -4
- package/dist/commands/configure.js +1 -1
- package/dist/commands/configure.shared.js +11 -0
- package/dist/commands/daemon-install-helpers.js +2 -2
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/dashboard.js +12 -10
- package/dist/commands/docs.js +14 -8
- package/dist/commands/doctor-config-flow.js +11 -9
- package/dist/commands/doctor-legacy-config.js +281 -0
- package/dist/commands/doctor-state-integrity.js +99 -23
- package/dist/commands/doctor-update.js +12 -9
- package/dist/commands/models/list.list-command.js +7 -5
- package/dist/commands/models/set-image.js +2 -21
- package/dist/commands/node-daemon-install-helpers.js +10 -8
- package/dist/commands/onboard-auth.config-minimax.js +54 -80
- package/dist/commands/onboard-auth.config-opencode.js +2 -18
- package/dist/commands/onboard-auth.credentials.js +90 -13
- package/dist/commands/onboard-auth.js +1 -1
- package/dist/commands/onboard-auth.models.js +6 -5
- package/dist/commands/onboard-hooks.js +1 -1
- package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
- package/dist/commands/onboard-provider-auth-flags.js +14 -0
- package/dist/commands/onboard-remote.js +14 -7
- package/dist/commands/onboard.js +11 -13
- package/dist/commands/sandbox-display.js +6 -5
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/commands/status-all/diagnosis.js +14 -10
- package/dist/commands/status-all/format.js +1 -0
- package/dist/commands/status.gateway-probe.js +1 -16
- package/dist/commands/systemd-linger.js +12 -6
- package/dist/config/agent-limits.js +2 -0
- package/dist/config/commands.js +32 -15
- package/dist/config/config-paths.js +9 -11
- package/dist/config/config.js +1 -1
- package/dist/config/defaults.js +22 -2
- package/dist/config/discord-preview-streaming.js +104 -0
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +45 -7
- package/dist/config/includes.js +4 -0
- package/dist/config/io.js +656 -171
- package/dist/config/legacy.migrations.part-1.js +189 -78
- package/dist/config/legacy.shared.js +3 -1
- package/dist/config/merge-patch.js +54 -4
- package/dist/config/prototype-keys.js +4 -0
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.help.js +44 -7
- package/dist/config/schema.js +58 -570
- package/dist/config/schema.labels.js +38 -6
- package/dist/config/sessions/delivery-info.js +10 -3
- package/dist/config/sessions/main-session.js +10 -5
- package/dist/config/sessions/session-file.js +33 -0
- package/dist/config/sessions/session-key.js +10 -5
- package/dist/config/sessions/store.js +1 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.agent-runtime.js +11 -0
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +156 -20
- package/dist/config/zod-schema.providers-core.js +78 -4
- package/dist/config/zod-schema.providers.js +6 -1
- package/dist/config/zod-schema.session.js +41 -2
- package/dist/cron/run-log.js +3 -0
- package/dist/cron/schedule.js +21 -10
- package/dist/cron/service/ops.js +35 -21
- package/dist/cron/service/timer.js +116 -16
- package/dist/cron/stagger.js +3 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/api.js +12 -6
- package/dist/discord/draft-chunking.js +22 -0
- package/dist/discord/draft-stream.js +124 -0
- package/dist/discord/monitor/agent-components.js +1 -1
- package/dist/discord/monitor/commands.js +5 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/discord/monitor/gateway-plugin.js +2 -1
- package/dist/discord/monitor/listeners.js +37 -27
- package/dist/discord/monitor/message-handler.js +4 -1
- package/dist/discord/monitor/message-handler.preflight.js +65 -8
- package/dist/discord/monitor/message-handler.process.js +246 -217
- package/dist/discord/monitor/message-utils.js +143 -6
- package/dist/discord/monitor/model-picker-preferences.js +143 -0
- package/dist/discord/monitor/model-picker.js +651 -0
- package/dist/discord/monitor/native-command.js +573 -16
- package/dist/discord/monitor/provider.allowlist.js +223 -0
- package/dist/discord/monitor/provider.js +275 -347
- package/dist/discord/monitor/provider.lifecycle.js +100 -0
- package/dist/discord/monitor/reply-delivery.js +123 -16
- package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
- package/dist/discord/monitor/thread-bindings.js +4 -0
- package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
- package/dist/discord/monitor/thread-bindings.manager.js +423 -0
- package/dist/discord/monitor/thread-bindings.messages.js +55 -0
- package/dist/discord/monitor/thread-bindings.state.js +358 -0
- package/dist/discord/monitor/thread-bindings.types.js +6 -0
- package/dist/discord/resolve-users.js +33 -21
- package/dist/discord/send.channels.js +15 -0
- package/dist/discord/send.js +3 -2
- package/dist/discord/send.outbound.js +82 -26
- package/dist/discord/send.permissions.js +83 -30
- package/dist/discord/send.reactions.js +8 -4
- package/dist/discord/token.js +10 -5
- package/dist/discord/voice/command.js +263 -0
- package/dist/discord/voice/manager.js +531 -0
- package/dist/gateway/auth.js +72 -13
- package/dist/gateway/call.js +152 -83
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/client.js +28 -4
- package/dist/gateway/config-reload.js +3 -4
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/control-ui.js +219 -96
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks-mapping.js +88 -38
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-auth-helpers.js +3 -2
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/http-endpoint-helpers.js +1 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +74 -9
- package/dist/gateway/node-invoke-system-run-approval.js +14 -35
- package/dist/gateway/node-registry.js +10 -5
- package/dist/gateway/openai-http.js +1 -0
- package/dist/gateway/openresponses-http.js +121 -110
- package/dist/gateway/origin-check.js +1 -18
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +4 -2
- package/dist/gateway/protocol/schema/cron.js +1 -0
- package/dist/gateway/protocol/schema/devices.js +1 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema/sessions.js +6 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/role-policy.js +17 -0
- package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
- package/dist/gateway/server/ws-connection/message-handler.js +175 -148
- package/dist/gateway/server-chat.js +83 -25
- package/dist/gateway/server-constants.js +10 -9
- package/dist/gateway/server-cron.js +1 -0
- package/dist/gateway/server-http.js +247 -54
- package/dist/gateway/server-maintenance.js +20 -5
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +465 -130
- package/dist/gateway/server-methods/config.js +193 -152
- package/dist/gateway/server-methods/devices.js +17 -3
- package/dist/gateway/server-methods/models.js +11 -1
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-methods/sessions.js +64 -8
- package/dist/gateway/server-methods/usage.js +162 -75
- package/dist/gateway/server-node-events.js +29 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +39 -13
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-startup-memory.js +17 -11
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/session-utils.fs.js +32 -34
- package/dist/gateway/sessions-resolve.js +17 -5
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.openai-mock.js +14 -7
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/gateway/tools-invoke-http.js +21 -10
- package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
- package/dist/hooks/bundled/command-logger/handler.js +7 -2
- package/dist/hooks/bundled/session-memory/handler.js +170 -38
- package/dist/hooks/frontmatter.js +6 -6
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/hooks/gmail-watcher.js +11 -6
- package/dist/hooks/internal-hooks.js +11 -1
- package/dist/hooks/llm-slug-generator.js +4 -1
- package/dist/hooks/workspace.js +47 -17
- package/dist/imessage/accounts.js +9 -20
- package/dist/imessage/monitor/inbound-processing.js +2 -1
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/archive.js +174 -73
- package/dist/infra/control-ui-assets.js +14 -6
- package/dist/infra/device-pairing.js +204 -144
- package/dist/infra/env.js +10 -5
- package/dist/infra/exec-approvals-allowlist.js +141 -70
- package/dist/infra/exec-approvals-analysis.js +78 -20
- package/dist/infra/exec-approvals.js +5 -17
- package/dist/infra/exec-safe-bin-policy.js +277 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/fs-safe.js +71 -39
- package/dist/infra/gateway-lock.js +6 -2
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/heartbeat-wake.js +6 -12
- package/dist/infra/host-env-security-policy.json +19 -0
- package/dist/infra/host-env-security.js +66 -0
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/net/ssrf.js +131 -38
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/bound-delivery-router.js +88 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/channel-selection.js +12 -6
- package/dist/infra/outbound/envelope.js +1 -1
- package/dist/infra/outbound/format.js +12 -6
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/outbound/payloads.js +14 -7
- package/dist/infra/outbound/session-binding-service.js +123 -0
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/path-guards.js +25 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/provider-usage.fetch.codex.js +7 -15
- package/dist/infra/provider-usage.fetch.gemini.js +14 -11
- package/dist/infra/provider-usage.fetch.shared.js +30 -1
- package/dist/infra/provider-usage.fetch.zai.js +10 -9
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/retry-policy.js +4 -2
- package/dist/infra/retry.js +9 -5
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/session-cost-usage.js +107 -59
- package/dist/infra/session-maintenance-warning.js +3 -1
- package/dist/infra/shell-env.js +98 -34
- package/dist/infra/ssh-config.js +12 -6
- package/dist/infra/system-run-command.js +49 -4
- package/dist/infra/update-channels.js +10 -5
- package/dist/infra/update-startup.js +86 -9
- package/dist/line/accounts.js +5 -7
- package/dist/line/bot-access.js +8 -20
- package/dist/line/bot-handlers.js +3 -1
- package/dist/link-understanding/detect.js +15 -7
- package/dist/media/constants.js +15 -6
- package/dist/media/image-ops.js +7 -0
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/media/local-roots.js +3 -2
- package/dist/media-understanding/apply.js +4 -1
- package/dist/media-understanding/concurrency.js +8 -20
- package/dist/memory/backend-config.js +45 -6
- package/dist/memory/embeddings.js +10 -4
- package/dist/memory/fs-utils.js +23 -0
- package/dist/memory/manager-search.js +12 -6
- package/dist/memory/manager-sync-ops.js +12 -2
- package/dist/memory/qmd-manager.js +466 -53
- package/dist/memory/query-expansion.js +167 -3
- package/dist/memory/status-format.js +10 -5
- package/dist/memory/sync-memory-files.js +1 -1
- package/dist/memory/test-manager.js +8 -0
- package/dist/node-host/invoke-system-run.js +281 -0
- package/dist/node-host/invoke.js +55 -337
- package/dist/pairing/pairing-store.js +22 -0
- package/dist/plugin-sdk/allow-from.js +1 -1
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/index.js +6 -3
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugin-sdk/webhook-targets.js +32 -0
- package/dist/plugins/bundled-dir.js +9 -6
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/hooks.js +50 -0
- package/dist/plugins/install.js +28 -16
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/plugins/runtime.js +3 -17
- package/dist/plugins/update.js +78 -12
- package/dist/process/spawn-utils.js +14 -7
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/providers/github-copilot-token.js +11 -6
- package/dist/providers/qwen-portal-oauth.js +14 -6
- package/dist/routing/account-id.js +30 -0
- package/dist/routing/resolve-route.js +3 -7
- package/dist/routing/session-key.js +2 -16
- package/dist/security/audit-channel.js +100 -20
- package/dist/security/audit-extra.async.js +505 -179
- package/dist/security/audit-extra.js +12 -2
- package/dist/security/audit-extra.sync.js +421 -35
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +180 -370
- package/dist/security/dm-policy-shared.js +68 -0
- package/dist/security/external-content.js +46 -14
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/entry-status.js +6 -0
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/node-match.js +11 -4
- package/dist/shared/operator-scope-compat.js +42 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/signal/accounts.js +7 -20
- package/dist/signal/monitor/event-handler.js +3 -1
- package/dist/slack/accounts.js +6 -19
- package/dist/slack/actions.js +11 -3
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/auth.js +1 -1
- package/dist/slack/monitor/message-handler/dispatch.js +50 -29
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/slack/monitor/replies.js +15 -7
- package/dist/slack/monitor/slash.js +22 -13
- package/dist/slack/resolve-channels.js +10 -5
- package/dist/slack/send.js +102 -12
- package/dist/slack/stream-mode.js +10 -0
- package/dist/slack/streaming.js +4 -2
- package/dist/telegram/accounts.js +19 -14
- package/dist/telegram/bot/helpers.js +3 -5
- package/dist/telegram/bot-access.js +35 -36
- package/dist/telegram/bot-handlers.js +120 -148
- package/dist/telegram/bot-message-context.js +68 -9
- package/dist/telegram/bot-message-dispatch.js +477 -210
- package/dist/telegram/bot-native-commands.js +16 -0
- package/dist/telegram/draft-stream.js +44 -8
- package/dist/telegram/inline-buttons.js +5 -15
- package/dist/telegram/monitor.js +11 -7
- package/dist/telegram/network-config.js +19 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/telegram/send.js +3 -2
- package/dist/telegram/sent-message-cache.js +5 -6
- package/dist/telegram/status-reaction-variants.js +208 -0
- package/dist/telegram/sticker-cache.js +11 -9
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/terminal/theme.js +12 -12
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tts/tts.js +80 -567
- package/dist/tui/components/chat-log.js +50 -8
- package/dist/tui/theme/theme.js +10 -12
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui-local-shell.js +16 -6
- package/dist/tui/tui.js +236 -48
- package/dist/utils/account-id.js +2 -4
- package/dist/utils/boolean.js +10 -5
- package/dist/utils/directive-tags.js +11 -0
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/queue-helpers.js +67 -12
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/auto-reply/deliver-reply.js +8 -4
- package/dist/web/auto-reply/mentions.js +10 -5
- package/dist/web/auto-reply/monitor/group-members.js +14 -7
- package/dist/web/auto-reply/monitor/process-message.js +45 -24
- package/dist/web/inbound/access-control.js +5 -2
- package/dist/web/login-qr.js +12 -6
- package/dist/web/media.js +126 -15
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
- package/extensions/bluebubbles/src/monitor.ts +208 -1950
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import { parseDurationMs } from "../cli/parse-duration.js";
|
|
|
4
4
|
import { ElevatedAllowFromSchema } from "./zod-schema.agent-runtime.js";
|
|
5
5
|
import { createAllowDenyChannelRulesSchema } from "./zod-schema.allowdeny.js";
|
|
6
6
|
import { GroupChatSchema, InboundDebounceSchema, NativeCommandsSettingSchema, QueueSchema, TtsConfigSchema, } from "./zod-schema.core.js";
|
|
7
|
+
import { sensitive } from "./zod-schema.sensitive.js";
|
|
7
8
|
const SessionResetConfigSchema = z
|
|
8
9
|
.object({
|
|
9
10
|
mode: z.union([z.literal("daily"), z.literal("idle")]).optional(),
|
|
@@ -56,6 +57,13 @@ export const SessionSchema = z
|
|
|
56
57
|
})
|
|
57
58
|
.strict()
|
|
58
59
|
.optional(),
|
|
60
|
+
threadBindings: z
|
|
61
|
+
.object({
|
|
62
|
+
enabled: z.boolean().optional(),
|
|
63
|
+
ttlHours: z.number().nonnegative().optional(),
|
|
64
|
+
})
|
|
65
|
+
.strict()
|
|
66
|
+
.optional(),
|
|
59
67
|
maintenance: z
|
|
60
68
|
.object({
|
|
61
69
|
mode: z.enum(["enforce", "warn"]).optional(),
|
|
@@ -106,6 +114,35 @@ export const MessagesSchema = z
|
|
|
106
114
|
ackReaction: z.string().optional(),
|
|
107
115
|
ackReactionScope: z.enum(["group-mentions", "group-all", "direct", "all"]).optional(),
|
|
108
116
|
removeAckAfterReply: z.boolean().optional(),
|
|
117
|
+
statusReactions: z
|
|
118
|
+
.object({
|
|
119
|
+
enabled: z.boolean().optional(),
|
|
120
|
+
emojis: z
|
|
121
|
+
.object({
|
|
122
|
+
thinking: z.string().optional(),
|
|
123
|
+
tool: z.string().optional(),
|
|
124
|
+
coding: z.string().optional(),
|
|
125
|
+
web: z.string().optional(),
|
|
126
|
+
done: z.string().optional(),
|
|
127
|
+
error: z.string().optional(),
|
|
128
|
+
stallSoft: z.string().optional(),
|
|
129
|
+
stallHard: z.string().optional(),
|
|
130
|
+
})
|
|
131
|
+
.strict()
|
|
132
|
+
.optional(),
|
|
133
|
+
timing: z
|
|
134
|
+
.object({
|
|
135
|
+
debounceMs: z.number().int().min(0).optional(),
|
|
136
|
+
stallSoftMs: z.number().int().min(0).optional(),
|
|
137
|
+
stallHardMs: z.number().int().min(0).optional(),
|
|
138
|
+
doneHoldMs: z.number().int().min(0).optional(),
|
|
139
|
+
errorHoldMs: z.number().int().min(0).optional(),
|
|
140
|
+
})
|
|
141
|
+
.strict()
|
|
142
|
+
.optional(),
|
|
143
|
+
})
|
|
144
|
+
.strict()
|
|
145
|
+
.optional(),
|
|
109
146
|
suppressToolErrors: z.boolean().optional(),
|
|
110
147
|
tts: TtsConfigSchema,
|
|
111
148
|
})
|
|
@@ -120,11 +157,13 @@ export const CommandsSchema = z
|
|
|
120
157
|
bashForegroundMs: z.number().int().min(0).max(30_000).optional(),
|
|
121
158
|
config: z.boolean().optional(),
|
|
122
159
|
debug: z.boolean().optional(),
|
|
123
|
-
restart: z.boolean().optional(),
|
|
160
|
+
restart: z.boolean().optional().default(true),
|
|
124
161
|
useAccessGroups: z.boolean().optional(),
|
|
125
162
|
ownerAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
163
|
+
ownerDisplay: z.enum(["raw", "hash"]).optional().default("raw"),
|
|
164
|
+
ownerDisplaySecret: z.string().optional().register(sensitive),
|
|
126
165
|
allowFrom: ElevatedAllowFromSchema.optional(),
|
|
127
166
|
})
|
|
128
167
|
.strict()
|
|
129
168
|
.optional()
|
|
130
|
-
.default({ native: "auto", nativeSkills: "auto" });
|
|
169
|
+
.default(() => ({ native: "auto", nativeSkills: "auto", restart: true, ownerDisplay: "raw" }));
|
package/dist/cron/run-log.js
CHANGED
|
@@ -93,6 +93,9 @@ export async function readCronRunLogEntries(filePath, opts) {
|
|
|
93
93
|
}
|
|
94
94
|
: undefined,
|
|
95
95
|
};
|
|
96
|
+
if (typeof obj.delivered === "boolean") {
|
|
97
|
+
entry.delivered = obj.delivered;
|
|
98
|
+
}
|
|
96
99
|
if (typeof obj.sessionId === "string" && obj.sessionId.trim().length > 0) {
|
|
97
100
|
entry.sessionId = obj.sessionId;
|
|
98
101
|
}
|
package/dist/cron/schedule.js
CHANGED
|
@@ -35,7 +35,11 @@ export function computeNextRunAtMs(schedule, nowMs) {
|
|
|
35
35
|
const steps = Math.max(1, Math.floor((elapsed + everyMs - 1) / everyMs));
|
|
36
36
|
return anchor + steps * everyMs;
|
|
37
37
|
}
|
|
38
|
-
const
|
|
38
|
+
const exprSource = schedule.expr;
|
|
39
|
+
if (typeof exprSource !== "string") {
|
|
40
|
+
throw new Error("invalid cron schedule: expr is required");
|
|
41
|
+
}
|
|
42
|
+
const expr = exprSource.trim();
|
|
39
43
|
if (!expr) {
|
|
40
44
|
return undefined;
|
|
41
45
|
}
|
|
@@ -43,17 +47,24 @@ export function computeNextRunAtMs(schedule, nowMs) {
|
|
|
43
47
|
timezone: resolveCronTimezone(schedule.tz),
|
|
44
48
|
catch: false,
|
|
45
49
|
});
|
|
46
|
-
|
|
47
|
-
// current second. This prevents the lookback from landing inside a matching
|
|
48
|
-
// second — if nowMs is e.g. 12:00:00.500 and the pattern fires at second 0,
|
|
49
|
-
// a 1ms lookback (12:00:00.499) is still *within* that second, causing
|
|
50
|
-
// croner to skip ahead to the next occurrence (e.g. the following day).
|
|
51
|
-
// Flooring first ensures the lookback always falls in the *previous* second.
|
|
52
|
-
const nowSecondMs = Math.floor(nowMs / 1000) * 1000;
|
|
53
|
-
const next = cron.nextRun(new Date(nowSecondMs - 1));
|
|
50
|
+
const next = cron.nextRun(new Date(nowMs));
|
|
54
51
|
if (!next) {
|
|
55
52
|
return undefined;
|
|
56
53
|
}
|
|
57
54
|
const nextMs = next.getTime();
|
|
58
|
-
|
|
55
|
+
if (!Number.isFinite(nextMs)) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (nextMs > nowMs) {
|
|
59
|
+
return nextMs;
|
|
60
|
+
}
|
|
61
|
+
// Guard against same-second rescheduling loops: if croner returns
|
|
62
|
+
// "now" (or an earlier instant), retry from the next whole second.
|
|
63
|
+
const nextSecondMs = Math.floor(nowMs / 1000) * 1000 + 1000;
|
|
64
|
+
const retry = cron.nextRun(new Date(nextSecondMs));
|
|
65
|
+
if (!retry) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const retryMs = retry.getTime();
|
|
69
|
+
return Number.isFinite(retryMs) && retryMs > nowMs ? retryMs : undefined;
|
|
59
70
|
}
|
package/dist/cron/service/ops.js
CHANGED
|
@@ -1,22 +1,40 @@
|
|
|
1
|
-
import { applyJobPatch, computeJobNextRunAtMs, createJob, findJobOrThrow, isJobDue, nextWakeAtMs, recomputeNextRuns, } from "./jobs.js";
|
|
1
|
+
import { applyJobPatch, computeJobNextRunAtMs, createJob, findJobOrThrow, isJobDue, nextWakeAtMs, recomputeNextRuns, recomputeNextRunsForMaintenance, } from "./jobs.js";
|
|
2
2
|
import { locked } from "./locked.js";
|
|
3
3
|
import { ensureLoaded, persist, warnIfDisabled } from "./store.js";
|
|
4
4
|
import { armTimer, emit, executeJob, runMissedJobs, stopTimer, wake } from "./timer.js";
|
|
5
|
+
async function ensureLoadedForRead(state) {
|
|
6
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
7
|
+
if (!state.store) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
// Use the maintenance-only version so that read-only operations never
|
|
11
|
+
// advance a past-due nextRunAtMs without executing the job (#16156).
|
|
12
|
+
const changed = recomputeNextRunsForMaintenance(state);
|
|
13
|
+
if (changed) {
|
|
14
|
+
await persist(state);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
5
17
|
export async function start(state) {
|
|
18
|
+
if (!state.deps.cronEnabled) {
|
|
19
|
+
state.deps.log.info({ enabled: false }, "cron: disabled");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const startupInterruptedJobIds = new Set();
|
|
6
23
|
await locked(state, async () => {
|
|
7
|
-
if (!state.deps.cronEnabled) {
|
|
8
|
-
state.deps.log.info({ enabled: false }, "cron: disabled");
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
24
|
await ensureLoaded(state, { skipRecompute: true });
|
|
12
25
|
const jobs = state.store?.jobs ?? [];
|
|
13
26
|
for (const job of jobs) {
|
|
14
27
|
if (typeof job.state.runningAtMs === "number") {
|
|
15
28
|
state.deps.log.warn({ jobId: job.id, runningAtMs: job.state.runningAtMs }, "cron: clearing stale running marker on startup");
|
|
16
29
|
job.state.runningAtMs = undefined;
|
|
30
|
+
startupInterruptedJobIds.add(job.id);
|
|
17
31
|
}
|
|
18
32
|
}
|
|
19
|
-
await
|
|
33
|
+
await persist(state);
|
|
34
|
+
});
|
|
35
|
+
await runMissedJobs(state, { skipJobIds: startupInterruptedJobIds });
|
|
36
|
+
await locked(state, async () => {
|
|
37
|
+
await ensureLoaded(state, { forceReload: true, skipRecompute: true });
|
|
20
38
|
recomputeNextRuns(state);
|
|
21
39
|
await persist(state);
|
|
22
40
|
armTimer(state);
|
|
@@ -32,13 +50,7 @@ export function stop(state) {
|
|
|
32
50
|
}
|
|
33
51
|
export async function status(state) {
|
|
34
52
|
return await locked(state, async () => {
|
|
35
|
-
await
|
|
36
|
-
if (state.store) {
|
|
37
|
-
const changed = recomputeNextRuns(state);
|
|
38
|
-
if (changed) {
|
|
39
|
-
await persist(state);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
53
|
+
await ensureLoadedForRead(state);
|
|
42
54
|
return {
|
|
43
55
|
enabled: state.deps.cronEnabled,
|
|
44
56
|
storePath: state.deps.storePath,
|
|
@@ -49,13 +61,7 @@ export async function status(state) {
|
|
|
49
61
|
}
|
|
50
62
|
export async function list(state, opts) {
|
|
51
63
|
return await locked(state, async () => {
|
|
52
|
-
await
|
|
53
|
-
if (state.store) {
|
|
54
|
-
const changed = recomputeNextRuns(state);
|
|
55
|
-
if (changed) {
|
|
56
|
-
await persist(state);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
64
|
+
await ensureLoadedForRead(state);
|
|
59
65
|
const includeDisabled = opts?.includeDisabled === true;
|
|
60
66
|
const jobs = (state.store?.jobs ?? []).filter((j) => includeDisabled || j.enabled);
|
|
61
67
|
return jobs.toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0));
|
|
@@ -90,7 +96,7 @@ export async function add(state, input) {
|
|
|
90
96
|
export async function update(state, id, patch) {
|
|
91
97
|
return await locked(state, async () => {
|
|
92
98
|
warnIfDisabled(state, "update");
|
|
93
|
-
await ensureLoaded(state);
|
|
99
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
94
100
|
const job = findJobOrThrow(state, id);
|
|
95
101
|
const now = state.deps.nowMs();
|
|
96
102
|
applyJobPatch(job, patch);
|
|
@@ -121,6 +127,14 @@ export async function update(state, id, patch) {
|
|
|
121
127
|
job.state.runningAtMs = undefined;
|
|
122
128
|
}
|
|
123
129
|
}
|
|
130
|
+
else if (job.enabled) {
|
|
131
|
+
// Non-schedule edits should not mutate other jobs, but still repair a
|
|
132
|
+
// missing/corrupt nextRunAtMs for the updated job.
|
|
133
|
+
const nextRun = job.state.nextRunAtMs;
|
|
134
|
+
if (typeof nextRun !== "number" || !Number.isFinite(nextRun)) {
|
|
135
|
+
job.state.nextRunAtMs = computeJobNextRunAtMs(job, now);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
124
138
|
await persist(state);
|
|
125
139
|
armTimer(state);
|
|
126
140
|
emit(state, {
|
|
@@ -19,6 +19,13 @@ const MIN_REFIRE_GAP_MS = 2_000;
|
|
|
19
19
|
* from wedging the entire cron lane.
|
|
20
20
|
*/
|
|
21
21
|
const DEFAULT_JOB_TIMEOUT_MS = 10 * 60_000; // 10 minutes
|
|
22
|
+
function resolveRunConcurrency(state) {
|
|
23
|
+
const raw = state.deps.cronConfig?.maxConcurrentRuns;
|
|
24
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
return Math.max(1, Math.floor(raw));
|
|
28
|
+
}
|
|
22
29
|
/**
|
|
23
30
|
* Exponential backoff delays (in ms) indexed by consecutive error count.
|
|
24
31
|
* After the last entry the delay stays constant.
|
|
@@ -45,6 +52,7 @@ function applyJobResult(state, job, result) {
|
|
|
45
52
|
job.state.lastStatus = result.status;
|
|
46
53
|
job.state.lastDurationMs = Math.max(0, result.endedAt - result.startedAt);
|
|
47
54
|
job.state.lastError = result.error;
|
|
55
|
+
job.state.lastDelivered = result.delivered;
|
|
48
56
|
job.updatedAtMs = result.endedAt;
|
|
49
57
|
// Track consecutive errors for backoff / auto-disable.
|
|
50
58
|
if (result.status === "error") {
|
|
@@ -187,8 +195,8 @@ export async function onTimer(state) {
|
|
|
187
195
|
job: j,
|
|
188
196
|
}));
|
|
189
197
|
});
|
|
190
|
-
const
|
|
191
|
-
|
|
198
|
+
const runDueJob = async (params) => {
|
|
199
|
+
const { id, job } = params;
|
|
192
200
|
const startedAt = state.deps.nowMs();
|
|
193
201
|
job.state.runningAtMs = startedAt;
|
|
194
202
|
emit(state, { jobId: job.id, action: "started", runAtMs: startedAt });
|
|
@@ -219,23 +227,41 @@ export async function onTimer(state) {
|
|
|
219
227
|
}
|
|
220
228
|
})()
|
|
221
229
|
: await executeJobCore(state, job);
|
|
222
|
-
|
|
230
|
+
return { jobId: id, ...result, startedAt, endedAt: state.deps.nowMs() };
|
|
223
231
|
}
|
|
224
232
|
catch (err) {
|
|
225
233
|
state.deps.log.warn({ jobId: id, jobName: job.name, timeoutMs: jobTimeoutMs ?? null }, `cron: job failed: ${String(err)}`);
|
|
226
|
-
|
|
234
|
+
return {
|
|
227
235
|
jobId: id,
|
|
228
236
|
status: "error",
|
|
229
237
|
error: String(err),
|
|
230
238
|
startedAt,
|
|
231
239
|
endedAt: state.deps.nowMs(),
|
|
232
|
-
}
|
|
240
|
+
};
|
|
233
241
|
}
|
|
234
|
-
}
|
|
235
|
-
|
|
242
|
+
};
|
|
243
|
+
const concurrency = Math.min(resolveRunConcurrency(state), Math.max(1, dueJobs.length));
|
|
244
|
+
const results = Array.from({ length: dueJobs.length });
|
|
245
|
+
let cursor = 0;
|
|
246
|
+
const workers = Array.from({ length: concurrency }, async () => {
|
|
247
|
+
for (;;) {
|
|
248
|
+
const index = cursor++;
|
|
249
|
+
if (index >= dueJobs.length) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const due = dueJobs[index];
|
|
253
|
+
if (!due) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
results[index] = await runDueJob(due);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
await Promise.all(workers);
|
|
260
|
+
const completedResults = results.filter((entry) => entry !== undefined);
|
|
261
|
+
if (completedResults.length > 0) {
|
|
236
262
|
await locked(state, async () => {
|
|
237
263
|
await ensureLoaded(state, { forceReload: true, skipRecompute: true });
|
|
238
|
-
for (const result of
|
|
264
|
+
for (const result of completedResults) {
|
|
239
265
|
const job = state.store?.jobs.find((j) => j.id === result.jobId);
|
|
240
266
|
if (!job) {
|
|
241
267
|
continue;
|
|
@@ -243,6 +269,7 @@ export async function onTimer(state) {
|
|
|
243
269
|
const shouldDelete = applyJobResult(state, job, {
|
|
244
270
|
status: result.status,
|
|
245
271
|
error: result.error,
|
|
272
|
+
delivered: result.delivered,
|
|
246
273
|
startedAt: result.startedAt,
|
|
247
274
|
endedAt: result.endedAt,
|
|
248
275
|
});
|
|
@@ -342,18 +369,88 @@ function collectRunnableJobs(state, nowMs, opts) {
|
|
|
342
369
|
}));
|
|
343
370
|
}
|
|
344
371
|
export async function runMissedJobs(state, opts) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
372
|
+
const startupCandidates = await locked(state, async () => {
|
|
373
|
+
await ensureLoaded(state, { skipRecompute: true });
|
|
374
|
+
if (!state.store) {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
const now = state.deps.nowMs();
|
|
378
|
+
const skipJobIds = opts?.skipJobIds;
|
|
379
|
+
const missed = collectRunnableJobs(state, now, { skipJobIds, skipAtIfAlreadyRan: true });
|
|
380
|
+
if (missed.length === 0) {
|
|
381
|
+
return [];
|
|
382
|
+
}
|
|
352
383
|
state.deps.log.info({ count: missed.length, jobIds: missed.map((j) => j.id) }, "cron: running missed jobs after restart");
|
|
353
384
|
for (const job of missed) {
|
|
354
|
-
|
|
385
|
+
job.state.runningAtMs = now;
|
|
386
|
+
job.state.lastError = undefined;
|
|
387
|
+
}
|
|
388
|
+
await persist(state);
|
|
389
|
+
return missed.map((job) => ({ jobId: job.id, job }));
|
|
390
|
+
});
|
|
391
|
+
if (startupCandidates.length === 0) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const outcomes = [];
|
|
395
|
+
for (const candidate of startupCandidates) {
|
|
396
|
+
const startedAt = state.deps.nowMs();
|
|
397
|
+
emit(state, { jobId: candidate.job.id, action: "started", runAtMs: startedAt });
|
|
398
|
+
try {
|
|
399
|
+
const result = await executeJobCore(state, candidate.job);
|
|
400
|
+
outcomes.push({
|
|
401
|
+
jobId: candidate.jobId,
|
|
402
|
+
status: result.status,
|
|
403
|
+
error: result.error,
|
|
404
|
+
summary: result.summary,
|
|
405
|
+
delivered: result.delivered,
|
|
406
|
+
sessionId: result.sessionId,
|
|
407
|
+
sessionKey: result.sessionKey,
|
|
408
|
+
model: result.model,
|
|
409
|
+
provider: result.provider,
|
|
410
|
+
usage: result.usage,
|
|
411
|
+
startedAt,
|
|
412
|
+
endedAt: state.deps.nowMs(),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
outcomes.push({
|
|
417
|
+
jobId: candidate.jobId,
|
|
418
|
+
status: "error",
|
|
419
|
+
error: String(err),
|
|
420
|
+
startedAt,
|
|
421
|
+
endedAt: state.deps.nowMs(),
|
|
422
|
+
});
|
|
355
423
|
}
|
|
356
424
|
}
|
|
425
|
+
await locked(state, async () => {
|
|
426
|
+
await ensureLoaded(state, { forceReload: true, skipRecompute: true });
|
|
427
|
+
if (!state.store) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
for (const result of outcomes) {
|
|
431
|
+
const job = state.store.jobs.find((entry) => entry.id === result.jobId);
|
|
432
|
+
if (!job) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const shouldDelete = applyJobResult(state, job, {
|
|
436
|
+
status: result.status,
|
|
437
|
+
error: result.error,
|
|
438
|
+
delivered: result.delivered,
|
|
439
|
+
startedAt: result.startedAt,
|
|
440
|
+
endedAt: result.endedAt,
|
|
441
|
+
});
|
|
442
|
+
emitJobFinished(state, job, result, result.startedAt);
|
|
443
|
+
if (shouldDelete) {
|
|
444
|
+
state.store.jobs = state.store.jobs.filter((entry) => entry.id !== job.id);
|
|
445
|
+
emit(state, { jobId: job.id, action: "removed" });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// Preserve any new past-due nextRunAtMs values that became due while
|
|
449
|
+
// startup catch-up was running. They should execute on a future tick
|
|
450
|
+
// instead of being silently advanced.
|
|
451
|
+
recomputeNextRunsForMaintenance(state);
|
|
452
|
+
await persist(state);
|
|
453
|
+
});
|
|
357
454
|
}
|
|
358
455
|
export async function runDueJobs(state) {
|
|
359
456
|
if (!state.store) {
|
|
@@ -463,6 +560,7 @@ async function executeJobCore(state, job) {
|
|
|
463
560
|
status: res.status,
|
|
464
561
|
error: res.error,
|
|
465
562
|
summary: res.summary,
|
|
563
|
+
delivered: res.delivered,
|
|
466
564
|
sessionId: res.sessionId,
|
|
467
565
|
sessionKey: res.sessionKey,
|
|
468
566
|
model: res.model,
|
|
@@ -493,6 +591,7 @@ export async function executeJob(state, job, _nowMs, _opts) {
|
|
|
493
591
|
const shouldDelete = applyJobResult(state, job, {
|
|
494
592
|
status: coreResult.status,
|
|
495
593
|
error: coreResult.error,
|
|
594
|
+
delivered: coreResult.delivered,
|
|
496
595
|
startedAt,
|
|
497
596
|
endedAt,
|
|
498
597
|
});
|
|
@@ -509,6 +608,7 @@ function emitJobFinished(state, job, result, runAtMs) {
|
|
|
509
608
|
status: result.status,
|
|
510
609
|
error: result.error,
|
|
511
610
|
summary: result.summary,
|
|
611
|
+
delivered: result.delivered,
|
|
512
612
|
sessionId: result.sessionId,
|
|
513
613
|
sessionKey: result.sessionKey,
|
|
514
614
|
runAtMs,
|
package/dist/cron/stagger.js
CHANGED
|
@@ -33,5 +33,7 @@ export function resolveCronStaggerMs(schedule) {
|
|
|
33
33
|
if (explicit !== undefined) {
|
|
34
34
|
return explicit;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
const expr = schedule.expr;
|
|
37
|
+
const cronExpr = typeof expr === "string" ? expr : "";
|
|
38
|
+
return resolveDefaultCronStaggerMs(cronExpr) ?? 0;
|
|
37
39
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { splitArgsPreservingQuotes } from "./arg-split.js";
|
|
2
|
+
import { assertNoCmdLineBreak } from "./cmd-set.js";
|
|
3
|
+
export function quoteCmdScriptArg(value) {
|
|
4
|
+
assertNoCmdLineBreak(value, "Command argument");
|
|
5
|
+
if (!value) {
|
|
6
|
+
return '""';
|
|
7
|
+
}
|
|
8
|
+
const escaped = value.replace(/"/g, '\\"').replace(/%/g, "%%").replace(/!/g, "^!");
|
|
9
|
+
if (!/[ \t"&|<>^()%!]/g.test(value)) {
|
|
10
|
+
return escaped;
|
|
11
|
+
}
|
|
12
|
+
return `"${escaped}"`;
|
|
13
|
+
}
|
|
14
|
+
export function unescapeCmdScriptArg(value) {
|
|
15
|
+
return value.replace(/\^!/g, "!").replace(/%%/g, "%");
|
|
16
|
+
}
|
|
17
|
+
export function parseCmdScriptCommandLine(value) {
|
|
18
|
+
// Script renderer escapes quotes (`\"`) and cmd expansions (`%%`, `^!`).
|
|
19
|
+
// Keep all other backslashes literal so Windows drive/UNC paths survive.
|
|
20
|
+
return splitArgsPreservingQuotes(value, { escapeMode: "backslash-quote-only" }).map(unescapeCmdScriptArg);
|
|
21
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function assertNoCmdLineBreak(value, field) {
|
|
2
|
+
if (/[\r\n]/.test(value)) {
|
|
3
|
+
throw new Error(`${field} cannot contain CR or LF in Windows task scripts.`);
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
function escapeCmdSetAssignmentComponent(value) {
|
|
7
|
+
return value.replace(/\^/g, "^^").replace(/%/g, "%%").replace(/!/g, "^!").replace(/"/g, '^"');
|
|
8
|
+
}
|
|
9
|
+
function unescapeCmdSetAssignmentComponent(value) {
|
|
10
|
+
let out = "";
|
|
11
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
12
|
+
const ch = value[i];
|
|
13
|
+
const next = value[i + 1];
|
|
14
|
+
if (ch === "^" && (next === "^" || next === '"' || next === "!")) {
|
|
15
|
+
out += next;
|
|
16
|
+
i += 1;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (ch === "%" && next === "%") {
|
|
20
|
+
out += "%";
|
|
21
|
+
i += 1;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
out += ch;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
export function parseCmdSetAssignment(line) {
|
|
29
|
+
const raw = line.trim();
|
|
30
|
+
if (!raw) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const quoted = raw.startsWith('"') && raw.endsWith('"') && raw.length >= 2;
|
|
34
|
+
const assignment = quoted ? raw.slice(1, -1) : raw;
|
|
35
|
+
const index = assignment.indexOf("=");
|
|
36
|
+
if (index <= 0) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const key = assignment.slice(0, index).trim();
|
|
40
|
+
const value = assignment.slice(index + 1).trim();
|
|
41
|
+
if (!key) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (!quoted) {
|
|
45
|
+
return { key, value };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
key: unescapeCmdSetAssignmentComponent(key),
|
|
49
|
+
value: unescapeCmdSetAssignmentComponent(value),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function renderCmdSetAssignment(key, value) {
|
|
53
|
+
assertNoCmdLineBreak(key, "Environment variable name");
|
|
54
|
+
assertNoCmdLineBreak(value, "Environment variable value");
|
|
55
|
+
const escapedKey = escapeCmdSetAssignmentComponent(key);
|
|
56
|
+
const escapedValue = escapeCmdSetAssignmentComponent(value);
|
|
57
|
+
return `set "${escapedKey}=${escapedValue}"`;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/discord/api.js
CHANGED
|
@@ -9,12 +9,14 @@ const DISCORD_API_RETRY_DEFAULTS = {
|
|
|
9
9
|
};
|
|
10
10
|
function parseDiscordApiErrorPayload(text) {
|
|
11
11
|
const trimmed = text.trim();
|
|
12
|
-
if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
|
|
12
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
|
13
13
|
return null;
|
|
14
|
+
}
|
|
14
15
|
try {
|
|
15
16
|
const payload = JSON.parse(trimmed);
|
|
16
|
-
if (payload && typeof payload === "object")
|
|
17
|
+
if (payload && typeof payload === "object") {
|
|
17
18
|
return payload;
|
|
19
|
+
}
|
|
18
20
|
}
|
|
19
21
|
catch {
|
|
20
22
|
return null;
|
|
@@ -26,24 +28,28 @@ function parseRetryAfterSeconds(text, response) {
|
|
|
26
28
|
const retryAfter = payload && typeof payload.retry_after === "number" && Number.isFinite(payload.retry_after)
|
|
27
29
|
? payload.retry_after
|
|
28
30
|
: undefined;
|
|
29
|
-
if (retryAfter !== undefined)
|
|
31
|
+
if (retryAfter !== undefined) {
|
|
30
32
|
return retryAfter;
|
|
33
|
+
}
|
|
31
34
|
const header = response.headers.get("Retry-After");
|
|
32
|
-
if (!header)
|
|
35
|
+
if (!header) {
|
|
33
36
|
return undefined;
|
|
37
|
+
}
|
|
34
38
|
const parsed = Number(header);
|
|
35
39
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
36
40
|
}
|
|
37
41
|
function formatRetryAfterSeconds(value) {
|
|
38
|
-
if (value === undefined || !Number.isFinite(value) || value < 0)
|
|
42
|
+
if (value === undefined || !Number.isFinite(value) || value < 0) {
|
|
39
43
|
return undefined;
|
|
44
|
+
}
|
|
40
45
|
const rounded = value < 10 ? value.toFixed(1) : Math.round(value).toString();
|
|
41
46
|
return `${rounded}s`;
|
|
42
47
|
}
|
|
43
48
|
function formatDiscordApiErrorText(text) {
|
|
44
49
|
const trimmed = text.trim();
|
|
45
|
-
if (!trimmed)
|
|
50
|
+
if (!trimmed) {
|
|
46
51
|
return undefined;
|
|
52
|
+
}
|
|
47
53
|
const payload = parseDiscordApiErrorPayload(trimmed);
|
|
48
54
|
if (!payload) {
|
|
49
55
|
const looksJson = trimmed.startsWith("{") && trimmed.endsWith("}");
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolveTextChunkLimit } from "../auto-reply/chunk.js";
|
|
2
|
+
import { getChannelDock } from "../channels/dock.js";
|
|
3
|
+
import { normalizeAccountId } from "../routing/session-key.js";
|
|
4
|
+
const DEFAULT_DISCORD_DRAFT_STREAM_MIN = 200;
|
|
5
|
+
const DEFAULT_DISCORD_DRAFT_STREAM_MAX = 800;
|
|
6
|
+
export function resolveDiscordDraftStreamingChunking(cfg, accountId) {
|
|
7
|
+
const providerChunkLimit = getChannelDock("discord")?.outbound?.textChunkLimit;
|
|
8
|
+
const textLimit = resolveTextChunkLimit(cfg, "discord", accountId, {
|
|
9
|
+
fallbackLimit: providerChunkLimit,
|
|
10
|
+
});
|
|
11
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
12
|
+
const draftCfg = cfg?.channels?.discord?.accounts?.[normalizedAccountId]?.draftChunk ??
|
|
13
|
+
cfg?.channels?.discord?.draftChunk;
|
|
14
|
+
const maxRequested = Math.max(1, Math.floor(draftCfg?.maxChars ?? DEFAULT_DISCORD_DRAFT_STREAM_MAX));
|
|
15
|
+
const maxChars = Math.max(1, Math.min(maxRequested, textLimit));
|
|
16
|
+
const minRequested = Math.max(1, Math.floor(draftCfg?.minChars ?? DEFAULT_DISCORD_DRAFT_STREAM_MIN));
|
|
17
|
+
const minChars = Math.min(minRequested, maxChars);
|
|
18
|
+
const breakPreference = draftCfg?.breakPreference === "newline" || draftCfg?.breakPreference === "sentence"
|
|
19
|
+
? draftCfg.breakPreference
|
|
20
|
+
: "paragraph";
|
|
21
|
+
return { minChars, maxChars, breakPreference };
|
|
22
|
+
}
|