Fixes#62571. `resolveSlackThreadStarter` and `resolveSlackThreadHistory`
in `extensions/slack/src/monitor/media.ts` swallowed ALL errors with bare
`catch {}` blocks — auth failures, rate-limit rejections, scope errors,
and network blips all mapped to the same silent `null` / `[]` fallback.
Operators had no way to distinguish "genuinely empty thread" from
"Slack rejected our call".
Replaces both bare catches with `logVerbose` calls that include the
channel, thread ts, and error message. Behavior is preserved — callers
still receive `null` / `[]` — but the failure reason now shows up in
verbose logs, matching the pattern already used elsewhere in the Slack
extension (see `monitor/context.ts:285`, `send.ts:140`, `actions.ts:49`).
Testing:
- New `describe("resolveSlackThreadStarter", ...)` block with 4 tests
(previously uncovered): success path, empty-text skip, Error throw
surfaces via logVerbose with channel/ts/reason, non-Error throw value
surfaces via String(err).
- Existing `resolveSlackThreadHistory` throws test upgraded to assert
the logVerbose call with channel/ts/reason.
- `pnpm vitest run extensions/slack/src/monitor/media.test.ts` → 35
passed (31 previous + 4 new).
* Refine plugin debug plumbing
* Tighten plugin debug handling
* Reduce active memory overhead
* Abort active memory sidecar on timeout
* Rename active memory blocking subagent wording
* Fix active memory cache and recall selection
* Preserve active memory session scope
* Sanitize recalled context before retrieval
* Add active memory changelog entry
* Harden active memory debug and transcript handling
* Add active memory policy config
* Raise active memory timeout default
* Keep usage footer on primary reply
* Clear stale active memory status lines
* Match legacy active memory status prefixes
* Preserve numeric active memory bullets
* Reuse canonical session keys for active memory
* Let active memory subagent decide relevance
* Refine active memory plugin summary flow
* Fix active memory main-session DM detection
* Trim active memory summaries at word boundaries
* Add active memory prompt styles
* Fix active memory stale status cleanup
* Rename active memory subagent wording
* Add active memory prompt and thinking overrides
* Remove active memory legacy status compat
* Resolve active memory session id status
* Add active memory session toggle
* Add active memory global toggle
* Fix active memory toggle state handling
* Harden active memory transcript persistence
* Fix active memory chat type gating
* Scope active memory transcripts by agent
* Show plugin debug before replies
Scope Slack turn-local delivery dedupe by reply dispatch kind so identical tool and final payloads on the same thread do not collapse into one send.
Expose the existing dispatcher kind on the public reply-runtime seam and cover the Slack tracker and preview-fallback paths with regression tests.
- Verified: pnpm build\n- Verified: pnpm test extensions/slack/src/monitor/media.test.ts\n- Verified: pnpm exec oxlint extensions/slack/src/monitor/media.ts extensions/slack/src/monitor/media.test.ts\n- Verified: pnpm exec oxfmt --check extensions/slack/src/monitor/media.ts extensions/slack/src/monitor/media.test.ts CHANGELOG.md\n\nRepo-wide pnpm lint and pnpm test were not clean on current main outside this fix, and the first full-suite test attempt from the default core sparse profile was additionally contaminated by missing ui/packages/OpenClawKit paths until they were materialized.
Closes#62088
When `buildActionOpts` returns undefined (default account, no token
override), `downloadSlackFile` calls `resolveToken(undefined, undefined)`
which re-reads raw config via `loadConfig()`. If botToken is a SecretRef
object, `normalizeResolvedSecretInputString` rejects it because it
expects a string — the download silently fails.
This injects the already-resolved botToken from the gateway runtime
snapshot into the download opts as a fallback, bypassing the raw config
re-read. Same root cause as the Discord fix in b51214ec3e.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>