@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
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { resolveIdentityNamePrefix } from "../../../agents/identity.js";
|
|
2
2
|
import { resolveChunkMode, resolveTextChunkLimit } from "../../../auto-reply/chunk.js";
|
|
3
|
+
import { shouldComputeCommandAuthorized } from "../../../auto-reply/command-detection.js";
|
|
3
4
|
import { formatInboundEnvelope, resolveEnvelopeFormatOptions, } from "../../../auto-reply/envelope.js";
|
|
4
5
|
import { buildHistoryContextFromEntries, } from "../../../auto-reply/reply/history.js";
|
|
5
|
-
import { dispatchReplyWithBufferedBlockDispatcher } from "../../../auto-reply/reply/provider-dispatcher.js";
|
|
6
|
-
import { shouldComputeCommandAuthorized } from "../../../auto-reply/command-detection.js";
|
|
7
6
|
import { finalizeInboundContext } from "../../../auto-reply/reply/inbound-context.js";
|
|
7
|
+
import { dispatchReplyWithBufferedBlockDispatcher } from "../../../auto-reply/reply/provider-dispatcher.js";
|
|
8
8
|
import { toLocationContext } from "../../../channels/location.js";
|
|
9
|
-
import {
|
|
10
|
-
import { readSessionUpdatedAt, recordSessionMetaFromInbound, resolveStorePath, } from "../../../config/sessions.js";
|
|
9
|
+
import { createReplyPrefixOptions } from "../../../channels/reply-prefix.js";
|
|
11
10
|
import { resolveMarkdownTableMode } from "../../../config/markdown-tables.js";
|
|
11
|
+
import { readSessionUpdatedAt, recordSessionMetaFromInbound, resolveStorePath, } from "../../../config/sessions.js";
|
|
12
12
|
import { logVerbose, shouldLogVerbose } from "../../../globals.js";
|
|
13
|
+
import { getAgentScopedMediaLocalRoots } from "../../../media/local-roots.js";
|
|
13
14
|
import { readChannelAllowFromStore } from "../../../pairing/pairing-store.js";
|
|
14
15
|
import { jidToE164, normalizeE164 } from "../../../utils.js";
|
|
16
|
+
import { resolveWhatsAppAccount } from "../../accounts.js";
|
|
15
17
|
import { newConnectionId } from "../../reconnect.js";
|
|
16
18
|
import { formatError } from "../../session.js";
|
|
17
19
|
import { deliverWebReply } from "../deliver-reply.js";
|
|
@@ -31,31 +33,39 @@ function normalizeAllowFromE164(values) {
|
|
|
31
33
|
}
|
|
32
34
|
async function resolveWhatsAppCommandAuthorized(params) {
|
|
33
35
|
const useAccessGroups = params.cfg.commands?.useAccessGroups !== false;
|
|
34
|
-
if (!useAccessGroups)
|
|
36
|
+
if (!useAccessGroups) {
|
|
35
37
|
return true;
|
|
38
|
+
}
|
|
36
39
|
const isGroup = params.msg.chatType === "group";
|
|
37
40
|
const senderE164 = normalizeE164(isGroup ? (params.msg.senderE164 ?? "") : (params.msg.senderE164 ?? params.msg.from ?? ""));
|
|
38
|
-
if (!senderE164)
|
|
41
|
+
if (!senderE164) {
|
|
39
42
|
return false;
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
+
}
|
|
44
|
+
const account = resolveWhatsAppAccount({ cfg: params.cfg, accountId: params.msg.accountId });
|
|
45
|
+
const dmPolicy = account.dmPolicy ?? "pairing";
|
|
46
|
+
const configuredAllowFrom = account.allowFrom ?? [];
|
|
47
|
+
const configuredGroupAllowFrom = account.groupAllowFrom ?? (configuredAllowFrom.length > 0 ? configuredAllowFrom : undefined);
|
|
43
48
|
if (isGroup) {
|
|
44
|
-
if (!configuredGroupAllowFrom || configuredGroupAllowFrom.length === 0)
|
|
49
|
+
if (!configuredGroupAllowFrom || configuredGroupAllowFrom.length === 0) {
|
|
45
50
|
return false;
|
|
46
|
-
|
|
51
|
+
}
|
|
52
|
+
if (configuredGroupAllowFrom.some((v) => String(v).trim() === "*")) {
|
|
47
53
|
return true;
|
|
54
|
+
}
|
|
48
55
|
return normalizeAllowFromE164(configuredGroupAllowFrom).includes(senderE164);
|
|
49
56
|
}
|
|
50
|
-
const storeAllowFrom =
|
|
57
|
+
const storeAllowFrom = dmPolicy === "allowlist"
|
|
58
|
+
? []
|
|
59
|
+
: await readChannelAllowFromStore("whatsapp", process.env, params.msg.accountId).catch(() => []);
|
|
51
60
|
const combinedAllowFrom = Array.from(new Set([...(configuredAllowFrom ?? []), ...storeAllowFrom]));
|
|
52
61
|
const allowFrom = combinedAllowFrom.length > 0
|
|
53
62
|
? combinedAllowFrom
|
|
54
63
|
: params.msg.selfE164
|
|
55
64
|
? [params.msg.selfE164]
|
|
56
65
|
: [];
|
|
57
|
-
if (allowFrom.some((v) => String(v).trim() === "*"))
|
|
66
|
+
if (allowFrom.some((v) => String(v).trim() === "*")) {
|
|
58
67
|
return true;
|
|
68
|
+
}
|
|
59
69
|
return normalizeAllowFromE164(allowFrom).includes(senderE164);
|
|
60
70
|
}
|
|
61
71
|
export async function processMessage(params) {
|
|
@@ -83,21 +93,17 @@ export async function processMessage(params) {
|
|
|
83
93
|
sender: m.sender,
|
|
84
94
|
body: m.body,
|
|
85
95
|
timestamp: m.timestamp,
|
|
86
|
-
messageId: m.id,
|
|
87
96
|
}));
|
|
88
97
|
combinedBody = buildHistoryContextFromEntries({
|
|
89
98
|
entries: historyEntries,
|
|
90
99
|
currentMessage: combinedBody,
|
|
91
100
|
excludeLast: false,
|
|
92
101
|
formatEntry: (entry) => {
|
|
93
|
-
const bodyWithId = entry.messageId
|
|
94
|
-
? `${entry.body}\n[message_id: ${entry.messageId}]`
|
|
95
|
-
: entry.body;
|
|
96
102
|
return formatInboundEnvelope({
|
|
97
103
|
channel: "WhatsApp",
|
|
98
104
|
from: conversationId,
|
|
99
105
|
timestamp: entry.timestamp,
|
|
100
|
-
body:
|
|
106
|
+
body: entry.body,
|
|
101
107
|
chatType: "group",
|
|
102
108
|
senderLabel: entry.sender,
|
|
103
109
|
envelope: envelopeOptions,
|
|
@@ -147,11 +153,13 @@ export async function processMessage(params) {
|
|
|
147
153
|
}
|
|
148
154
|
const dmRouteTarget = params.msg.chatType !== "group"
|
|
149
155
|
? (() => {
|
|
150
|
-
if (params.msg.senderE164)
|
|
156
|
+
if (params.msg.senderE164) {
|
|
151
157
|
return normalizeE164(params.msg.senderE164);
|
|
158
|
+
}
|
|
152
159
|
// In direct chats, `msg.from` is already the canonical conversation id.
|
|
153
|
-
if (params.msg.from.includes("@"))
|
|
160
|
+
if (params.msg.from.includes("@")) {
|
|
154
161
|
return jidToE164(params.msg.from);
|
|
162
|
+
}
|
|
155
163
|
return normalizeE164(params.msg.from);
|
|
156
164
|
})()
|
|
157
165
|
: undefined;
|
|
@@ -162,25 +170,37 @@ export async function processMessage(params) {
|
|
|
162
170
|
channel: "whatsapp",
|
|
163
171
|
accountId: params.route.accountId,
|
|
164
172
|
});
|
|
173
|
+
const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
|
|
165
174
|
let didLogHeartbeatStrip = false;
|
|
166
175
|
let didSendReply = false;
|
|
167
176
|
const commandAuthorized = shouldComputeCommandAuthorized(params.msg.body, params.cfg)
|
|
168
177
|
? await resolveWhatsAppCommandAuthorized({ cfg: params.cfg, msg: params.msg })
|
|
169
178
|
: undefined;
|
|
170
179
|
const configuredResponsePrefix = params.cfg.messages?.responsePrefix;
|
|
171
|
-
const
|
|
180
|
+
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
|
172
181
|
cfg: params.cfg,
|
|
173
182
|
agentId: params.route.agentId,
|
|
183
|
+
channel: "whatsapp",
|
|
184
|
+
accountId: params.route.accountId,
|
|
174
185
|
});
|
|
175
186
|
const isSelfChat = params.msg.chatType !== "group" &&
|
|
176
187
|
Boolean(params.msg.selfE164) &&
|
|
177
188
|
normalizeE164(params.msg.from) === normalizeE164(params.msg.selfE164 ?? "");
|
|
178
|
-
const responsePrefix =
|
|
189
|
+
const responsePrefix = prefixOptions.responsePrefix ??
|
|
179
190
|
(configuredResponsePrefix === undefined && isSelfChat
|
|
180
191
|
? (resolveIdentityNamePrefix(params.cfg, params.route.agentId) ?? "[poolbot]")
|
|
181
192
|
: undefined);
|
|
193
|
+
const inboundHistory = params.msg.chatType === "group"
|
|
194
|
+
? (params.groupHistory ?? params.groupHistories.get(params.groupHistoryKey) ?? []).map((entry) => ({
|
|
195
|
+
sender: entry.sender,
|
|
196
|
+
body: entry.body,
|
|
197
|
+
timestamp: entry.timestamp,
|
|
198
|
+
}))
|
|
199
|
+
: undefined;
|
|
182
200
|
const ctxPayload = finalizeInboundContext({
|
|
183
201
|
Body: combinedBody,
|
|
202
|
+
BodyForAgent: params.msg.body,
|
|
203
|
+
InboundHistory: inboundHistory,
|
|
184
204
|
RawBody: params.msg.body,
|
|
185
205
|
CommandBody: params.msg.body,
|
|
186
206
|
From: params.msg.from,
|
|
@@ -243,8 +263,8 @@ export async function processMessage(params) {
|
|
|
243
263
|
cfg: params.cfg,
|
|
244
264
|
replyResolver: params.replyResolver,
|
|
245
265
|
dispatcherOptions: {
|
|
266
|
+
...prefixOptions,
|
|
246
267
|
responsePrefix,
|
|
247
|
-
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
|
248
268
|
onHeartbeatStrip: () => {
|
|
249
269
|
if (!didLogHeartbeatStrip) {
|
|
250
270
|
didLogHeartbeatStrip = true;
|
|
@@ -255,6 +275,7 @@ export async function processMessage(params) {
|
|
|
255
275
|
await deliverWebReply({
|
|
256
276
|
replyResult: payload,
|
|
257
277
|
msg: params.msg,
|
|
278
|
+
mediaLocalRoots,
|
|
258
279
|
maxMediaBytes: params.maxMediaBytes,
|
|
259
280
|
textLimit,
|
|
260
281
|
chunkMode,
|
|
@@ -299,7 +320,7 @@ export async function processMessage(params) {
|
|
|
299
320
|
disableBlockStreaming: typeof params.cfg.channels?.whatsapp?.blockStreaming === "boolean"
|
|
300
321
|
? !params.cfg.channels.whatsapp.blockStreaming
|
|
301
322
|
: undefined,
|
|
302
|
-
onModelSelected
|
|
323
|
+
onModelSelected,
|
|
303
324
|
},
|
|
304
325
|
});
|
|
305
326
|
if (!queuedFinal) {
|
|
@@ -11,9 +11,11 @@ export async function checkInboundAccessControl(params) {
|
|
|
11
11
|
cfg,
|
|
12
12
|
accountId: params.accountId,
|
|
13
13
|
});
|
|
14
|
-
const dmPolicy =
|
|
14
|
+
const dmPolicy = account.dmPolicy ?? "pairing";
|
|
15
15
|
const configuredAllowFrom = account.allowFrom;
|
|
16
|
-
const storeAllowFrom =
|
|
16
|
+
const storeAllowFrom = dmPolicy === "allowlist"
|
|
17
|
+
? []
|
|
18
|
+
: await readChannelAllowFromStore("whatsapp", process.env, account.accountId).catch(() => []);
|
|
17
19
|
// Without user config, default to self-only DM access so the owner can talk to themselves.
|
|
18
20
|
const combinedAllowFrom = Array.from(new Set([...(configuredAllowFrom ?? []), ...storeAllowFrom]));
|
|
19
21
|
const defaultAllowFrom = combinedAllowFrom.length === 0 && params.selfE164 ? [params.selfE164] : undefined;
|
|
@@ -107,6 +109,7 @@ export async function checkInboundAccessControl(params) {
|
|
|
107
109
|
const { code, created } = await upsertChannelPairingRequest({
|
|
108
110
|
channel: "whatsapp",
|
|
109
111
|
id: candidate,
|
|
112
|
+
accountId: account.accountId,
|
|
110
113
|
meta: { name: (params.pushName ?? "").trim() || undefined },
|
|
111
114
|
});
|
|
112
115
|
if (created) {
|
package/dist/web/login-qr.js
CHANGED
|
@@ -34,20 +34,23 @@ function attachLoginWaiter(accountId, login) {
|
|
|
34
34
|
login.waitPromise = waitForWaConnection(login.sock)
|
|
35
35
|
.then(() => {
|
|
36
36
|
const current = activeLogins.get(accountId);
|
|
37
|
-
if (current?.id === login.id)
|
|
37
|
+
if (current?.id === login.id) {
|
|
38
38
|
current.connected = true;
|
|
39
|
+
}
|
|
39
40
|
})
|
|
40
41
|
.catch((err) => {
|
|
41
42
|
const current = activeLogins.get(accountId);
|
|
42
|
-
if (current?.id !== login.id)
|
|
43
|
+
if (current?.id !== login.id) {
|
|
43
44
|
return;
|
|
45
|
+
}
|
|
44
46
|
current.error = formatError(err);
|
|
45
47
|
current.errorStatus = getStatusCode(err);
|
|
46
48
|
});
|
|
47
49
|
}
|
|
48
50
|
async function restartLoginSocket(login, runtime) {
|
|
49
|
-
if (login.restartAttempted)
|
|
51
|
+
if (login.restartAttempted) {
|
|
50
52
|
return false;
|
|
53
|
+
}
|
|
51
54
|
login.restartAttempted = true;
|
|
52
55
|
runtime.log(info("WhatsApp asked for a restart after pairing (code 515); retrying connection once…"));
|
|
53
56
|
closeSocket(login.sock);
|
|
@@ -103,12 +106,14 @@ export async function startWebLoginWithQr(opts = {}) {
|
|
|
103
106
|
sock = await createWaSocket(false, Boolean(opts.verbose), {
|
|
104
107
|
authDir: account.authDir,
|
|
105
108
|
onQr: (qr) => {
|
|
106
|
-
if (pendingQr)
|
|
109
|
+
if (pendingQr) {
|
|
107
110
|
return;
|
|
111
|
+
}
|
|
108
112
|
pendingQr = qr;
|
|
109
113
|
const current = activeLogins.get(account.accountId);
|
|
110
|
-
if (current && !current.qr)
|
|
114
|
+
if (current && !current.qr) {
|
|
111
115
|
current.qr = qr;
|
|
116
|
+
}
|
|
112
117
|
clearTimeout(qrTimer);
|
|
113
118
|
runtime.log(info("WhatsApp QR received."));
|
|
114
119
|
resolveQr?.(qr);
|
|
@@ -135,8 +140,9 @@ export async function startWebLoginWithQr(opts = {}) {
|
|
|
135
140
|
verbose: Boolean(opts.verbose),
|
|
136
141
|
};
|
|
137
142
|
activeLogins.set(account.accountId, login);
|
|
138
|
-
if (pendingQr && !login.qr)
|
|
143
|
+
if (pendingQr && !login.qr) {
|
|
139
144
|
login.qr = pendingQr;
|
|
145
|
+
}
|
|
140
146
|
attachLoginWaiter(account.accountId, login);
|
|
141
147
|
let qr;
|
|
142
148
|
try {
|
package/dist/web/media.js
CHANGED
|
@@ -2,15 +2,71 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { logVerbose, shouldLogVerbose } from "../globals.js";
|
|
5
|
+
import { SafeOpenError, readLocalFileSafely } from "../infra/fs-safe.js";
|
|
5
6
|
import { maxBytesForKind, mediaKindFromMime } from "../media/constants.js";
|
|
6
|
-
import { resolveUserPath } from "../utils.js";
|
|
7
7
|
import { fetchRemoteMedia } from "../media/fetch.js";
|
|
8
|
-
import { getDefaultMediaLocalRoots } from "../media/local-roots.js";
|
|
9
8
|
import { convertHeicToJpeg, hasAlphaChannel, optimizeImageToPng, resizeToJpeg, } from "../media/image-ops.js";
|
|
9
|
+
import { getDefaultMediaLocalRoots } from "../media/local-roots.js";
|
|
10
10
|
import { detectMime, extensionForMime } from "../media/mime.js";
|
|
11
|
+
import { resolveUserPath } from "../utils.js";
|
|
12
|
+
export class LocalMediaAccessError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
constructor(code, message, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.name = "LocalMediaAccessError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
11
20
|
export function getDefaultLocalRoots() {
|
|
12
21
|
return getDefaultMediaLocalRoots();
|
|
13
22
|
}
|
|
23
|
+
async function assertLocalMediaAllowed(mediaPath, localRoots) {
|
|
24
|
+
if (localRoots === "any") {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const roots = localRoots ?? getDefaultLocalRoots();
|
|
28
|
+
// Resolve symlinks so a symlink under /tmp pointing to /etc/passwd is caught.
|
|
29
|
+
let resolved;
|
|
30
|
+
try {
|
|
31
|
+
resolved = await fs.realpath(mediaPath);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
resolved = path.resolve(mediaPath);
|
|
35
|
+
}
|
|
36
|
+
// Hardening: the default allowlist includes the Pool Bot temp dir, and tests/CI may
|
|
37
|
+
// override the state dir into tmp. Avoid accidentally allowing per-agent
|
|
38
|
+
// `workspace-*` state roots via the temp-root prefix match; require explicit
|
|
39
|
+
// localRoots for those.
|
|
40
|
+
if (localRoots === undefined) {
|
|
41
|
+
const workspaceRoot = roots.find((root) => path.basename(root) === "workspace");
|
|
42
|
+
if (workspaceRoot) {
|
|
43
|
+
const stateDir = path.dirname(workspaceRoot);
|
|
44
|
+
const rel = path.relative(stateDir, resolved);
|
|
45
|
+
if (rel && !rel.startsWith("..") && !path.isAbsolute(rel)) {
|
|
46
|
+
const firstSegment = rel.split(path.sep)[0] ?? "";
|
|
47
|
+
if (firstSegment.startsWith("workspace-")) {
|
|
48
|
+
throw new LocalMediaAccessError("path-not-allowed", `Local media path is not under an allowed directory: ${mediaPath}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const root of roots) {
|
|
54
|
+
let resolvedRoot;
|
|
55
|
+
try {
|
|
56
|
+
resolvedRoot = await fs.realpath(root);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
resolvedRoot = path.resolve(root);
|
|
60
|
+
}
|
|
61
|
+
if (resolvedRoot === path.parse(resolvedRoot).root) {
|
|
62
|
+
throw new LocalMediaAccessError("invalid-root", `Invalid localRoots entry (refuses filesystem root): ${root}. Pass a narrower directory.`);
|
|
63
|
+
}
|
|
64
|
+
if (resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path.sep)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
throw new LocalMediaAccessError("path-not-allowed", `Local media path is not under an allowed directory: ${mediaPath}`);
|
|
69
|
+
}
|
|
14
70
|
const HEIC_MIME_RE = /^image\/hei[cf]$/i;
|
|
15
71
|
const HEIC_EXT_RE = /\.(heic|heif)$/i;
|
|
16
72
|
const MB = 1024 * 1024;
|
|
@@ -24,18 +80,22 @@ function formatCapReduce(label, cap, size) {
|
|
|
24
80
|
return `${label} could not be reduced below ${formatMb(cap, 0)}MB (got ${formatMb(size)}MB)`;
|
|
25
81
|
}
|
|
26
82
|
function isHeicSource(opts) {
|
|
27
|
-
if (opts.contentType && HEIC_MIME_RE.test(opts.contentType.trim()))
|
|
83
|
+
if (opts.contentType && HEIC_MIME_RE.test(opts.contentType.trim())) {
|
|
28
84
|
return true;
|
|
29
|
-
|
|
85
|
+
}
|
|
86
|
+
if (opts.fileName && HEIC_EXT_RE.test(opts.fileName.trim())) {
|
|
30
87
|
return true;
|
|
88
|
+
}
|
|
31
89
|
return false;
|
|
32
90
|
}
|
|
33
91
|
function toJpegFileName(fileName) {
|
|
34
|
-
if (!fileName)
|
|
92
|
+
if (!fileName) {
|
|
35
93
|
return undefined;
|
|
94
|
+
}
|
|
36
95
|
const trimmed = fileName.trim();
|
|
37
|
-
if (!trimmed)
|
|
96
|
+
if (!trimmed) {
|
|
38
97
|
return fileName;
|
|
98
|
+
}
|
|
39
99
|
const parsed = path.parse(trimmed);
|
|
40
100
|
if (!parsed.ext || HEIC_EXT_RE.test(parsed.ext)) {
|
|
41
101
|
return path.format({ dir: parsed.dir, name: parsed.name || trimmed, ext: ".jpg" });
|
|
@@ -43,10 +103,12 @@ function toJpegFileName(fileName) {
|
|
|
43
103
|
return path.format({ dir: parsed.dir, name: parsed.name, ext: ".jpg" });
|
|
44
104
|
}
|
|
45
105
|
function logOptimizedImage(params) {
|
|
46
|
-
if (!shouldLogVerbose())
|
|
106
|
+
if (!shouldLogVerbose()) {
|
|
47
107
|
return;
|
|
48
|
-
|
|
108
|
+
}
|
|
109
|
+
if (params.optimized.optimizedSize >= params.originalSize) {
|
|
49
110
|
return;
|
|
111
|
+
}
|
|
50
112
|
if (params.optimized.format === "png") {
|
|
51
113
|
logVerbose(`Optimized PNG (preserving alpha) from ${formatMb(params.originalSize)}MB to ${formatMb(params.optimized.optimizedSize)}MB (side≤${params.optimized.resizeSide}px)`);
|
|
52
114
|
return;
|
|
@@ -70,14 +132,17 @@ async function optimizeImageWithFallback(params) {
|
|
|
70
132
|
return { ...optimized, format: "jpeg" };
|
|
71
133
|
}
|
|
72
134
|
async function loadWebMediaInternal(mediaUrl, options = {}) {
|
|
73
|
-
const { maxBytes, optimizeImages = true } = options;
|
|
135
|
+
const { maxBytes, optimizeImages = true, ssrfPolicy, localRoots, sandboxValidated = false, readFile: readFileOverride, } = options;
|
|
136
|
+
// Strip MEDIA: prefix used by agent tools (e.g. TTS) to tag media paths.
|
|
137
|
+
// Be lenient: LLM output may add extra whitespace (e.g. " MEDIA : /tmp/x.png").
|
|
138
|
+
mediaUrl = mediaUrl.replace(/^\s*MEDIA\s*:\s*/i, "");
|
|
74
139
|
// Use fileURLToPath for proper handling of file:// URLs (handles file://localhost/path, etc.)
|
|
75
140
|
if (mediaUrl.startsWith("file://")) {
|
|
76
141
|
try {
|
|
77
142
|
mediaUrl = fileURLToPath(mediaUrl);
|
|
78
143
|
}
|
|
79
144
|
catch {
|
|
80
|
-
throw new
|
|
145
|
+
throw new LocalMediaAccessError("invalid-file-url", `Invalid file:// URL: ${mediaUrl}`);
|
|
81
146
|
}
|
|
82
147
|
}
|
|
83
148
|
const optimizeAndClampImage = async (buffer, cap, meta) => {
|
|
@@ -133,7 +198,15 @@ async function loadWebMediaInternal(mediaUrl, options = {}) {
|
|
|
133
198
|
};
|
|
134
199
|
};
|
|
135
200
|
if (/^https?:\/\//i.test(mediaUrl)) {
|
|
136
|
-
|
|
201
|
+
// Enforce a download cap during fetch to avoid unbounded memory usage.
|
|
202
|
+
// For optimized images, allow fetching larger payloads before compression.
|
|
203
|
+
const defaultFetchCap = maxBytesForKind("unknown");
|
|
204
|
+
const fetchCap = maxBytes === undefined
|
|
205
|
+
? defaultFetchCap
|
|
206
|
+
: optimizeImages
|
|
207
|
+
? Math.max(maxBytes, defaultFetchCap)
|
|
208
|
+
: maxBytes;
|
|
209
|
+
const fetched = await fetchRemoteMedia({ url: mediaUrl, maxBytes: fetchCap, ssrfPolicy });
|
|
137
210
|
const { buffer, contentType, fileName } = fetched;
|
|
138
211
|
const kind = mediaKindFromMime(contentType);
|
|
139
212
|
return await clampAndFinalize({ buffer, contentType, kind, fileName });
|
|
@@ -142,15 +215,45 @@ async function loadWebMediaInternal(mediaUrl, options = {}) {
|
|
|
142
215
|
if (mediaUrl.startsWith("~")) {
|
|
143
216
|
mediaUrl = resolveUserPath(mediaUrl);
|
|
144
217
|
}
|
|
218
|
+
if ((sandboxValidated || localRoots === "any") && !readFileOverride) {
|
|
219
|
+
throw new LocalMediaAccessError("unsafe-bypass", "Refusing localRoots bypass without readFile override. Use sandboxValidated with readFile, or pass explicit localRoots.");
|
|
220
|
+
}
|
|
221
|
+
// Guard local reads against allowed directory roots to prevent file exfiltration.
|
|
222
|
+
if (!(sandboxValidated || localRoots === "any")) {
|
|
223
|
+
await assertLocalMediaAllowed(mediaUrl, localRoots);
|
|
224
|
+
}
|
|
145
225
|
// Local path
|
|
146
|
-
|
|
226
|
+
let data;
|
|
227
|
+
if (readFileOverride) {
|
|
228
|
+
data = await readFileOverride(mediaUrl);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
try {
|
|
232
|
+
data = (await readLocalFileSafely({ filePath: mediaUrl })).buffer;
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
if (err instanceof SafeOpenError) {
|
|
236
|
+
if (err.code === "not-found") {
|
|
237
|
+
throw new LocalMediaAccessError("not-found", `Local media file not found: ${mediaUrl}`, {
|
|
238
|
+
cause: err,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (err.code === "not-file") {
|
|
242
|
+
throw new LocalMediaAccessError("not-file", `Local media path is not a file: ${mediaUrl}`, { cause: err });
|
|
243
|
+
}
|
|
244
|
+
throw new LocalMediaAccessError("invalid-path", `Local media path is not safe to read: ${mediaUrl}`, { cause: err });
|
|
245
|
+
}
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
147
249
|
const mime = await detectMime({ buffer: data, filePath: mediaUrl });
|
|
148
250
|
const kind = mediaKindFromMime(mime);
|
|
149
251
|
let fileName = path.basename(mediaUrl) || undefined;
|
|
150
252
|
if (fileName && !path.extname(fileName) && mime) {
|
|
151
253
|
const ext = extensionForMime(mime);
|
|
152
|
-
if (ext)
|
|
254
|
+
if (ext) {
|
|
153
255
|
fileName = `${fileName}${ext}`;
|
|
256
|
+
}
|
|
154
257
|
}
|
|
155
258
|
return await clampAndFinalize({
|
|
156
259
|
buffer: data,
|
|
@@ -159,11 +262,13 @@ async function loadWebMediaInternal(mediaUrl, options = {}) {
|
|
|
159
262
|
fileName,
|
|
160
263
|
});
|
|
161
264
|
}
|
|
162
|
-
export async function loadWebMedia(mediaUrl, maxBytesOrOptions) {
|
|
265
|
+
export async function loadWebMedia(mediaUrl, maxBytesOrOptions, options) {
|
|
163
266
|
if (typeof maxBytesOrOptions === "number" || maxBytesOrOptions === undefined) {
|
|
164
267
|
return await loadWebMediaInternal(mediaUrl, {
|
|
165
268
|
maxBytes: maxBytesOrOptions,
|
|
166
269
|
optimizeImages: true,
|
|
270
|
+
ssrfPolicy: options?.ssrfPolicy,
|
|
271
|
+
localRoots: options?.localRoots,
|
|
167
272
|
});
|
|
168
273
|
}
|
|
169
274
|
return await loadWebMediaInternal(mediaUrl, {
|
|
@@ -171,11 +276,13 @@ export async function loadWebMedia(mediaUrl, maxBytesOrOptions) {
|
|
|
171
276
|
optimizeImages: maxBytesOrOptions.optimizeImages ?? true,
|
|
172
277
|
});
|
|
173
278
|
}
|
|
174
|
-
export async function loadWebMediaRaw(mediaUrl, maxBytesOrOptions) {
|
|
279
|
+
export async function loadWebMediaRaw(mediaUrl, maxBytesOrOptions, options) {
|
|
175
280
|
if (typeof maxBytesOrOptions === "number" || maxBytesOrOptions === undefined) {
|
|
176
281
|
return await loadWebMediaInternal(mediaUrl, {
|
|
177
282
|
maxBytes: maxBytesOrOptions,
|
|
178
283
|
optimizeImages: false,
|
|
284
|
+
ssrfPolicy: options?.ssrfPolicy,
|
|
285
|
+
localRoots: options?.localRoots,
|
|
179
286
|
});
|
|
180
287
|
}
|
|
181
288
|
return await loadWebMediaInternal(mediaUrl, {
|
|
@@ -191,7 +298,7 @@ export async function optimizeImageToJpeg(buffer, maxBytes, opts = {}) {
|
|
|
191
298
|
source = await convertHeicToJpeg(buffer);
|
|
192
299
|
}
|
|
193
300
|
catch (err) {
|
|
194
|
-
throw new Error(`HEIC image conversion failed: ${String(err)}
|
|
301
|
+
throw new Error(`HEIC image conversion failed: ${String(err)}`, { cause: err });
|
|
195
302
|
}
|
|
196
303
|
}
|
|
197
304
|
const sides = [2048, 1536, 1280, 1024, 800];
|