* fix(slack): route DM thread replies to main session instead of thread-scoped session
DM thread replies (user replies inside a thread under a bot message in a
DM) were routed to a thread-specific session key instead of the user's
main DM session. This caused the agent to never receive the inbound on
the expected session, making the bot appear unresponsive.
The root cause was in prepare-routing.ts: canonicalThreadId for
isDirectMessage was set to threadTs when isThreadReply was true, creating
a session key like agent:main:slack:direct:u3🧵<ts>. DM threads
are a UI affordance — not a session boundary — so all DM messages should
route to the main DM session regardless of thread_ts.
Also adds a diagnostic logVerbose warning when assistant_app_thread
message_changed events fail sender resolution (Case 2 of #82390),
which was previously completely silent.
Fixes#82390
* chore(slack): polish DM thread routing PR
* test(slack): update DM thread routing contract
* test(slack): flatten non-main DM thread expectations
* fix(slack): preserve bound DM thread routes
* test(slack): align DM thread session fixtures
* fix(slack): keep flattened DM thread metadata scoped
* fix(slack): preserve DM thread delivery routes
---------
Co-authored-by: Peter Steinberger <steipete@gmail.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.