@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
|
@@ -6,8 +6,10 @@ import { stopSubagentsForRequester } from "../../auto-reply/reply/abort.js";
|
|
|
6
6
|
import { clearSessionQueues } from "../../auto-reply/reply/queue.js";
|
|
7
7
|
import { loadConfig } from "../../config/config.js";
|
|
8
8
|
import { loadSessionStore, snapshotSessionOrigin, resolveMainSessionKey, updateSessionStore, } from "../../config/sessions.js";
|
|
9
|
+
import { unbindThreadBindingsBySessionKey } from "../../discord/monitor/thread-bindings.js";
|
|
9
10
|
import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
|
|
10
|
-
import {
|
|
11
|
+
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
|
|
12
|
+
import { isSubagentSessionKey, normalizeAgentId, parseAgentSessionKey, } from "../../routing/session-key.js";
|
|
11
13
|
import { ErrorCodes, errorShape, validateSessionsCompactParams, validateSessionsDeleteParams, validateSessionsListParams, validateSessionsPatchParams, validateSessionsPreviewParams, validateSessionsResetParams, validateSessionsResolveParams, } from "../protocol/index.js";
|
|
12
14
|
import { archiveFileOnDisk, archiveSessionTranscripts, listSessionsFromStore, loadCombinedSessionStoreForGateway, loadSessionEntry, pruneLegacyStoreKeys, readSessionPreviewItemsFromTranscript, resolveGatewaySessionStoreTarget, resolveSessionModelRef, resolveSessionTranscriptCandidates, } from "../session-utils.js";
|
|
13
15
|
import { applySessionsPatchToStore } from "../sessions-patch.js";
|
|
@@ -33,6 +35,13 @@ function resolveGatewaySessionTargetFromKey(key) {
|
|
|
33
35
|
const target = resolveGatewaySessionStoreTarget({ cfg, key });
|
|
34
36
|
return { cfg, target, storePath: target.storePath };
|
|
35
37
|
}
|
|
38
|
+
function rejectWebchatSessionMutation(params) {
|
|
39
|
+
if (!params.client?.connect || !params.isWebchatConnect(params.client.connect)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
params.respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `webchat clients cannot ${params.action} sessions; use chat.send for session-scoped updates`));
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
36
45
|
function migrateAndPruneSessionStoreKey(params) {
|
|
37
46
|
const target = resolveGatewaySessionStoreTarget({
|
|
38
47
|
cfg: params.cfg,
|
|
@@ -65,6 +74,31 @@ function archiveSessionTranscriptsForSession(params) {
|
|
|
65
74
|
reason: params.reason,
|
|
66
75
|
});
|
|
67
76
|
}
|
|
77
|
+
async function emitSessionUnboundLifecycleEvent(params) {
|
|
78
|
+
const targetKind = isSubagentSessionKey(params.targetSessionKey) ? "subagent" : "acp";
|
|
79
|
+
unbindThreadBindingsBySessionKey({
|
|
80
|
+
targetSessionKey: params.targetSessionKey,
|
|
81
|
+
targetKind,
|
|
82
|
+
reason: params.reason,
|
|
83
|
+
sendFarewell: true,
|
|
84
|
+
});
|
|
85
|
+
if (params.emitHooks === false) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const hookRunner = getGlobalHookRunner();
|
|
89
|
+
if (!hookRunner?.hasHooks("subagent_ended")) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
await hookRunner.runSubagentEnded({
|
|
93
|
+
targetSessionKey: params.targetSessionKey,
|
|
94
|
+
targetKind,
|
|
95
|
+
reason: params.reason,
|
|
96
|
+
sendFarewell: true,
|
|
97
|
+
outcome: params.reason === "session-reset" ? "reset" : "deleted",
|
|
98
|
+
}, {
|
|
99
|
+
childSessionKey: params.targetSessionKey,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
68
102
|
async function ensureSessionRuntimeCleanup(params) {
|
|
69
103
|
const queueKeys = new Set(params.target.storeKeys);
|
|
70
104
|
queueKeys.add(params.target.canonicalKey);
|
|
@@ -161,7 +195,7 @@ export const sessionsHandlers = {
|
|
|
161
195
|
}
|
|
162
196
|
respond(true, { ok: true, key: resolved.key }, undefined);
|
|
163
197
|
},
|
|
164
|
-
"sessions.patch": async ({ params, respond, context }) => {
|
|
198
|
+
"sessions.patch": async ({ params, respond, context, client, isWebchatConnect }) => {
|
|
165
199
|
if (!assertValidParams(params, validateSessionsPatchParams, "sessions.patch", respond)) {
|
|
166
200
|
return;
|
|
167
201
|
}
|
|
@@ -170,6 +204,9 @@ export const sessionsHandlers = {
|
|
|
170
204
|
if (!key) {
|
|
171
205
|
return;
|
|
172
206
|
}
|
|
207
|
+
if (rejectWebchatSessionMutation({ action: "patch", client, isWebchatConnect, respond })) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
173
210
|
const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
|
|
174
211
|
const applied = await updateSessionStore(storePath, async (store) => {
|
|
175
212
|
const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
|
|
@@ -211,6 +248,7 @@ export const sessionsHandlers = {
|
|
|
211
248
|
}
|
|
212
249
|
const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
|
|
213
250
|
const { entry } = loadSessionEntry(key);
|
|
251
|
+
const hadExistingEntry = Boolean(entry);
|
|
214
252
|
const commandReason = p.reason === "new" ? "new" : "reset";
|
|
215
253
|
const hookEvent = createInternalHookEvent("command", commandReason, target.canonicalKey ?? key, {
|
|
216
254
|
sessionEntry: entry,
|
|
@@ -267,9 +305,15 @@ export const sessionsHandlers = {
|
|
|
267
305
|
agentId: target.agentId,
|
|
268
306
|
reason: "reset",
|
|
269
307
|
});
|
|
308
|
+
if (hadExistingEntry) {
|
|
309
|
+
await emitSessionUnboundLifecycleEvent({
|
|
310
|
+
targetSessionKey: target.canonicalKey ?? key,
|
|
311
|
+
reason: "session-reset",
|
|
312
|
+
});
|
|
313
|
+
}
|
|
270
314
|
respond(true, { ok: true, key: target.canonicalKey, entry: next }, undefined);
|
|
271
315
|
},
|
|
272
|
-
"sessions.delete": async ({ params, respond }) => {
|
|
316
|
+
"sessions.delete": async ({ params, respond, client, isWebchatConnect }) => {
|
|
273
317
|
if (!assertValidParams(params, validateSessionsDeleteParams, "sessions.delete", respond)) {
|
|
274
318
|
return;
|
|
275
319
|
}
|
|
@@ -278,6 +322,9 @@ export const sessionsHandlers = {
|
|
|
278
322
|
if (!key) {
|
|
279
323
|
return;
|
|
280
324
|
}
|
|
325
|
+
if (rejectWebchatSessionMutation({ action: "delete", client, isWebchatConnect, respond })) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
281
328
|
const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
|
|
282
329
|
const mainKey = resolveMainSessionKey(cfg);
|
|
283
330
|
if (target.canonicalKey === mainKey) {
|
|
@@ -287,19 +334,20 @@ export const sessionsHandlers = {
|
|
|
287
334
|
const deleteTranscript = typeof p.deleteTranscript === "boolean" ? p.deleteTranscript : true;
|
|
288
335
|
const { entry } = loadSessionEntry(key);
|
|
289
336
|
const sessionId = entry?.sessionId;
|
|
290
|
-
const existed = Boolean(entry);
|
|
291
337
|
const cleanupError = await ensureSessionRuntimeCleanup({ cfg, key, target, sessionId });
|
|
292
338
|
if (cleanupError) {
|
|
293
339
|
respond(false, undefined, cleanupError);
|
|
294
340
|
return;
|
|
295
341
|
}
|
|
296
|
-
await updateSessionStore(storePath, (store) => {
|
|
342
|
+
const deleted = await updateSessionStore(storePath, (store) => {
|
|
297
343
|
const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
|
|
298
|
-
|
|
344
|
+
const hadEntry = Boolean(store[primaryKey]);
|
|
345
|
+
if (hadEntry) {
|
|
299
346
|
delete store[primaryKey];
|
|
300
347
|
}
|
|
348
|
+
return hadEntry;
|
|
301
349
|
});
|
|
302
|
-
const archived = deleteTranscript
|
|
350
|
+
const archived = deleted && deleteTranscript
|
|
303
351
|
? archiveSessionTranscriptsForSession({
|
|
304
352
|
sessionId,
|
|
305
353
|
storePath,
|
|
@@ -308,7 +356,15 @@ export const sessionsHandlers = {
|
|
|
308
356
|
reason: "deleted",
|
|
309
357
|
})
|
|
310
358
|
: [];
|
|
311
|
-
|
|
359
|
+
if (deleted) {
|
|
360
|
+
const emitLifecycleHooks = p.emitLifecycleHooks !== false;
|
|
361
|
+
await emitSessionUnboundLifecycleEvent({
|
|
362
|
+
targetSessionKey: target.canonicalKey ?? key,
|
|
363
|
+
reason: "session-delete",
|
|
364
|
+
emitHooks: emitLifecycleHooks,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
respond(true, { ok: true, key: target.canonicalKey, deleted, archived }, undefined);
|
|
312
368
|
},
|
|
313
369
|
"sessions.compact": async ({ params, respond }) => {
|
|
314
370
|
if (!assertValidParams(params, validateSessionsCompactParams, "sessions.compact", respond)) {
|
|
@@ -1,32 +1,122 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { loadConfig } from "../../config/config.js";
|
|
3
|
-
import { resolveSessionFilePath } from "../../config/sessions/paths.js";
|
|
3
|
+
import { resolveSessionFilePath, resolveSessionFilePathOptions, } from "../../config/sessions/paths.js";
|
|
4
4
|
import { loadProviderUsageSummary } from "../../infra/provider-usage.js";
|
|
5
5
|
import { loadCostUsageSummary, loadSessionCostSummary, loadSessionUsageTimeSeries, discoverAllSessions, } from "../../infra/session-cost-usage.js";
|
|
6
6
|
import { parseAgentSessionKey } from "../../routing/session-key.js";
|
|
7
|
+
import { buildUsageAggregateTail } from "../../shared/usage-aggregates.js";
|
|
7
8
|
import { ErrorCodes, errorShape, formatValidationErrors, validateSessionsUsageParams, } from "../protocol/index.js";
|
|
8
9
|
import { listAgentsForGateway, loadCombinedSessionStoreForGateway, loadSessionEntry, } from "../session-utils.js";
|
|
9
10
|
const COST_USAGE_CACHE_TTL_MS = 30_000;
|
|
11
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
10
12
|
const costUsageCache = new Map();
|
|
13
|
+
function resolveSessionUsageFileOrRespond(key, respond) {
|
|
14
|
+
const config = loadConfig();
|
|
15
|
+
const { entry, storePath } = loadSessionEntry(key);
|
|
16
|
+
// For discovered sessions (not in store), try using key as sessionId directly
|
|
17
|
+
const parsed = parseAgentSessionKey(key);
|
|
18
|
+
const agentId = parsed?.agentId;
|
|
19
|
+
const rawSessionId = parsed?.rest ?? key;
|
|
20
|
+
const sessionId = entry?.sessionId ?? rawSessionId;
|
|
21
|
+
let sessionFile;
|
|
22
|
+
try {
|
|
23
|
+
const pathOpts = resolveSessionFilePathOptions({ storePath, agentId });
|
|
24
|
+
sessionFile = resolveSessionFilePath(sessionId, entry, pathOpts);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session key: ${key}`));
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return { config, entry, agentId, sessionId, sessionFile };
|
|
31
|
+
}
|
|
32
|
+
const parseDateParts = (raw) => {
|
|
33
|
+
if (typeof raw !== "string" || !raw.trim()) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
|
|
37
|
+
if (!match) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const [, yearStr, monthStr, dayStr] = match;
|
|
41
|
+
const year = Number(yearStr);
|
|
42
|
+
const monthIndex = Number(monthStr) - 1;
|
|
43
|
+
const day = Number(dayStr);
|
|
44
|
+
if (!Number.isFinite(year) || !Number.isFinite(monthIndex) || !Number.isFinite(day)) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return { year, monthIndex, day };
|
|
48
|
+
};
|
|
11
49
|
/**
|
|
12
|
-
* Parse a
|
|
13
|
-
* Returns undefined if invalid.
|
|
50
|
+
* Parse a UTC offset string in the format UTC+H, UTC-H, UTC+HH, UTC-HH, UTC+H:MM, UTC-HH:MM.
|
|
51
|
+
* Returns the UTC offset in minutes (east-positive), or undefined if invalid.
|
|
14
52
|
*/
|
|
15
|
-
const
|
|
53
|
+
const parseUtcOffsetToMinutes = (raw) => {
|
|
16
54
|
if (typeof raw !== "string" || !raw.trim()) {
|
|
17
55
|
return undefined;
|
|
18
56
|
}
|
|
19
|
-
const match = /^(
|
|
57
|
+
const match = /^UTC([+-])(\d{1,2})(?::([0-5]\d))?$/.exec(raw.trim());
|
|
20
58
|
if (!match) {
|
|
21
59
|
return undefined;
|
|
22
60
|
}
|
|
23
|
-
const [
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
if (Number.
|
|
61
|
+
const sign = match[1] === "+" ? 1 : -1;
|
|
62
|
+
const hours = Number(match[2]);
|
|
63
|
+
const minutes = Number(match[3] ?? "0");
|
|
64
|
+
if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
if (hours > 14 || (hours === 14 && minutes !== 0)) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const totalMinutes = sign * (hours * 60 + minutes);
|
|
71
|
+
if (totalMinutes < -12 * 60 || totalMinutes > 14 * 60) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return totalMinutes;
|
|
75
|
+
};
|
|
76
|
+
const resolveDateInterpretation = (params) => {
|
|
77
|
+
if (params.mode === "gateway") {
|
|
78
|
+
return { mode: "gateway" };
|
|
79
|
+
}
|
|
80
|
+
if (params.mode === "specific") {
|
|
81
|
+
const utcOffsetMinutes = parseUtcOffsetToMinutes(params.utcOffset);
|
|
82
|
+
if (utcOffsetMinutes !== undefined) {
|
|
83
|
+
return { mode: "specific", utcOffsetMinutes };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Backward compatibility: when mode is missing (or invalid), keep current UTC interpretation.
|
|
87
|
+
return { mode: "utc" };
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Parse a date string (YYYY-MM-DD) to start-of-day timestamp based on interpretation mode.
|
|
91
|
+
* Returns undefined if invalid.
|
|
92
|
+
*/
|
|
93
|
+
const parseDateToMs = (raw, interpretation = { mode: "utc" }) => {
|
|
94
|
+
const parts = parseDateParts(raw);
|
|
95
|
+
if (!parts) {
|
|
27
96
|
return undefined;
|
|
28
97
|
}
|
|
29
|
-
|
|
98
|
+
const { year, monthIndex, day } = parts;
|
|
99
|
+
if (interpretation.mode === "gateway") {
|
|
100
|
+
const ms = new Date(year, monthIndex, day).getTime();
|
|
101
|
+
return Number.isNaN(ms) ? undefined : ms;
|
|
102
|
+
}
|
|
103
|
+
if (interpretation.mode === "specific") {
|
|
104
|
+
const ms = Date.UTC(year, monthIndex, day) - interpretation.utcOffsetMinutes * 60 * 1000;
|
|
105
|
+
return Number.isNaN(ms) ? undefined : ms;
|
|
106
|
+
}
|
|
107
|
+
const ms = Date.UTC(year, monthIndex, day);
|
|
108
|
+
return Number.isNaN(ms) ? undefined : ms;
|
|
109
|
+
};
|
|
110
|
+
const getTodayStartMs = (now, interpretation) => {
|
|
111
|
+
if (interpretation.mode === "gateway") {
|
|
112
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
113
|
+
}
|
|
114
|
+
if (interpretation.mode === "specific") {
|
|
115
|
+
const shifted = new Date(now.getTime() + interpretation.utcOffsetMinutes * 60 * 1000);
|
|
116
|
+
return (Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth(), shifted.getUTCDate()) -
|
|
117
|
+
interpretation.utcOffsetMinutes * 60 * 1000);
|
|
118
|
+
}
|
|
119
|
+
return Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
|
30
120
|
};
|
|
31
121
|
const parseDays = (raw) => {
|
|
32
122
|
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
@@ -46,25 +136,34 @@ const parseDays = (raw) => {
|
|
|
46
136
|
*/
|
|
47
137
|
const parseDateRange = (params) => {
|
|
48
138
|
const now = new Date();
|
|
49
|
-
|
|
50
|
-
const todayStartMs =
|
|
51
|
-
const todayEndMs = todayStartMs +
|
|
52
|
-
const startMs = parseDateToMs(params.startDate);
|
|
53
|
-
const endMs = parseDateToMs(params.endDate);
|
|
139
|
+
const interpretation = resolveDateInterpretation(params);
|
|
140
|
+
const todayStartMs = getTodayStartMs(now, interpretation);
|
|
141
|
+
const todayEndMs = todayStartMs + DAY_MS - 1;
|
|
142
|
+
const startMs = parseDateToMs(params.startDate, interpretation);
|
|
143
|
+
const endMs = parseDateToMs(params.endDate, interpretation);
|
|
54
144
|
if (startMs !== undefined && endMs !== undefined) {
|
|
55
145
|
// endMs should be end of day
|
|
56
|
-
return { startMs, endMs: endMs +
|
|
146
|
+
return { startMs, endMs: endMs + DAY_MS - 1 };
|
|
57
147
|
}
|
|
58
148
|
const days = parseDays(params.days);
|
|
59
149
|
if (days !== undefined) {
|
|
60
150
|
const clampedDays = Math.max(1, days);
|
|
61
|
-
const start = todayStartMs - (clampedDays - 1) *
|
|
151
|
+
const start = todayStartMs - (clampedDays - 1) * DAY_MS;
|
|
62
152
|
return { startMs: start, endMs: todayEndMs };
|
|
63
153
|
}
|
|
64
154
|
// Default to last 30 days
|
|
65
|
-
const defaultStartMs = todayStartMs - 29 *
|
|
155
|
+
const defaultStartMs = todayStartMs - 29 * DAY_MS;
|
|
66
156
|
return { startMs: defaultStartMs, endMs: todayEndMs };
|
|
67
157
|
};
|
|
158
|
+
function buildStoreBySessionId(store) {
|
|
159
|
+
const storeBySessionId = new Map();
|
|
160
|
+
for (const [key, entry] of Object.entries(store)) {
|
|
161
|
+
if (entry?.sessionId) {
|
|
162
|
+
storeBySessionId.set(entry.sessionId, { key, entry });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return storeBySessionId;
|
|
166
|
+
}
|
|
68
167
|
async function discoverAllSessionsForUsage(params) {
|
|
69
168
|
const agents = listAgentsForGateway(params.config).agents;
|
|
70
169
|
const results = await Promise.all(agents.map(async (agent) => {
|
|
@@ -122,7 +221,11 @@ async function loadCostUsageSummaryCached(params) {
|
|
|
122
221
|
}
|
|
123
222
|
// Exposed for unit tests (kept as a single export to avoid widening the public API surface).
|
|
124
223
|
export const __test = {
|
|
224
|
+
parseDateParts,
|
|
225
|
+
parseUtcOffsetToMinutes,
|
|
226
|
+
resolveDateInterpretation,
|
|
125
227
|
parseDateToMs,
|
|
228
|
+
getTodayStartMs,
|
|
126
229
|
parseDays,
|
|
127
230
|
parseDateRange,
|
|
128
231
|
discoverAllSessionsForUsage,
|
|
@@ -140,6 +243,8 @@ export const usageHandlers = {
|
|
|
140
243
|
startDate: params?.startDate,
|
|
141
244
|
endDate: params?.endDate,
|
|
142
245
|
days: params?.days,
|
|
246
|
+
mode: params?.mode,
|
|
247
|
+
utcOffset: params?.utcOffset,
|
|
143
248
|
});
|
|
144
249
|
const summary = await loadCostUsageSummaryCached({ startMs, endMs, config });
|
|
145
250
|
respond(true, summary, undefined);
|
|
@@ -154,12 +259,14 @@ export const usageHandlers = {
|
|
|
154
259
|
const { startMs, endMs } = parseDateRange({
|
|
155
260
|
startDate: p.startDate,
|
|
156
261
|
endDate: p.endDate,
|
|
262
|
+
mode: p.mode,
|
|
263
|
+
utcOffset: p.utcOffset,
|
|
157
264
|
});
|
|
158
265
|
const limit = typeof p.limit === "number" && Number.isFinite(p.limit) ? p.limit : 50;
|
|
159
266
|
const includeContextWeight = p.includeContextWeight ?? false;
|
|
160
267
|
const specificKey = typeof p.key === "string" ? p.key.trim() : null;
|
|
161
268
|
// Load session store for named sessions
|
|
162
|
-
const { store } = loadCombinedSessionStoreForGateway(config);
|
|
269
|
+
const { storePath, store } = loadCombinedSessionStoreForGateway(config);
|
|
163
270
|
const now = Date.now();
|
|
164
271
|
const mergedEntries = [];
|
|
165
272
|
// Optimization: If a specific key is requested, skip full directory scan
|
|
@@ -169,12 +276,7 @@ export const usageHandlers = {
|
|
|
169
276
|
const keyRest = parsed?.rest ?? specificKey;
|
|
170
277
|
// Prefer the store entry when available, even if the caller provides a discovered key
|
|
171
278
|
// (`agent:<id>:<sessionId>`) for a session that now has a canonical store key.
|
|
172
|
-
const storeBySessionId =
|
|
173
|
-
for (const [key, entry] of Object.entries(store)) {
|
|
174
|
-
if (entry?.sessionId) {
|
|
175
|
-
storeBySessionId.set(entry.sessionId, { key, entry });
|
|
176
|
-
}
|
|
177
|
-
}
|
|
279
|
+
const storeBySessionId = buildStoreBySessionId(store);
|
|
178
280
|
const storeMatch = store[specificKey]
|
|
179
281
|
? { key: specificKey, entry: store[specificKey] }
|
|
180
282
|
: null;
|
|
@@ -183,9 +285,18 @@ export const usageHandlers = {
|
|
|
183
285
|
const storeEntry = storeMatch?.entry ?? storeByIdMatch?.entry;
|
|
184
286
|
const sessionId = storeEntry?.sessionId ?? keyRest;
|
|
185
287
|
// Resolve the session file path
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
288
|
+
let sessionFile;
|
|
289
|
+
try {
|
|
290
|
+
const pathOpts = resolveSessionFilePathOptions({
|
|
291
|
+
storePath: storePath !== "(multiple)" ? storePath : undefined,
|
|
292
|
+
agentId: agentIdFromKey,
|
|
293
|
+
});
|
|
294
|
+
sessionFile = resolveSessionFilePath(sessionId, storeEntry, pathOpts);
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session reference: ${specificKey}`));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
189
300
|
try {
|
|
190
301
|
const stats = fs.statSync(sessionFile);
|
|
191
302
|
if (stats.isFile()) {
|
|
@@ -211,12 +322,7 @@ export const usageHandlers = {
|
|
|
211
322
|
endMs,
|
|
212
323
|
});
|
|
213
324
|
// Build a map of sessionId -> store entry for quick lookup
|
|
214
|
-
const storeBySessionId =
|
|
215
|
-
for (const [key, entry] of Object.entries(store)) {
|
|
216
|
-
if (entry?.sessionId) {
|
|
217
|
-
storeBySessionId.set(entry.sessionId, { key, entry });
|
|
218
|
-
}
|
|
219
|
-
}
|
|
325
|
+
const storeBySessionId = buildStoreBySessionId(store);
|
|
220
326
|
for (const discovered of discoveredSessions) {
|
|
221
327
|
const storeMatch = storeBySessionId.get(discovered.sessionId);
|
|
222
328
|
if (storeMatch) {
|
|
@@ -312,11 +418,13 @@ export const usageHandlers = {
|
|
|
312
418
|
target.missingCostEntries += source.missingCostEntries;
|
|
313
419
|
};
|
|
314
420
|
for (const merged of limitedEntries) {
|
|
421
|
+
const agentId = parseAgentSessionKey(merged.key)?.agentId;
|
|
315
422
|
const usage = await loadSessionCostSummary({
|
|
316
423
|
sessionId: merged.sessionId,
|
|
317
424
|
sessionEntry: merged.storeEntry,
|
|
318
425
|
sessionFile: merged.sessionFile,
|
|
319
426
|
config,
|
|
427
|
+
agentId,
|
|
320
428
|
startMs,
|
|
321
429
|
endMs,
|
|
322
430
|
});
|
|
@@ -333,7 +441,6 @@ export const usageHandlers = {
|
|
|
333
441
|
aggregateTotals.cacheWriteCost += usage.cacheWriteCost;
|
|
334
442
|
aggregateTotals.missingCostEntries += usage.missingCostEntries;
|
|
335
443
|
}
|
|
336
|
-
const agentId = parseAgentSessionKey(merged.key)?.agentId;
|
|
337
444
|
const channel = merged.storeEntry?.channel ?? merged.storeEntry?.origin?.provider;
|
|
338
445
|
const chatType = merged.storeEntry?.chatType ?? merged.storeEntry?.origin?.chatType;
|
|
339
446
|
if (usage) {
|
|
@@ -488,6 +595,13 @@ export const usageHandlers = {
|
|
|
488
595
|
const d = new Date(ms);
|
|
489
596
|
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`;
|
|
490
597
|
};
|
|
598
|
+
const tail = buildUsageAggregateTail({
|
|
599
|
+
byChannelMap: byChannelMap,
|
|
600
|
+
latencyTotals,
|
|
601
|
+
dailyLatencyMap,
|
|
602
|
+
modelDailyMap,
|
|
603
|
+
dailyMap: dailyAggregateMap,
|
|
604
|
+
});
|
|
491
605
|
const aggregates = {
|
|
492
606
|
messages: aggregateMessages,
|
|
493
607
|
tools: {
|
|
@@ -514,30 +628,7 @@ export const usageHandlers = {
|
|
|
514
628
|
byAgent: Array.from(byAgentMap.entries())
|
|
515
629
|
.map(([id, totals]) => ({ agentId: id, totals }))
|
|
516
630
|
.toSorted((a, b) => b.totals.totalCost - a.totals.totalCost),
|
|
517
|
-
|
|
518
|
-
.map(([name, totals]) => ({ channel: name, totals }))
|
|
519
|
-
.toSorted((a, b) => b.totals.totalCost - a.totals.totalCost),
|
|
520
|
-
latency: latencyTotals.count > 0
|
|
521
|
-
? {
|
|
522
|
-
count: latencyTotals.count,
|
|
523
|
-
avgMs: latencyTotals.sum / latencyTotals.count,
|
|
524
|
-
minMs: latencyTotals.min === Number.POSITIVE_INFINITY ? 0 : latencyTotals.min,
|
|
525
|
-
maxMs: latencyTotals.max,
|
|
526
|
-
p95Ms: latencyTotals.p95Max,
|
|
527
|
-
}
|
|
528
|
-
: undefined,
|
|
529
|
-
dailyLatency: Array.from(dailyLatencyMap.values())
|
|
530
|
-
.map((entry) => ({
|
|
531
|
-
date: entry.date,
|
|
532
|
-
count: entry.count,
|
|
533
|
-
avgMs: entry.count ? entry.sum / entry.count : 0,
|
|
534
|
-
minMs: entry.min === Number.POSITIVE_INFINITY ? 0 : entry.min,
|
|
535
|
-
maxMs: entry.max,
|
|
536
|
-
p95Ms: entry.p95Max,
|
|
537
|
-
}))
|
|
538
|
-
.toSorted((a, b) => a.date.localeCompare(b.date)),
|
|
539
|
-
modelDaily: Array.from(modelDailyMap.values()).toSorted((a, b) => a.date.localeCompare(b.date) || b.cost - a.cost),
|
|
540
|
-
daily: Array.from(dailyAggregateMap.values()).toSorted((a, b) => a.date.localeCompare(b.date)),
|
|
631
|
+
...tail,
|
|
541
632
|
};
|
|
542
633
|
const result = {
|
|
543
634
|
updatedAt: now,
|
|
@@ -555,19 +646,17 @@ export const usageHandlers = {
|
|
|
555
646
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key is required for timeseries"));
|
|
556
647
|
return;
|
|
557
648
|
}
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const agentId =
|
|
563
|
-
const rawSessionId = parsed?.rest ?? key;
|
|
564
|
-
const sessionId = entry?.sessionId ?? rawSessionId;
|
|
565
|
-
const sessionFile = entry?.sessionFile ?? resolveSessionFilePath(rawSessionId, entry, { agentId });
|
|
649
|
+
const resolved = resolveSessionUsageFileOrRespond(key, respond);
|
|
650
|
+
if (!resolved) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const { config, entry, agentId, sessionId, sessionFile } = resolved;
|
|
566
654
|
const timeseries = await loadSessionUsageTimeSeries({
|
|
567
655
|
sessionId,
|
|
568
656
|
sessionEntry: entry,
|
|
569
657
|
sessionFile,
|
|
570
658
|
config,
|
|
659
|
+
agentId,
|
|
571
660
|
maxPoints: 200,
|
|
572
661
|
});
|
|
573
662
|
if (!timeseries) {
|
|
@@ -585,20 +674,18 @@ export const usageHandlers = {
|
|
|
585
674
|
const limit = typeof params?.limit === "number" && Number.isFinite(params.limit)
|
|
586
675
|
? Math.min(params.limit, 1000)
|
|
587
676
|
: 200;
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
const agentId =
|
|
593
|
-
const rawSessionId = parsed?.rest ?? key;
|
|
594
|
-
const sessionId = entry?.sessionId ?? rawSessionId;
|
|
595
|
-
const sessionFile = entry?.sessionFile ?? resolveSessionFilePath(rawSessionId, entry, { agentId });
|
|
677
|
+
const resolved = resolveSessionUsageFileOrRespond(key, respond);
|
|
678
|
+
if (!resolved) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const { config, entry, agentId, sessionId, sessionFile } = resolved;
|
|
596
682
|
const { loadSessionLogs } = await import("../../infra/session-cost-usage.js");
|
|
597
683
|
const logs = await loadSessionLogs({
|
|
598
684
|
sessionId,
|
|
599
685
|
sessionEntry: entry,
|
|
600
686
|
sessionFile,
|
|
601
687
|
config,
|
|
688
|
+
agentId,
|
|
602
689
|
limit,
|
|
603
690
|
});
|
|
604
691
|
respond(true, { logs: logs ?? [] }, undefined);
|
|
@@ -8,6 +8,7 @@ import { updateSessionStore } from "../config/sessions.js";
|
|
|
8
8
|
import { requestHeartbeatNow } from "../infra/heartbeat-wake.js";
|
|
9
9
|
import { deliverOutboundPayloads } from "../infra/outbound/deliver.js";
|
|
10
10
|
import { resolveOutboundTarget } from "../infra/outbound/targets.js";
|
|
11
|
+
import { registerApnsToken } from "../infra/push-apns.js";
|
|
11
12
|
import { enqueueSystemEvent } from "../infra/system-events.js";
|
|
12
13
|
import { normalizeMainKey } from "../routing/session-key.js";
|
|
13
14
|
import { defaultRuntime } from "../runtime.js";
|
|
@@ -410,6 +411,34 @@ export const handleNodeEvent = async (ctx, nodeId, evt) => {
|
|
|
410
411
|
requestHeartbeatNow({ reason: "exec-event" });
|
|
411
412
|
return;
|
|
412
413
|
}
|
|
414
|
+
case "push.apns.register": {
|
|
415
|
+
if (!evt.payloadJSON) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
let payload;
|
|
419
|
+
try {
|
|
420
|
+
payload = JSON.parse(evt.payloadJSON);
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const obj = typeof payload === "object" && payload !== null ? payload : {};
|
|
426
|
+
const token = typeof obj.token === "string" ? obj.token : "";
|
|
427
|
+
const topic = typeof obj.topic === "string" ? obj.topic : "";
|
|
428
|
+
const environment = obj.environment;
|
|
429
|
+
try {
|
|
430
|
+
await registerApnsToken({
|
|
431
|
+
nodeId,
|
|
432
|
+
token,
|
|
433
|
+
topic,
|
|
434
|
+
environment,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
ctx.logGateway.warn(`push apns register failed node=${nodeId}: ${formatForLog(err)}`);
|
|
439
|
+
}
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
413
442
|
default:
|
|
414
443
|
return;
|
|
415
444
|
}
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import { assertGatewayAuthConfigured, resolveGatewayAuth, } from "./auth.js";
|
|
2
2
|
import { normalizeControlUiBasePath } from "./control-ui-shared.js";
|
|
3
3
|
import { resolveHooksConfig } from "./hooks.js";
|
|
4
|
-
import { isLoopbackHost, resolveGatewayBindHost } from "./net.js";
|
|
4
|
+
import { isLoopbackHost, isTrustedProxyAddress, isValidIPv4, resolveGatewayBindHost, } from "./net.js";
|
|
5
|
+
import { mergeGatewayTailscaleConfig } from "./startup-auth.js";
|
|
5
6
|
export async function resolveGatewayRuntimeConfig(params) {
|
|
6
7
|
const bindMode = params.bind ?? params.cfg.gateway?.bind ?? "loopback";
|
|
7
8
|
const customBindHost = params.cfg.gateway?.customBindHost;
|
|
8
9
|
const bindHost = params.host ?? (await resolveGatewayBindHost(bindMode, customBindHost));
|
|
10
|
+
if (bindMode === "loopback" && !isLoopbackHost(bindHost)) {
|
|
11
|
+
throw new Error(`gateway bind=loopback resolved to non-loopback host ${bindHost}; refusing fallback to a network bind`);
|
|
12
|
+
}
|
|
13
|
+
if (bindMode === "custom") {
|
|
14
|
+
const configuredCustomBindHost = customBindHost?.trim();
|
|
15
|
+
if (!configuredCustomBindHost) {
|
|
16
|
+
throw new Error("gateway.bind=custom requires gateway.customBindHost");
|
|
17
|
+
}
|
|
18
|
+
if (!isValidIPv4(configuredCustomBindHost)) {
|
|
19
|
+
throw new Error(`gateway.bind=custom requires a valid IPv4 customBindHost (got ${configuredCustomBindHost})`);
|
|
20
|
+
}
|
|
21
|
+
if (bindHost !== configuredCustomBindHost) {
|
|
22
|
+
throw new Error(`gateway bind=custom requested ${configuredCustomBindHost} but resolved ${bindHost}; refusing fallback`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
9
25
|
const controlUiEnabled = params.controlUiEnabled ?? params.cfg.gateway?.controlUi?.enabled ?? true;
|
|
10
26
|
const openAiChatCompletionsEnabled = params.openAiChatCompletionsEnabled ??
|
|
11
27
|
params.cfg.gateway?.http?.endpoints?.chatCompletions?.enabled ??
|
|
@@ -17,21 +33,13 @@ export async function resolveGatewayRuntimeConfig(params) {
|
|
|
17
33
|
const controlUiRoot = typeof controlUiRootRaw === "string" && controlUiRootRaw.trim().length > 0
|
|
18
34
|
? controlUiRootRaw.trim()
|
|
19
35
|
: undefined;
|
|
20
|
-
const authBase = params.cfg.gateway?.auth ?? {};
|
|
21
|
-
const authOverrides = params.auth ?? {};
|
|
22
|
-
const authConfig = {
|
|
23
|
-
...authBase,
|
|
24
|
-
...authOverrides,
|
|
25
|
-
};
|
|
26
36
|
const tailscaleBase = params.cfg.gateway?.tailscale ?? {};
|
|
27
37
|
const tailscaleOverrides = params.tailscale ?? {};
|
|
28
|
-
const tailscaleConfig =
|
|
29
|
-
...tailscaleBase,
|
|
30
|
-
...tailscaleOverrides,
|
|
31
|
-
};
|
|
38
|
+
const tailscaleConfig = mergeGatewayTailscaleConfig(tailscaleBase, tailscaleOverrides);
|
|
32
39
|
const tailscaleMode = tailscaleConfig.mode ?? "off";
|
|
33
40
|
const resolvedAuth = resolveGatewayAuth({
|
|
34
|
-
authConfig,
|
|
41
|
+
authConfig: params.cfg.gateway?.auth,
|
|
42
|
+
authOverride: params.auth,
|
|
35
43
|
env: process.env,
|
|
36
44
|
tailscaleMode,
|
|
37
45
|
});
|
|
@@ -41,6 +49,7 @@ export async function resolveGatewayRuntimeConfig(params) {
|
|
|
41
49
|
const hasSharedSecret = (authMode === "token" && hasToken) || (authMode === "password" && hasPassword);
|
|
42
50
|
const hooksConfig = resolveHooksConfig(params.cfg);
|
|
43
51
|
const canvasHostEnabled = process.env.CLAWDBOT_SKIP_CANVAS_HOST !== "1" && params.cfg.canvasHost?.enabled !== false;
|
|
52
|
+
const trustedProxies = params.cfg.gateway?.trustedProxies ?? [];
|
|
44
53
|
assertGatewayAuthConfigured(resolvedAuth);
|
|
45
54
|
if (tailscaleMode === "funnel" && authMode !== "password") {
|
|
46
55
|
throw new Error("tailscale funnel requires gateway auth mode=password (set gateway.auth.password or POOLBOT_GATEWAY_PASSWORD)");
|
|
@@ -48,9 +57,21 @@ export async function resolveGatewayRuntimeConfig(params) {
|
|
|
48
57
|
if (tailscaleMode !== "off" && !isLoopbackHost(bindHost)) {
|
|
49
58
|
throw new Error("tailscale serve/funnel requires gateway bind=loopback (127.0.0.1)");
|
|
50
59
|
}
|
|
51
|
-
if (!isLoopbackHost(bindHost) && !hasSharedSecret) {
|
|
60
|
+
if (!isLoopbackHost(bindHost) && !hasSharedSecret && authMode !== "trusted-proxy") {
|
|
52
61
|
throw new Error(`refusing to bind gateway to ${bindHost}:${params.port} without auth (set gateway.auth.token/password, or set POOLBOT_GATEWAY_TOKEN/POOLBOT_GATEWAY_PASSWORD)`);
|
|
53
62
|
}
|
|
63
|
+
if (authMode === "trusted-proxy") {
|
|
64
|
+
if (trustedProxies.length === 0) {
|
|
65
|
+
throw new Error("gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured with at least one proxy IP");
|
|
66
|
+
}
|
|
67
|
+
if (isLoopbackHost(bindHost)) {
|
|
68
|
+
const hasLoopbackTrustedProxy = isTrustedProxyAddress("127.0.0.1", trustedProxies) ||
|
|
69
|
+
isTrustedProxyAddress("::1", trustedProxies);
|
|
70
|
+
if (!hasLoopbackTrustedProxy) {
|
|
71
|
+
throw new Error("gateway auth mode=trusted-proxy with bind=loopback requires gateway.trustedProxies to include 127.0.0.1, ::1, or a loopback CIDR");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
54
75
|
return {
|
|
55
76
|
bindHost,
|
|
56
77
|
controlUiEnabled,
|