@poolzin/pool-bot 2026.2.25 → 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/dist/acp/event-mapper.js +87 -22
- package/dist/acp/meta.js +12 -6
- package/dist/agents/agent-paths.js +8 -9
- package/dist/agents/agent-scope.js +7 -5
- package/dist/agents/auth-profiles/oauth.js +148 -64
- package/dist/agents/auth-profiles/session-override.js +13 -7
- package/dist/agents/bash-tools.exec-host-gateway.js +14 -4
- package/dist/agents/bash-tools.exec-runtime.js +2 -25
- 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/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-selection.js +7 -1
- package/dist/agents/models-config.providers.js +93 -11
- 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-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.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/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.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/fast-coding-tools.js +1 -18
- package/dist/agents/test-helpers/fast-core-tools.js +1 -17
- 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.js +51 -26
- package/dist/agents/tools/browser-tool.js +2 -2
- package/dist/agents/tools/canvas-tool.js +27 -1
- package/dist/agents/tools/common.js +45 -0
- package/dist/agents/tools/discord-actions-guild.js +4 -1
- package/dist/agents/tools/gateway-tool.js +3 -1
- package/dist/agents/tools/nodes-utils.js +1 -10
- 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/auto-reply/commands-registry.data.js +51 -0
- package/dist/auto-reply/commands-registry.js +4 -3
- package/dist/auto-reply/group-activation.js +10 -5
- package/dist/auto-reply/inbound-debounce.js +10 -5
- package/dist/auto-reply/reply/abort.js +1 -1
- package/dist/auto-reply/reply/agent-runner-execution.js +4 -1
- 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.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 +4 -0
- package/dist/browser/extension-relay-auth.js +55 -0
- package/dist/browser/extension-relay.js +74 -29
- package/dist/browser/navigation-guard.js +9 -1
- 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 +2 -2
- package/dist/browser/server-context.js +7 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +2 -1
- 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/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/cli/banner.js +1 -1
- 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/gateway-cli/run-loop.js +23 -5
- package/dist/cli/node-cli/register.js +14 -5
- package/dist/cli/nodes-media-utils.js +7 -2
- 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 +34 -41
- package/dist/cli/ports.js +11 -10
- package/dist/cli/program/command-registry.js +2 -11
- package/dist/cli/program/command-tree.js +16 -0
- 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 +11 -7
- package/dist/cli/system-cli.js +36 -46
- 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 +22 -2
- 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/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/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/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 +30 -16
- package/dist/config/config-paths.js +9 -11
- package/dist/config/defaults.js +22 -2
- package/dist/config/discord-preview-streaming.js +104 -0
- package/dist/config/env-vars.js +37 -8
- package/dist/config/includes.js +4 -0
- package/dist/config/io.js +97 -12
- package/dist/config/legacy.migrations.part-1.js +189 -78
- package/dist/config/legacy.shared.js +3 -1
- package/dist/config/merge-patch.js +4 -0
- package/dist/config/prototype-keys.js +4 -0
- package/dist/config/schema.help.js +44 -7
- 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/zod-schema.agent-runtime.js +11 -0
- package/dist/config/zod-schema.js +148 -13
- 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/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/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 +34 -10
- package/dist/gateway/call.js +4 -16
- package/dist/gateway/client.js +28 -4
- package/dist/gateway/config-reload.js +3 -4
- package/dist/gateway/control-ui.js +219 -96
- package/dist/gateway/hooks-mapping.js +88 -38
- package/dist/gateway/http-auth-helpers.js +3 -2
- package/dist/gateway/http-endpoint-helpers.js +1 -0
- package/dist/gateway/net.js +54 -12
- 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 +1 -0
- package/dist/gateway/origin-check.js +1 -18
- package/dist/gateway/protocol/index.js +4 -3
- 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 +2 -1
- package/dist/gateway/protocol/schema/sessions.js +6 -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 +16 -7
- package/dist/gateway/server-maintenance.js +20 -5
- package/dist/gateway/server-methods/chat.js +10 -6
- package/dist/gateway/server-methods/config.js +12 -14
- package/dist/gateway/server-methods/devices.js +17 -3
- package/dist/gateway/server-methods/models.js +11 -1
- 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-runtime-config.js +34 -13
- package/dist/gateway/server-startup-memory.js +17 -11
- package/dist/gateway/session-utils.fs.js +32 -34
- package/dist/gateway/sessions-resolve.js +17 -5
- package/dist/gateway/test-helpers.openai-mock.js +14 -7
- 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 +6 -5
- package/dist/hooks/frontmatter.js +6 -6
- 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.js +174 -73
- package/dist/infra/control-ui-assets.js +14 -6
- package/dist/infra/device-pairing.js +108 -29
- package/dist/infra/env.js +10 -5
- package/dist/infra/exec-approvals-allowlist.js +122 -0
- package/dist/infra/exec-approvals-analysis.js +34 -3
- package/dist/infra/exec-approvals.js +5 -17
- package/dist/infra/exec-safe-bin-policy.js +53 -45
- package/dist/infra/fs-safe.js +71 -39
- package/dist/infra/gateway-lock.js +6 -2
- 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/net/ssrf.js +131 -38
- package/dist/infra/outbound/bound-delivery-router.js +88 -0
- 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/payloads.js +14 -7
- package/dist/infra/outbound/session-binding-service.js +123 -0
- package/dist/infra/path-guards.js +25 -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/retry-policy.js +4 -2
- package/dist/infra/retry.js +9 -5
- 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/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/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/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/webhook-targets.js +32 -0
- package/dist/plugins/bundled-dir.js +9 -6
- package/dist/plugins/hooks.js +50 -0
- package/dist/plugins/install.js +28 -16
- 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-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 +93 -2
- package/dist/security/audit-extra.async.js +159 -5
- package/dist/security/audit-extra.js +1 -1
- package/dist/security/audit-extra.sync.js +85 -6
- package/dist/security/audit.js +40 -4
- package/dist/security/dm-policy-shared.js +44 -0
- package/dist/security/external-content.js +26 -6
- package/dist/shared/entry-status.js +6 -0
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-match.js +11 -4
- package/dist/shared/operator-scope-compat.js +8 -3
- 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/monitor/auth.js +1 -1
- package/dist/slack/monitor/message-handler/dispatch.js +50 -29
- 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 +155 -90
- package/dist/telegram/bot-native-commands.js +16 -0
- package/dist/telegram/draft-stream.js +14 -1
- 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/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/theme.js +12 -12
- package/dist/tts/tts.js +80 -567
- package/dist/tui/components/chat-log.js +41 -8
- package/dist/tui/theme/theme.js +10 -12
- package/dist/tui/tui-local-shell.js +16 -6
- package/dist/tui/tui.js +58 -6
- 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/queue-helpers.js +67 -12
- 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 +123 -16
- package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
- package/extensions/bluebubbles/src/monitor.ts +208 -1950
- package/package.json +1 -1
package/dist/tts/tts.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, mkdtempSync, rmSync, renameSync, unlinkSync, } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { completeSimple } from "@mariozechner/pi-ai";
|
|
5
|
-
import { EdgeTTS } from "node-edge-tts";
|
|
6
4
|
import { normalizeChannelId } from "../channels/plugins/index.js";
|
|
7
5
|
import { logVerbose } from "../globals.js";
|
|
6
|
+
import { resolvePreferredPoolbotTmpDir } from "../infra/tmp-poolbot-dir.js";
|
|
7
|
+
import { stripMarkdown } from "../line/markdown-to-line.js";
|
|
8
8
|
import { isVoiceCompatibleAudio } from "../media/audio.js";
|
|
9
9
|
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
import { resolveModel } from "../agents/pi-embedded-runner/model.js";
|
|
10
|
+
import { edgeTTS, elevenLabsTTS, inferEdgeExtension, isValidOpenAIModel, isValidOpenAIVoice, isValidVoiceId, OPENAI_TTS_MODELS, OPENAI_TTS_VOICES, openaiTTS, parseTtsDirectives, scheduleCleanup, summarizeText, } from "./tts-core.js";
|
|
11
|
+
export { OPENAI_TTS_MODELS, OPENAI_TTS_VOICES } from "./tts-core.js";
|
|
13
12
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
14
13
|
const DEFAULT_TTS_MAX_LENGTH = 1500;
|
|
15
14
|
const DEFAULT_TTS_SUMMARIZE = true;
|
|
16
15
|
const DEFAULT_MAX_TEXT_LENGTH = 4096;
|
|
17
|
-
const TEMP_FILE_CLEANUP_DELAY_MS = 5 * 60 * 1000; // 5 minutes
|
|
18
16
|
const DEFAULT_ELEVENLABS_BASE_URL = "https://api.elevenlabs.io";
|
|
19
17
|
const DEFAULT_ELEVENLABS_VOICE_ID = "pMsXgVXv3BLzUgSXRplE";
|
|
20
18
|
const DEFAULT_ELEVENLABS_MODEL_ID = "eleven_multilingual_v2";
|
|
@@ -51,8 +49,9 @@ const TELEPHONY_OUTPUT = {
|
|
|
51
49
|
const TTS_AUTO_MODES = new Set(["off", "always", "inbound", "tagged"]);
|
|
52
50
|
let lastTtsAttempt;
|
|
53
51
|
export function normalizeTtsAutoMode(value) {
|
|
54
|
-
if (typeof value !== "string")
|
|
52
|
+
if (typeof value !== "string") {
|
|
55
53
|
return undefined;
|
|
54
|
+
}
|
|
56
55
|
const normalized = value.trim().toLowerCase();
|
|
57
56
|
if (TTS_AUTO_MODES.has(normalized)) {
|
|
58
57
|
return normalized;
|
|
@@ -73,11 +72,12 @@ function resolveModelOverridePolicy(overrides) {
|
|
|
73
72
|
allowSeed: false,
|
|
74
73
|
};
|
|
75
74
|
}
|
|
76
|
-
const allow = (value) => value ??
|
|
75
|
+
const allow = (value, defaultValue = true) => value ?? defaultValue;
|
|
77
76
|
return {
|
|
78
77
|
enabled: true,
|
|
79
78
|
allowText: allow(overrides?.allowText),
|
|
80
|
-
|
|
79
|
+
// Provider switching is higher-impact than voice/style tweaks; keep opt-in.
|
|
80
|
+
allowProvider: allow(overrides?.allowProvider, false),
|
|
81
81
|
allowVoice: allow(overrides?.allowVoice),
|
|
82
82
|
allowModelId: allow(overrides?.allowModelId),
|
|
83
83
|
allowVoiceSettings: allow(overrides?.allowVoiceSettings),
|
|
@@ -139,17 +139,20 @@ export function resolveTtsConfig(cfg) {
|
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
141
|
export function resolveTtsPrefsPath(config) {
|
|
142
|
-
if (config.prefsPath?.trim())
|
|
142
|
+
if (config.prefsPath?.trim()) {
|
|
143
143
|
return resolveUserPath(config.prefsPath.trim());
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
}
|
|
145
|
+
const envPath = process.env.POOLBOT_TTS_PREFS?.trim();
|
|
146
|
+
if (envPath) {
|
|
146
147
|
return resolveUserPath(envPath);
|
|
148
|
+
}
|
|
147
149
|
return path.join(CONFIG_DIR, "settings", "tts.json");
|
|
148
150
|
}
|
|
149
151
|
function resolveTtsAutoModeFromPrefs(prefs) {
|
|
150
152
|
const auto = normalizeTtsAutoMode(prefs.tts?.auto);
|
|
151
|
-
if (auto)
|
|
153
|
+
if (auto) {
|
|
152
154
|
return auto;
|
|
155
|
+
}
|
|
153
156
|
if (typeof prefs.tts?.enabled === "boolean") {
|
|
154
157
|
return prefs.tts.enabled ? "always" : "off";
|
|
155
158
|
}
|
|
@@ -157,19 +160,22 @@ function resolveTtsAutoModeFromPrefs(prefs) {
|
|
|
157
160
|
}
|
|
158
161
|
export function resolveTtsAutoMode(params) {
|
|
159
162
|
const sessionAuto = normalizeTtsAutoMode(params.sessionAuto);
|
|
160
|
-
if (sessionAuto)
|
|
163
|
+
if (sessionAuto) {
|
|
161
164
|
return sessionAuto;
|
|
165
|
+
}
|
|
162
166
|
const prefsAuto = resolveTtsAutoModeFromPrefs(readPrefs(params.prefsPath));
|
|
163
|
-
if (prefsAuto)
|
|
167
|
+
if (prefsAuto) {
|
|
164
168
|
return prefsAuto;
|
|
169
|
+
}
|
|
165
170
|
return params.config.auto;
|
|
166
171
|
}
|
|
167
172
|
export function buildTtsSystemPromptHint(cfg) {
|
|
168
173
|
const config = resolveTtsConfig(cfg);
|
|
169
174
|
const prefsPath = resolveTtsPrefsPath(config);
|
|
170
175
|
const autoMode = resolveTtsAutoMode({ config, prefsPath });
|
|
171
|
-
if (autoMode === "off")
|
|
176
|
+
if (autoMode === "off") {
|
|
172
177
|
return undefined;
|
|
178
|
+
}
|
|
173
179
|
const maxLength = getTtsMaxLength(prefsPath);
|
|
174
180
|
const summarize = isSummarizationEnabled(prefsPath) ? "on" : "off";
|
|
175
181
|
const autoHint = autoMode === "inbound"
|
|
@@ -188,8 +194,9 @@ export function buildTtsSystemPromptHint(cfg) {
|
|
|
188
194
|
}
|
|
189
195
|
function readPrefs(prefsPath) {
|
|
190
196
|
try {
|
|
191
|
-
if (!existsSync(prefsPath))
|
|
197
|
+
if (!existsSync(prefsPath)) {
|
|
192
198
|
return {};
|
|
199
|
+
}
|
|
193
200
|
return JSON.parse(readFileSync(prefsPath, "utf8"));
|
|
194
201
|
}
|
|
195
202
|
catch {
|
|
@@ -197,8 +204,8 @@ function readPrefs(prefsPath) {
|
|
|
197
204
|
}
|
|
198
205
|
}
|
|
199
206
|
function atomicWriteFileSync(filePath, content) {
|
|
200
|
-
const tmpPath = `${filePath}.tmp.${Date.now()}.${
|
|
201
|
-
writeFileSync(tmpPath, content);
|
|
207
|
+
const tmpPath = `${filePath}.tmp.${Date.now()}.${randomBytes(8).toString("hex")}`;
|
|
208
|
+
writeFileSync(tmpPath, content, { mode: 0o600 });
|
|
202
209
|
try {
|
|
203
210
|
renameSync(tmpPath, filePath);
|
|
204
211
|
}
|
|
@@ -234,14 +241,18 @@ export function setTtsEnabled(prefsPath, enabled) {
|
|
|
234
241
|
}
|
|
235
242
|
export function getTtsProvider(config, prefsPath) {
|
|
236
243
|
const prefs = readPrefs(prefsPath);
|
|
237
|
-
if (prefs.tts?.provider)
|
|
244
|
+
if (prefs.tts?.provider) {
|
|
238
245
|
return prefs.tts.provider;
|
|
239
|
-
|
|
246
|
+
}
|
|
247
|
+
if (config.providerSource === "config") {
|
|
240
248
|
return config.provider;
|
|
241
|
-
|
|
249
|
+
}
|
|
250
|
+
if (resolveTtsApiKey(config, "openai")) {
|
|
242
251
|
return "openai";
|
|
243
|
-
|
|
252
|
+
}
|
|
253
|
+
if (resolveTtsApiKey(config, "elevenlabs")) {
|
|
244
254
|
return "elevenlabs";
|
|
255
|
+
}
|
|
245
256
|
return "edge";
|
|
246
257
|
}
|
|
247
258
|
export function setTtsProvider(prefsPath, provider) {
|
|
@@ -274,8 +285,9 @@ export function setLastTtsAttempt(entry) {
|
|
|
274
285
|
lastTtsAttempt = entry;
|
|
275
286
|
}
|
|
276
287
|
function resolveOutputFormat(channelId) {
|
|
277
|
-
if (channelId === "telegram")
|
|
288
|
+
if (channelId === "telegram") {
|
|
278
289
|
return TELEGRAM_OUTPUT;
|
|
290
|
+
}
|
|
279
291
|
return DEFAULT_OUTPUT;
|
|
280
292
|
}
|
|
281
293
|
function resolveChannelId(channel) {
|
|
@@ -298,518 +310,17 @@ export function resolveTtsProviderOrder(primary) {
|
|
|
298
310
|
return [primary, ...TTS_PROVIDERS.filter((provider) => provider !== primary)];
|
|
299
311
|
}
|
|
300
312
|
export function isTtsProviderConfigured(config, provider) {
|
|
301
|
-
if (provider === "edge")
|
|
313
|
+
if (provider === "edge") {
|
|
302
314
|
return config.edge.enabled;
|
|
303
|
-
return Boolean(resolveTtsApiKey(config, provider));
|
|
304
|
-
}
|
|
305
|
-
function isValidVoiceId(voiceId) {
|
|
306
|
-
return /^[a-zA-Z0-9]{10,40}$/.test(voiceId);
|
|
307
|
-
}
|
|
308
|
-
function normalizeElevenLabsBaseUrl(baseUrl) {
|
|
309
|
-
const trimmed = baseUrl.trim();
|
|
310
|
-
if (!trimmed)
|
|
311
|
-
return DEFAULT_ELEVENLABS_BASE_URL;
|
|
312
|
-
return trimmed.replace(/\/+$/, "");
|
|
313
|
-
}
|
|
314
|
-
function requireInRange(value, min, max, label) {
|
|
315
|
-
if (!Number.isFinite(value) || value < min || value > max) {
|
|
316
|
-
throw new Error(`${label} must be between ${min} and ${max}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
function assertElevenLabsVoiceSettings(settings) {
|
|
320
|
-
requireInRange(settings.stability, 0, 1, "stability");
|
|
321
|
-
requireInRange(settings.similarityBoost, 0, 1, "similarityBoost");
|
|
322
|
-
requireInRange(settings.style, 0, 1, "style");
|
|
323
|
-
requireInRange(settings.speed, 0.5, 2, "speed");
|
|
324
|
-
}
|
|
325
|
-
function normalizeLanguageCode(code) {
|
|
326
|
-
const trimmed = code?.trim();
|
|
327
|
-
if (!trimmed)
|
|
328
|
-
return undefined;
|
|
329
|
-
const normalized = trimmed.toLowerCase();
|
|
330
|
-
if (!/^[a-z]{2}$/.test(normalized)) {
|
|
331
|
-
throw new Error("languageCode must be a 2-letter ISO 639-1 code (e.g. en, de, fr)");
|
|
332
|
-
}
|
|
333
|
-
return normalized;
|
|
334
|
-
}
|
|
335
|
-
function normalizeApplyTextNormalization(mode) {
|
|
336
|
-
const trimmed = mode?.trim();
|
|
337
|
-
if (!trimmed)
|
|
338
|
-
return undefined;
|
|
339
|
-
const normalized = trimmed.toLowerCase();
|
|
340
|
-
if (normalized === "auto" || normalized === "on" || normalized === "off")
|
|
341
|
-
return normalized;
|
|
342
|
-
throw new Error("applyTextNormalization must be one of: auto, on, off");
|
|
343
|
-
}
|
|
344
|
-
function normalizeSeed(seed) {
|
|
345
|
-
if (seed == null)
|
|
346
|
-
return undefined;
|
|
347
|
-
const next = Math.floor(seed);
|
|
348
|
-
if (!Number.isFinite(next) || next < 0 || next > 4_294_967_295) {
|
|
349
|
-
throw new Error("seed must be between 0 and 4294967295");
|
|
350
|
-
}
|
|
351
|
-
return next;
|
|
352
|
-
}
|
|
353
|
-
function parseBooleanValue(value) {
|
|
354
|
-
const normalized = value.trim().toLowerCase();
|
|
355
|
-
if (["true", "1", "yes", "on"].includes(normalized))
|
|
356
|
-
return true;
|
|
357
|
-
if (["false", "0", "no", "off"].includes(normalized))
|
|
358
|
-
return false;
|
|
359
|
-
return undefined;
|
|
360
|
-
}
|
|
361
|
-
function parseNumberValue(value) {
|
|
362
|
-
const parsed = Number.parseFloat(value);
|
|
363
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
364
|
-
}
|
|
365
|
-
function parseTtsDirectives(text, policy) {
|
|
366
|
-
if (!policy.enabled) {
|
|
367
|
-
return { cleanedText: text, overrides: {}, warnings: [], hasDirective: false };
|
|
368
|
-
}
|
|
369
|
-
const overrides = {};
|
|
370
|
-
const warnings = [];
|
|
371
|
-
let cleanedText = text;
|
|
372
|
-
let hasDirective = false;
|
|
373
|
-
const blockRegex = /\[\[tts:text\]\]([\s\S]*?)\[\[\/tts:text\]\]/gi;
|
|
374
|
-
cleanedText = cleanedText.replace(blockRegex, (_match, inner) => {
|
|
375
|
-
hasDirective = true;
|
|
376
|
-
if (policy.allowText && overrides.ttsText == null) {
|
|
377
|
-
overrides.ttsText = inner.trim();
|
|
378
|
-
}
|
|
379
|
-
return "";
|
|
380
|
-
});
|
|
381
|
-
const directiveRegex = /\[\[tts:([^\]]+)\]\]/gi;
|
|
382
|
-
cleanedText = cleanedText.replace(directiveRegex, (_match, body) => {
|
|
383
|
-
hasDirective = true;
|
|
384
|
-
const tokens = body.split(/\s+/).filter(Boolean);
|
|
385
|
-
for (const token of tokens) {
|
|
386
|
-
const eqIndex = token.indexOf("=");
|
|
387
|
-
if (eqIndex === -1)
|
|
388
|
-
continue;
|
|
389
|
-
const rawKey = token.slice(0, eqIndex).trim();
|
|
390
|
-
const rawValue = token.slice(eqIndex + 1).trim();
|
|
391
|
-
if (!rawKey || !rawValue)
|
|
392
|
-
continue;
|
|
393
|
-
const key = rawKey.toLowerCase();
|
|
394
|
-
try {
|
|
395
|
-
switch (key) {
|
|
396
|
-
case "provider":
|
|
397
|
-
if (!policy.allowProvider)
|
|
398
|
-
break;
|
|
399
|
-
if (rawValue === "openai" || rawValue === "elevenlabs" || rawValue === "edge") {
|
|
400
|
-
overrides.provider = rawValue;
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
warnings.push(`unsupported provider "${rawValue}"`);
|
|
404
|
-
}
|
|
405
|
-
break;
|
|
406
|
-
case "voice":
|
|
407
|
-
case "openai_voice":
|
|
408
|
-
case "openaivoice":
|
|
409
|
-
if (!policy.allowVoice)
|
|
410
|
-
break;
|
|
411
|
-
if (isValidOpenAIVoice(rawValue)) {
|
|
412
|
-
overrides.openai = { ...overrides.openai, voice: rawValue };
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
warnings.push(`invalid OpenAI voice "${rawValue}"`);
|
|
416
|
-
}
|
|
417
|
-
break;
|
|
418
|
-
case "voiceid":
|
|
419
|
-
case "voice_id":
|
|
420
|
-
case "elevenlabs_voice":
|
|
421
|
-
case "elevenlabsvoice":
|
|
422
|
-
if (!policy.allowVoice)
|
|
423
|
-
break;
|
|
424
|
-
if (isValidVoiceId(rawValue)) {
|
|
425
|
-
overrides.elevenlabs = { ...overrides.elevenlabs, voiceId: rawValue };
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
warnings.push(`invalid ElevenLabs voiceId "${rawValue}"`);
|
|
429
|
-
}
|
|
430
|
-
break;
|
|
431
|
-
case "model":
|
|
432
|
-
case "modelid":
|
|
433
|
-
case "model_id":
|
|
434
|
-
case "elevenlabs_model":
|
|
435
|
-
case "elevenlabsmodel":
|
|
436
|
-
case "openai_model":
|
|
437
|
-
case "openaimodel":
|
|
438
|
-
if (!policy.allowModelId)
|
|
439
|
-
break;
|
|
440
|
-
if (isValidOpenAIModel(rawValue)) {
|
|
441
|
-
overrides.openai = { ...overrides.openai, model: rawValue };
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
overrides.elevenlabs = { ...overrides.elevenlabs, modelId: rawValue };
|
|
445
|
-
}
|
|
446
|
-
break;
|
|
447
|
-
case "stability":
|
|
448
|
-
if (!policy.allowVoiceSettings)
|
|
449
|
-
break;
|
|
450
|
-
{
|
|
451
|
-
const value = parseNumberValue(rawValue);
|
|
452
|
-
if (value == null) {
|
|
453
|
-
warnings.push("invalid stability value");
|
|
454
|
-
break;
|
|
455
|
-
}
|
|
456
|
-
requireInRange(value, 0, 1, "stability");
|
|
457
|
-
overrides.elevenlabs = {
|
|
458
|
-
...overrides.elevenlabs,
|
|
459
|
-
voiceSettings: { ...overrides.elevenlabs?.voiceSettings, stability: value },
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
break;
|
|
463
|
-
case "similarity":
|
|
464
|
-
case "similarityboost":
|
|
465
|
-
case "similarity_boost":
|
|
466
|
-
if (!policy.allowVoiceSettings)
|
|
467
|
-
break;
|
|
468
|
-
{
|
|
469
|
-
const value = parseNumberValue(rawValue);
|
|
470
|
-
if (value == null) {
|
|
471
|
-
warnings.push("invalid similarityBoost value");
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
|
-
requireInRange(value, 0, 1, "similarityBoost");
|
|
475
|
-
overrides.elevenlabs = {
|
|
476
|
-
...overrides.elevenlabs,
|
|
477
|
-
voiceSettings: { ...overrides.elevenlabs?.voiceSettings, similarityBoost: value },
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
break;
|
|
481
|
-
case "style":
|
|
482
|
-
if (!policy.allowVoiceSettings)
|
|
483
|
-
break;
|
|
484
|
-
{
|
|
485
|
-
const value = parseNumberValue(rawValue);
|
|
486
|
-
if (value == null) {
|
|
487
|
-
warnings.push("invalid style value");
|
|
488
|
-
break;
|
|
489
|
-
}
|
|
490
|
-
requireInRange(value, 0, 1, "style");
|
|
491
|
-
overrides.elevenlabs = {
|
|
492
|
-
...overrides.elevenlabs,
|
|
493
|
-
voiceSettings: { ...overrides.elevenlabs?.voiceSettings, style: value },
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
break;
|
|
497
|
-
case "speed":
|
|
498
|
-
if (!policy.allowVoiceSettings)
|
|
499
|
-
break;
|
|
500
|
-
{
|
|
501
|
-
const value = parseNumberValue(rawValue);
|
|
502
|
-
if (value == null) {
|
|
503
|
-
warnings.push("invalid speed value");
|
|
504
|
-
break;
|
|
505
|
-
}
|
|
506
|
-
requireInRange(value, 0.5, 2, "speed");
|
|
507
|
-
overrides.elevenlabs = {
|
|
508
|
-
...overrides.elevenlabs,
|
|
509
|
-
voiceSettings: { ...overrides.elevenlabs?.voiceSettings, speed: value },
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
break;
|
|
513
|
-
case "speakerboost":
|
|
514
|
-
case "speaker_boost":
|
|
515
|
-
case "usespeakerboost":
|
|
516
|
-
case "use_speaker_boost":
|
|
517
|
-
if (!policy.allowVoiceSettings)
|
|
518
|
-
break;
|
|
519
|
-
{
|
|
520
|
-
const value = parseBooleanValue(rawValue);
|
|
521
|
-
if (value == null) {
|
|
522
|
-
warnings.push("invalid useSpeakerBoost value");
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
overrides.elevenlabs = {
|
|
526
|
-
...overrides.elevenlabs,
|
|
527
|
-
voiceSettings: { ...overrides.elevenlabs?.voiceSettings, useSpeakerBoost: value },
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
break;
|
|
531
|
-
case "normalize":
|
|
532
|
-
case "applytextnormalization":
|
|
533
|
-
case "apply_text_normalization":
|
|
534
|
-
if (!policy.allowNormalization)
|
|
535
|
-
break;
|
|
536
|
-
overrides.elevenlabs = {
|
|
537
|
-
...overrides.elevenlabs,
|
|
538
|
-
applyTextNormalization: normalizeApplyTextNormalization(rawValue),
|
|
539
|
-
};
|
|
540
|
-
break;
|
|
541
|
-
case "language":
|
|
542
|
-
case "languagecode":
|
|
543
|
-
case "language_code":
|
|
544
|
-
if (!policy.allowNormalization)
|
|
545
|
-
break;
|
|
546
|
-
overrides.elevenlabs = {
|
|
547
|
-
...overrides.elevenlabs,
|
|
548
|
-
languageCode: normalizeLanguageCode(rawValue),
|
|
549
|
-
};
|
|
550
|
-
break;
|
|
551
|
-
case "seed":
|
|
552
|
-
if (!policy.allowSeed)
|
|
553
|
-
break;
|
|
554
|
-
overrides.elevenlabs = {
|
|
555
|
-
...overrides.elevenlabs,
|
|
556
|
-
seed: normalizeSeed(Number.parseInt(rawValue, 10)),
|
|
557
|
-
};
|
|
558
|
-
break;
|
|
559
|
-
default:
|
|
560
|
-
break;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
catch (err) {
|
|
564
|
-
warnings.push(err.message);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
return "";
|
|
568
|
-
});
|
|
569
|
-
return {
|
|
570
|
-
cleanedText,
|
|
571
|
-
ttsText: overrides.ttsText,
|
|
572
|
-
hasDirective,
|
|
573
|
-
overrides,
|
|
574
|
-
warnings,
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
export const OPENAI_TTS_MODELS = ["gpt-4o-mini-tts", "tts-1", "tts-1-hd"];
|
|
578
|
-
/**
|
|
579
|
-
* Custom OpenAI-compatible TTS endpoint.
|
|
580
|
-
* When set, model/voice validation is relaxed to allow non-OpenAI models.
|
|
581
|
-
* Example: OPENAI_TTS_BASE_URL=http://localhost:8880/v1
|
|
582
|
-
*
|
|
583
|
-
* Note: Read at runtime (not module load) to support config.env loading.
|
|
584
|
-
*/
|
|
585
|
-
function getOpenAITtsBaseUrl() {
|
|
586
|
-
return (process.env.OPENAI_TTS_BASE_URL?.trim() || "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
587
|
-
}
|
|
588
|
-
function isCustomOpenAIEndpoint() {
|
|
589
|
-
return getOpenAITtsBaseUrl() !== "https://api.openai.com/v1";
|
|
590
|
-
}
|
|
591
|
-
export const OPENAI_TTS_VOICES = [
|
|
592
|
-
"alloy",
|
|
593
|
-
"ash",
|
|
594
|
-
"coral",
|
|
595
|
-
"echo",
|
|
596
|
-
"fable",
|
|
597
|
-
"onyx",
|
|
598
|
-
"nova",
|
|
599
|
-
"sage",
|
|
600
|
-
"shimmer",
|
|
601
|
-
];
|
|
602
|
-
function isValidOpenAIModel(model) {
|
|
603
|
-
// Allow any model when using custom endpoint (e.g., Kokoro, LocalAI)
|
|
604
|
-
if (isCustomOpenAIEndpoint())
|
|
605
|
-
return true;
|
|
606
|
-
return OPENAI_TTS_MODELS.includes(model);
|
|
607
|
-
}
|
|
608
|
-
function isValidOpenAIVoice(voice) {
|
|
609
|
-
// Allow any voice when using custom endpoint (e.g., Kokoro Chinese voices)
|
|
610
|
-
if (isCustomOpenAIEndpoint())
|
|
611
|
-
return true;
|
|
612
|
-
return OPENAI_TTS_VOICES.includes(voice);
|
|
613
|
-
}
|
|
614
|
-
function resolveSummaryModelRef(cfg, config) {
|
|
615
|
-
const defaultRef = resolveDefaultModelForAgent({ cfg });
|
|
616
|
-
const override = config.summaryModel?.trim();
|
|
617
|
-
if (!override)
|
|
618
|
-
return { ref: defaultRef, source: "default" };
|
|
619
|
-
const aliasIndex = buildModelAliasIndex({ cfg, defaultProvider: defaultRef.provider });
|
|
620
|
-
const resolved = resolveModelRefFromString({
|
|
621
|
-
raw: override,
|
|
622
|
-
defaultProvider: defaultRef.provider,
|
|
623
|
-
aliasIndex,
|
|
624
|
-
});
|
|
625
|
-
if (!resolved)
|
|
626
|
-
return { ref: defaultRef, source: "default" };
|
|
627
|
-
return { ref: resolved.ref, source: "summaryModel" };
|
|
628
|
-
}
|
|
629
|
-
function isTextContentBlock(block) {
|
|
630
|
-
return block.type === "text";
|
|
631
|
-
}
|
|
632
|
-
async function summarizeText(params) {
|
|
633
|
-
const { text, targetLength, cfg, config, timeoutMs } = params;
|
|
634
|
-
if (targetLength < 100 || targetLength > 10_000) {
|
|
635
|
-
throw new Error(`Invalid targetLength: ${targetLength}`);
|
|
636
|
-
}
|
|
637
|
-
const startTime = Date.now();
|
|
638
|
-
const { ref } = resolveSummaryModelRef(cfg, config);
|
|
639
|
-
const resolved = resolveModel(ref.provider, ref.model, undefined, cfg);
|
|
640
|
-
if (!resolved.model) {
|
|
641
|
-
throw new Error(resolved.error ?? `Unknown summary model: ${ref.provider}/${ref.model}`);
|
|
642
|
-
}
|
|
643
|
-
const apiKey = requireApiKey(await getApiKeyForModel({ model: resolved.model, cfg }), ref.provider);
|
|
644
|
-
try {
|
|
645
|
-
const controller = new AbortController();
|
|
646
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
647
|
-
try {
|
|
648
|
-
const res = await completeSimple(resolved.model, {
|
|
649
|
-
messages: [
|
|
650
|
-
{
|
|
651
|
-
role: "user",
|
|
652
|
-
content: `You are an assistant that summarizes texts concisely while keeping the most important information. ` +
|
|
653
|
-
`Summarize the text to approximately ${targetLength} characters. Maintain the original tone and style. ` +
|
|
654
|
-
`Reply only with the summary, without additional explanations.\n\n` +
|
|
655
|
-
`<text_to_summarize>\n${text}\n</text_to_summarize>`,
|
|
656
|
-
timestamp: Date.now(),
|
|
657
|
-
},
|
|
658
|
-
],
|
|
659
|
-
}, {
|
|
660
|
-
apiKey,
|
|
661
|
-
maxTokens: Math.ceil(targetLength / 2),
|
|
662
|
-
temperature: 0.3,
|
|
663
|
-
signal: controller.signal,
|
|
664
|
-
});
|
|
665
|
-
const summary = res.content
|
|
666
|
-
.filter(isTextContentBlock)
|
|
667
|
-
.map((block) => block.text.trim())
|
|
668
|
-
.filter(Boolean)
|
|
669
|
-
.join(" ")
|
|
670
|
-
.trim();
|
|
671
|
-
if (!summary) {
|
|
672
|
-
throw new Error("No summary returned");
|
|
673
|
-
}
|
|
674
|
-
return {
|
|
675
|
-
summary,
|
|
676
|
-
latencyMs: Date.now() - startTime,
|
|
677
|
-
inputLength: text.length,
|
|
678
|
-
outputLength: summary.length,
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
finally {
|
|
682
|
-
clearTimeout(timeout);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
catch (err) {
|
|
686
|
-
const error = err;
|
|
687
|
-
if (error.name === "AbortError") {
|
|
688
|
-
throw new Error("Summarization timed out", { cause: err });
|
|
689
|
-
}
|
|
690
|
-
throw err;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
function scheduleCleanup(tempDir, delayMs = TEMP_FILE_CLEANUP_DELAY_MS) {
|
|
694
|
-
const timer = setTimeout(() => {
|
|
695
|
-
try {
|
|
696
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
697
|
-
}
|
|
698
|
-
catch {
|
|
699
|
-
// ignore cleanup errors
|
|
700
|
-
}
|
|
701
|
-
}, delayMs);
|
|
702
|
-
timer.unref();
|
|
703
|
-
}
|
|
704
|
-
async function elevenLabsTTS(params) {
|
|
705
|
-
const { text, apiKey, baseUrl, voiceId, modelId, outputFormat, seed, applyTextNormalization, languageCode, voiceSettings, timeoutMs, } = params;
|
|
706
|
-
if (!isValidVoiceId(voiceId)) {
|
|
707
|
-
throw new Error("Invalid voiceId format");
|
|
708
|
-
}
|
|
709
|
-
assertElevenLabsVoiceSettings(voiceSettings);
|
|
710
|
-
const normalizedLanguage = normalizeLanguageCode(languageCode);
|
|
711
|
-
const normalizedNormalization = normalizeApplyTextNormalization(applyTextNormalization);
|
|
712
|
-
const normalizedSeed = normalizeSeed(seed);
|
|
713
|
-
const controller = new AbortController();
|
|
714
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
715
|
-
try {
|
|
716
|
-
const url = new URL(`${normalizeElevenLabsBaseUrl(baseUrl)}/v1/text-to-speech/${voiceId}`);
|
|
717
|
-
if (outputFormat) {
|
|
718
|
-
url.searchParams.set("output_format", outputFormat);
|
|
719
|
-
}
|
|
720
|
-
const response = await fetch(url.toString(), {
|
|
721
|
-
method: "POST",
|
|
722
|
-
headers: {
|
|
723
|
-
"xi-api-key": apiKey,
|
|
724
|
-
"Content-Type": "application/json",
|
|
725
|
-
Accept: "audio/mpeg",
|
|
726
|
-
},
|
|
727
|
-
body: JSON.stringify({
|
|
728
|
-
text,
|
|
729
|
-
model_id: modelId,
|
|
730
|
-
seed: normalizedSeed,
|
|
731
|
-
apply_text_normalization: normalizedNormalization,
|
|
732
|
-
language_code: normalizedLanguage,
|
|
733
|
-
voice_settings: {
|
|
734
|
-
stability: voiceSettings.stability,
|
|
735
|
-
similarity_boost: voiceSettings.similarityBoost,
|
|
736
|
-
style: voiceSettings.style,
|
|
737
|
-
use_speaker_boost: voiceSettings.useSpeakerBoost,
|
|
738
|
-
speed: voiceSettings.speed,
|
|
739
|
-
},
|
|
740
|
-
}),
|
|
741
|
-
signal: controller.signal,
|
|
742
|
-
});
|
|
743
|
-
if (!response.ok) {
|
|
744
|
-
throw new Error(`ElevenLabs API error (${response.status})`);
|
|
745
|
-
}
|
|
746
|
-
return Buffer.from(await response.arrayBuffer());
|
|
747
|
-
}
|
|
748
|
-
finally {
|
|
749
|
-
clearTimeout(timeout);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
async function openaiTTS(params) {
|
|
753
|
-
const { text, apiKey, model, voice, responseFormat, timeoutMs } = params;
|
|
754
|
-
if (!isValidOpenAIModel(model)) {
|
|
755
|
-
throw new Error(`Invalid model: ${model}`);
|
|
756
|
-
}
|
|
757
|
-
if (!isValidOpenAIVoice(voice)) {
|
|
758
|
-
throw new Error(`Invalid voice: ${voice}`);
|
|
759
|
-
}
|
|
760
|
-
const controller = new AbortController();
|
|
761
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
762
|
-
try {
|
|
763
|
-
const response = await fetch(`${getOpenAITtsBaseUrl()}/audio/speech`, {
|
|
764
|
-
method: "POST",
|
|
765
|
-
headers: {
|
|
766
|
-
Authorization: `Bearer ${apiKey}`,
|
|
767
|
-
"Content-Type": "application/json",
|
|
768
|
-
},
|
|
769
|
-
body: JSON.stringify({
|
|
770
|
-
model,
|
|
771
|
-
input: text,
|
|
772
|
-
voice,
|
|
773
|
-
response_format: responseFormat,
|
|
774
|
-
}),
|
|
775
|
-
signal: controller.signal,
|
|
776
|
-
});
|
|
777
|
-
if (!response.ok) {
|
|
778
|
-
throw new Error(`OpenAI TTS API error (${response.status})`);
|
|
779
|
-
}
|
|
780
|
-
return Buffer.from(await response.arrayBuffer());
|
|
781
|
-
}
|
|
782
|
-
finally {
|
|
783
|
-
clearTimeout(timeout);
|
|
784
315
|
}
|
|
316
|
+
return Boolean(resolveTtsApiKey(config, provider));
|
|
785
317
|
}
|
|
786
|
-
function
|
|
787
|
-
const
|
|
788
|
-
if (
|
|
789
|
-
return
|
|
790
|
-
if (normalized.includes("ogg"))
|
|
791
|
-
return ".ogg";
|
|
792
|
-
if (normalized.includes("opus"))
|
|
793
|
-
return ".opus";
|
|
794
|
-
if (normalized.includes("wav") || normalized.includes("riff") || normalized.includes("pcm")) {
|
|
795
|
-
return ".wav";
|
|
318
|
+
function formatTtsProviderError(provider, err) {
|
|
319
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
320
|
+
if (error.name === "AbortError") {
|
|
321
|
+
return `${provider}: request timed out`;
|
|
796
322
|
}
|
|
797
|
-
return
|
|
798
|
-
}
|
|
799
|
-
async function edgeTTS(params) {
|
|
800
|
-
const { text, outputPath, config, timeoutMs } = params;
|
|
801
|
-
const tts = new EdgeTTS({
|
|
802
|
-
voice: config.voice,
|
|
803
|
-
lang: config.lang,
|
|
804
|
-
outputFormat: config.outputFormat,
|
|
805
|
-
saveSubtitles: config.saveSubtitles,
|
|
806
|
-
proxy: config.proxy,
|
|
807
|
-
rate: config.rate,
|
|
808
|
-
pitch: config.pitch,
|
|
809
|
-
volume: config.volume,
|
|
810
|
-
timeout: config.timeoutMs ?? timeoutMs,
|
|
811
|
-
});
|
|
812
|
-
await tts.ttsPromise(text, outputPath);
|
|
323
|
+
return `${provider}: ${error.message}`;
|
|
813
324
|
}
|
|
814
325
|
export async function textToSpeech(params) {
|
|
815
326
|
const config = resolveTtsConfig(params.cfg);
|
|
@@ -826,16 +337,18 @@ export async function textToSpeech(params) {
|
|
|
826
337
|
const overrideProvider = params.overrides?.provider;
|
|
827
338
|
const provider = overrideProvider ?? userProvider;
|
|
828
339
|
const providers = resolveTtsProviderOrder(provider);
|
|
829
|
-
|
|
340
|
+
const errors = [];
|
|
830
341
|
for (const provider of providers) {
|
|
831
342
|
const providerStart = Date.now();
|
|
832
343
|
try {
|
|
833
344
|
if (provider === "edge") {
|
|
834
345
|
if (!config.edge.enabled) {
|
|
835
|
-
|
|
346
|
+
errors.push("edge: disabled");
|
|
836
347
|
continue;
|
|
837
348
|
}
|
|
838
|
-
const
|
|
349
|
+
const tempRoot = resolvePreferredPoolbotTmpDir();
|
|
350
|
+
mkdirSync(tempRoot, { recursive: true, mode: 0o700 });
|
|
351
|
+
const tempDir = mkdtempSync(path.join(tempRoot, "tts-"));
|
|
839
352
|
let edgeOutputFormat = resolveEdgeOutputFormat(config);
|
|
840
353
|
const fallbackEdgeOutputFormat = edgeOutputFormat !== DEFAULT_EDGE_OUTPUT_FORMAT ? DEFAULT_EDGE_OUTPUT_FORMAT : undefined;
|
|
841
354
|
const attemptEdgeTts = async (outputFormat) => {
|
|
@@ -896,7 +409,7 @@ export async function textToSpeech(params) {
|
|
|
896
409
|
}
|
|
897
410
|
const apiKey = resolveTtsApiKey(config, provider);
|
|
898
411
|
if (!apiKey) {
|
|
899
|
-
|
|
412
|
+
errors.push(`${provider}: no API key`);
|
|
900
413
|
continue;
|
|
901
414
|
}
|
|
902
415
|
let audioBuffer;
|
|
@@ -937,7 +450,9 @@ export async function textToSpeech(params) {
|
|
|
937
450
|
});
|
|
938
451
|
}
|
|
939
452
|
const latencyMs = Date.now() - providerStart;
|
|
940
|
-
const
|
|
453
|
+
const tempRoot = resolvePreferredPoolbotTmpDir();
|
|
454
|
+
mkdirSync(tempRoot, { recursive: true, mode: 0o700 });
|
|
455
|
+
const tempDir = mkdtempSync(path.join(tempRoot, "tts-"));
|
|
941
456
|
const audioPath = path.join(tempDir, `voice-${Date.now()}${output.extension}`);
|
|
942
457
|
writeFileSync(audioPath, audioBuffer);
|
|
943
458
|
scheduleCleanup(tempDir);
|
|
@@ -951,18 +466,12 @@ export async function textToSpeech(params) {
|
|
|
951
466
|
};
|
|
952
467
|
}
|
|
953
468
|
catch (err) {
|
|
954
|
-
|
|
955
|
-
if (error.name === "AbortError") {
|
|
956
|
-
lastError = `${provider}: request timed out`;
|
|
957
|
-
}
|
|
958
|
-
else {
|
|
959
|
-
lastError = `${provider}: ${error.message}`;
|
|
960
|
-
}
|
|
469
|
+
errors.push(formatTtsProviderError(provider, err));
|
|
961
470
|
}
|
|
962
471
|
}
|
|
963
472
|
return {
|
|
964
473
|
success: false,
|
|
965
|
-
error: `TTS conversion failed: ${
|
|
474
|
+
error: `TTS conversion failed: ${errors.join("; ") || "no providers available"}`,
|
|
966
475
|
};
|
|
967
476
|
}
|
|
968
477
|
export async function textToSpeechTelephony(params) {
|
|
@@ -976,17 +485,17 @@ export async function textToSpeechTelephony(params) {
|
|
|
976
485
|
}
|
|
977
486
|
const userProvider = getTtsProvider(config, prefsPath);
|
|
978
487
|
const providers = resolveTtsProviderOrder(userProvider);
|
|
979
|
-
|
|
488
|
+
const errors = [];
|
|
980
489
|
for (const provider of providers) {
|
|
981
490
|
const providerStart = Date.now();
|
|
982
491
|
try {
|
|
983
492
|
if (provider === "edge") {
|
|
984
|
-
|
|
493
|
+
errors.push("edge: unsupported for telephony");
|
|
985
494
|
continue;
|
|
986
495
|
}
|
|
987
496
|
const apiKey = resolveTtsApiKey(config, provider);
|
|
988
497
|
if (!apiKey) {
|
|
989
|
-
|
|
498
|
+
errors.push(`${provider}: no API key`);
|
|
990
499
|
continue;
|
|
991
500
|
}
|
|
992
501
|
if (provider === "elevenlabs") {
|
|
@@ -1032,18 +541,12 @@ export async function textToSpeechTelephony(params) {
|
|
|
1032
541
|
};
|
|
1033
542
|
}
|
|
1034
543
|
catch (err) {
|
|
1035
|
-
|
|
1036
|
-
if (error.name === "AbortError") {
|
|
1037
|
-
lastError = `${provider}: request timed out`;
|
|
1038
|
-
}
|
|
1039
|
-
else {
|
|
1040
|
-
lastError = `${provider}: ${error.message}`;
|
|
1041
|
-
}
|
|
544
|
+
errors.push(formatTtsProviderError(provider, err));
|
|
1042
545
|
}
|
|
1043
546
|
}
|
|
1044
547
|
return {
|
|
1045
548
|
success: false,
|
|
1046
|
-
error: `TTS conversion failed: ${
|
|
549
|
+
error: `TTS conversion failed: ${errors.join("; ") || "no providers available"}`,
|
|
1047
550
|
};
|
|
1048
551
|
}
|
|
1049
552
|
export async function maybeApplyTtsToPayload(params) {
|
|
@@ -1054,8 +557,9 @@ export async function maybeApplyTtsToPayload(params) {
|
|
|
1054
557
|
prefsPath,
|
|
1055
558
|
sessionAuto: params.ttsAuto,
|
|
1056
559
|
});
|
|
1057
|
-
if (autoMode === "off")
|
|
560
|
+
if (autoMode === "off") {
|
|
1058
561
|
return params.payload;
|
|
562
|
+
}
|
|
1059
563
|
const text = params.payload.text ?? "";
|
|
1060
564
|
const directives = parseTtsDirectives(text, config.modelOverrides);
|
|
1061
565
|
if (directives.warnings.length > 0) {
|
|
@@ -1071,32 +575,37 @@ export async function maybeApplyTtsToPayload(params) {
|
|
|
1071
575
|
...params.payload,
|
|
1072
576
|
text: visibleText.length > 0 ? visibleText : undefined,
|
|
1073
577
|
};
|
|
1074
|
-
if (autoMode === "tagged" && !directives.hasDirective)
|
|
578
|
+
if (autoMode === "tagged" && !directives.hasDirective) {
|
|
1075
579
|
return nextPayload;
|
|
1076
|
-
|
|
580
|
+
}
|
|
581
|
+
if (autoMode === "inbound" && params.inboundAudio !== true) {
|
|
1077
582
|
return nextPayload;
|
|
583
|
+
}
|
|
1078
584
|
const mode = config.mode ?? "final";
|
|
1079
|
-
if (mode === "final" && params.kind && params.kind !== "final")
|
|
585
|
+
if (mode === "final" && params.kind && params.kind !== "final") {
|
|
1080
586
|
return nextPayload;
|
|
1081
|
-
|
|
587
|
+
}
|
|
588
|
+
if (!ttsText.trim()) {
|
|
1082
589
|
return nextPayload;
|
|
1083
|
-
|
|
590
|
+
}
|
|
591
|
+
if (params.payload.mediaUrl || (params.payload.mediaUrls?.length ?? 0) > 0) {
|
|
1084
592
|
return nextPayload;
|
|
1085
|
-
|
|
593
|
+
}
|
|
594
|
+
if (text.includes("MEDIA:")) {
|
|
1086
595
|
return nextPayload;
|
|
1087
|
-
|
|
596
|
+
}
|
|
597
|
+
if (ttsText.trim().length < 10) {
|
|
1088
598
|
return nextPayload;
|
|
599
|
+
}
|
|
1089
600
|
const maxLength = getTtsMaxLength(prefsPath);
|
|
1090
601
|
let textForAudio = ttsText.trim();
|
|
1091
602
|
let wasSummarized = false;
|
|
1092
603
|
if (textForAudio.length > maxLength) {
|
|
1093
604
|
if (!isSummarizationEnabled(prefsPath)) {
|
|
1094
|
-
// Truncate text when summarization is disabled
|
|
1095
605
|
logVerbose(`TTS: truncating long text (${textForAudio.length} > ${maxLength}), summarization disabled.`);
|
|
1096
606
|
textForAudio = `${textForAudio.slice(0, maxLength - 3)}...`;
|
|
1097
607
|
}
|
|
1098
608
|
else {
|
|
1099
|
-
// Summarize text when enabled
|
|
1100
609
|
try {
|
|
1101
610
|
const summary = await summarizeText({
|
|
1102
611
|
text: textForAudio,
|
|
@@ -1119,6 +628,10 @@ export async function maybeApplyTtsToPayload(params) {
|
|
|
1119
628
|
}
|
|
1120
629
|
}
|
|
1121
630
|
}
|
|
631
|
+
textForAudio = stripMarkdown(textForAudio).trim(); // strip markdown for TTS (### → "hashtag" etc.)
|
|
632
|
+
if (textForAudio.length < 10) {
|
|
633
|
+
return nextPayload;
|
|
634
|
+
}
|
|
1122
635
|
const ttsStart = Date.now();
|
|
1123
636
|
const result = await textToSpeech({
|
|
1124
637
|
text: textForAudio,
|