Defense-in-depth safety net for #81628: even with the cron-tool fix in
place, any other code path that ever produces a 33-char LINE-shaped
recipient missing its leading capital (C/U/R) would otherwise hit the
LINE API and return HTTP 400 with no permanent-error signal, causing
delivery-recovery to retry five times before moving the entry to
failed/.
normalizeTarget now throws "Recipient is not a valid LINE id ..." when
the post-strip value looks like a LINE id but the case was lost. The
message matches the existing /recipient is not a valid/i pattern in
delivery-queue-recovery's PERMANENT_ERROR_PATTERNS, so recovery moves
the entry to failed/ on the first attempt instead of silently retrying.
Short fixtures (length < 33) are left alone so existing tests using
"U123", "line:user:1", etc. keep working.
Fix Codex app-server turns that go quiet after the last non-assistant current-turn item completes without turn/completed.\n\nMaintainer proof: focused watchdog regression passed on rebased head, diff whitespace check passed, and local OpenClaw CLI + WebSocket app-server transport proof observed turn/interrupt after the short idle watchdog.\n\nCo-authored-by: funmerlin <funmerlin@users.noreply.github.com>
Route Discord and Slack prepared message turns through the core prepared-turn runner directly.
Local proof before landing:
- node scripts/run-vitest.mjs src/channels/turn/kernel.test.ts extensions/discord/src/monitor/message-handler.process.test.ts extensions/slack/src/monitor/message-handler/prepare.test.ts extensions/slack/src/monitor/message-handler/dispatch.preview-fallback.test.ts
- node scripts/run-tsgo.mjs -p tsconfig.core.json --incremental false
- node scripts/run-tsgo.mjs -p tsconfig.extensions.json --incremental false
- OPENCLAW_TESTBOX_REMOTE_RUN=1 OPENCLAW_VITEST_MAX_WORKERS=1 pnpm check:changed
- codex-review clean after accepted Slack bot-loop history cleanup finding was fixed in core
GitHub checks had no failures; Blacksmith/GitHub runner jobs were still queued when maintainer approved landing based on local proof.
Slack link unfurls (inline message previews) are enabled by default
when unfurl_links is not explicitly set in chat.postMessage. This means
bot messages containing Slack message links or URLs automatically expand
into rich preview cards, which can be noisy in channels.
Default unfurl_links to false so outbound messages don't show inline
link previews unless the operator explicitly opts in via:
channels.slack.unfurlLinks: true
unfurlMedia remains opt-in (only sent when explicitly configured).
* feat: attach recent inbound history images
* fix: bound recent history media downloads
* fix: preserve sticker history media
* fix: enforce history media cap for stickers
* refactor: name agent turn attachments generically
* refactor: share pending history media recording
* fix: gate historical media attachment visibility
* fix: avoid media runtime on text-only turns
* fix: preserve fallback history media selection
* fix: avoid sparse media history index collisions
* fix: skip history images for current non-image media
* test: import history media type directly
* test: satisfy agent media runtime mock lint
* fix: respect mocked Slack media fetches
* fix: settle history media recording races
Keep queued system-event owner downgrades as structured runtime metadata while rendering the model-visible prompt as plain `System:` lines.
This preserves least-privilege wakeups for webhook/node/exec/cron/reaction/hook producers, keeps legacy `trusted: false` compatibility for installed plugins and older hosts, and updates representative gateway, agent, cron, plugin, and OpenGrep coverage.