Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f2f000e
fix: standardize provider user agents with the AstrBot version (#10082)
Soulter Sep 16, 2026
daa55f3
fix(qqofficial): always send the state=10 closing frame in C2C stream…
he-yufeng Sep 16, 2026
b8036e5
feat: optimize model selection with source filters and sticky groups …
Soulter Sep 16, 2026
4434f29
fix: preserve OpenAI endpoint resolution for empty api_base (#10079)
wcqqq1214 Sep 16, 2026
b3688a6
fix: reduce Telegram log spam from polling and conflict errors (#1002…
nina-ysml Sep 14, 2026
07b094c
fix: preserve input image geometry under CUA sandbox and warn on over…
piexian Sep 16, 2026
2d87541
chore(sync): record upstream integration
BegoniaHe Sep 16, 2026
ddf60a0
chore: date 4.28.1 and open 4.28.2 wip
BegoniaHe Sep 16, 2026
77e4d26
feat: add role-based Local computer permissions (#9472)
Soulter Sep 16, 2026
9b7e9ca
Fix:local sandbox policy feedback (#10096)
RC-CHN Sep 15, 2026
b682ddb
chore(deps): bump the github-actions group with 2 updates (#10088)
dependabot[bot] Sep 16, 2026
cb4dc22
docs: sync guides with current WebUI navigation (#10093)
Soulter Sep 16, 2026
8cf6d67
fix(cron): say who owns a task instead of returning an empty list (#1…
Pleiades1726 Sep 16, 2026
f53cc28
chore(sync): record upstream integration
BegoniaHe Sep 16, 2026
968eae8
fix(provider): restore request headers after user-agent sync
BegoniaHe Sep 16, 2026
6eaa37d
fix: resolve local sandbox CodeQL and CI failures
BegoniaHe Sep 16, 2026
ffd965b
fix: keep restricted local file tools working on windows
BegoniaHe Sep 16, 2026
43825ea
fix: close local permission review findings
BegoniaHe Sep 16, 2026
10e204b
fix(codeql): close restricted file descriptors on error
BegoniaHe Sep 16, 2026
103a89a
fix(computer): skip the shell UX blocklist inside the OS sandbox
BegoniaHe Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 11.21.0
- name: Setup Node.js
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/code-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
node-version: '26.5.0'
package-manager-cache: false
- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 11.21.0
- name: Install root toolchain
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
Expand Down Expand Up @@ -95,6 +95,6 @@ jobs:
exit 1

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
with:
category: '/language:${{matrix.language}}'
2 changes: 1 addition & 1 deletion .github/workflows/dashboard_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 11.21.0

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linux-development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
package-manager-cache: false

- name: Set up pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 11.21.0

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/plugin-ui-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
python -m pip install uv
bash scripts/uv_sync_ci.sh --group dev --locked
- name: Set up pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 11.21.0
- name: Set up Node.js
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
python -m pip install uv
bash scripts/uv_sync_ci.sh --group dev --locked
- name: Set up pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 11.21.0
- name: Set up Node.js
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/quality-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
package-manager-cache: false

- name: Set up pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 11.21.0

Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@ jobs:
python -m pip install --upgrade pip
python -m pip install uv

- name: Install Linux sandbox dependency
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y bubblewrap

- name: Enable and verify Linux sandbox execution
if: runner.os == 'Linux'
run: |
if [ -f /proc/sys/kernel/apparmor_restrict_unprivileged_userns ]; then
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
fi
bwrap --unshare-all --ro-bind / / /bin/true

- name: Verify macOS sandbox dependency
if: runner.os == 'macOS'
run: test -x /usr/bin/sandbox-exec

- name: Run tests
run: |
chmod +x scripts/run_pytests_ci.sh
Expand Down
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,9 @@ User/developer documentation is bilingual. A behavior, command, navigation, or
configuration change normally requires matching updates under `docs/zh/` and
`docs/en/`, plus `docs/.vitepress/config.mjs` when navigation changes. Keep the
two languages structurally aligned, but write natural translations rather than
copying stale text.
copying stale text. For renamed, moved, or merged WebUI entry points, include
an old-to-new mapping in the relevant docs (see `docs/zh/use/webui.md` and
`docs/en/use/webui.md`).

Validate documentation with:

Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ fork behavior apart from upstream merges:

### v4.28.x

- [v4.28.1](./changelogs/v4.28.1.md) — `[WIP]`
- [v4.28.2](./changelogs/v4.28.2.md) — `[WIP]`
- [v4.28.1](./changelogs/v4.28.1.md) — 2026-09-14
- [v4.28.0](./changelogs/v4.28.0.md)

### v4.27.x
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ cd dashboard && pnpm generate:api

`make run` 会把文档打进 WebUI 的 `/help/`。不要把文档链接指向上游 `docs.astrbot.app`。

修改 WebUI 菜单入口、页面结构或界面术语时,在同一个 PR 中更新 `docs/zh` 和 `docs/en` 的相关操作说明。入口改名、移动或合并时,在对应文档中写明「旧入口 → 新入口」,并维护 [WebUI 入口对照](docs/zh/use/webui.md#菜单与旧入口对照)。

如果你修改了后端 OpenAPI、接口路由或响应结构,请同时刷新:

```bash
Expand Down Expand Up @@ -148,6 +150,8 @@ cd dashboard && pnpm generate:api

`make run` serves documentation from the WebUI at `/help/`. Do not point documentation links at upstream `docs.astrbot.app`.

When changing WebUI navigation, page structure, or labels, update the affected instructions in `docs/zh` and `docs/en` in the same PR. For renamed, moved, or merged entry points, include an **old entry → new entry** mapping and keep the [WebUI navigation reference](docs/en/use/webui.md#navigation-and-previous-entry-points) current.

If you change backend OpenAPI routes, request schemas, or response schemas, also refresh:

```bash
Expand Down
2 changes: 1 addition & 1 deletion astrbot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import logging

__version__ = "4.28.1"
__version__ = "4.28.2"
logger = logging.getLogger("astrbot")
39 changes: 39 additions & 0 deletions astrbot/core/agent/request_preparation.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@


_MAX_PREPARED_MEDIA_BYTES = 20 * 1024 * 1024
_CUA_IMAGE_WARN_BYTES = 5 * 1024 * 1024


def clone_provider_request(request: ProviderRequest) -> ProviderRequest:
Expand Down Expand Up @@ -70,6 +71,27 @@ def _safe_media_ref(ref: object) -> str | None:
return value


def cua_pixel_mode_from_settings(
provider_settings: dict[str, object] | None,
) -> bool:
"""Return whether CUA sandbox stills must keep 1:1 pixel coordinates.

Args:
provider_settings: ``provider_settings`` mapping, or ``None``.

Returns:
True when Computer Use is the CUA sandbox runtime.
"""
if not isinstance(provider_settings, dict):
return False
sandbox_cfg = provider_settings.get("sandbox")
return (
provider_settings.get("computer_use_runtime") == "sandbox"
and isinstance(sandbox_cfg, dict)
and sandbox_cfg.get("booter") == "cua"
)


def image_compress_args_from_settings(
provider_settings: dict[str, object] | None,
) -> tuple[bool, int, int]:
Expand All @@ -81,13 +103,16 @@ def image_compress_args_from_settings(
Returns:
``(enabled, max_size, quality)``. ``enabled`` controls long-edge
resize only; JPEG conversion always runs at the choke point.
CUA sandbox stills skip resize so pixel tools stay 1:1.
"""
if not isinstance(provider_settings, dict):
return True, IMAGE_COMPRESS_DEFAULT_MAX_SIZE, IMAGE_COMPRESS_DEFAULT_QUALITY

enabled = provider_settings.get("image_compress_enabled", True)
if not isinstance(enabled, bool):
enabled = True
if cua_pixel_mode_from_settings(provider_settings):
enabled = False

raw_options = provider_settings.get("image_compress_options", {})
options = raw_options if isinstance(raw_options, dict) else {}
Expand Down Expand Up @@ -123,6 +148,7 @@ async def _prepare_media(
image_compress_enabled: bool = True,
image_max_size: int = IMAGE_COMPRESS_DEFAULT_MAX_SIZE,
image_quality: int = IMAGE_COMPRESS_DEFAULT_QUALITY,
warn_unresized_images: bool = False,
) -> tuple[list[str], list[ProviderContentBlock], bool]:
"""Resolve allowed media to data URLs and report whether anything was dropped."""
prepared_refs: list[str] = []
Expand All @@ -139,6 +165,7 @@ async def _prepare_media(
image_compress_enabled=image_compress_enabled,
image_max_size=image_max_size,
image_quality=image_quality,
warn_unresized_images=warn_unresized_images,
)

for ref in refs:
Expand Down Expand Up @@ -191,6 +218,7 @@ async def _prepare_image_refs(
image_compress_enabled: bool = True,
image_max_size: int = IMAGE_COMPRESS_DEFAULT_MAX_SIZE,
image_quality: int = IMAGE_COMPRESS_DEFAULT_QUALITY,
warn_unresized_images: bool = False,
) -> tuple[list[str], list[ProviderContentBlock], bool]:
prepared_refs: list[str] = []
blocks: list[ProviderContentBlock] = []
Expand Down Expand Up @@ -223,6 +251,15 @@ async def _prepare_image_refs(
try:
for jpeg_path in jpeg_paths:
jpeg_bytes = Path(jpeg_path).read_bytes()
if (
warn_unresized_images
and _CUA_IMAGE_WARN_BYTES < len(jpeg_bytes) <= max_bytes
):
logger.warning(
"Prepared image is %.1f MB without resize; "
"this may exceed provider image upload limits.",
len(jpeg_bytes) / 1048576,
)
if len(jpeg_bytes) > max_bytes:
logger.warning(
"Drop invalid or oversized image provider media."
Expand Down Expand Up @@ -272,6 +309,7 @@ async def prepare_provider_request(
image_compress_enabled: bool = True,
image_max_size: int = IMAGE_COMPRESS_DEFAULT_MAX_SIZE,
image_quality: int = IMAGE_COMPRESS_DEFAULT_QUALITY,
warn_unresized_images: bool = False,
) -> ProviderRequest:
"""Return a sanitized, normalized copy suitable for a provider request.

Expand Down Expand Up @@ -301,6 +339,7 @@ async def prepare_provider_request(
image_compress_enabled=image_compress_enabled,
image_max_size=image_max_size,
image_quality=image_quality,
warn_unresized_images=warn_unresized_images,
)
audio_refs, audio_blocks, audio_dropped = await _prepare_media(
prepared_request.audio_urls,
Expand Down
4 changes: 4 additions & 0 deletions astrbot/core/astr_main_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from astrbot.core.agent.message import TextPart
from astrbot.core.agent.request_preparation import (
clone_provider_request,
cua_pixel_mode_from_settings,
image_compress_args_from_settings,
prepare_provider_request,
)
Expand Down Expand Up @@ -2175,6 +2176,9 @@ async def build_main_agent(
image_compress_enabled=compress_enabled,
image_max_size=image_max_size,
image_quality=image_quality,
warn_unresized_images=cua_pixel_mode_from_settings(
config.provider_settings
),
)
event.set_extra("provider_request", req)

Expand Down
Loading
Loading