* feat(mattermost): add opt-in DM threading via dmReplyToMode
Resolves#93203.
Mattermost direct messages are hardcoded non-threaded: resolveMattermostReplyToMode
returns "off" for direct chats before consulting config, and the reply-root /
effective-reply resolvers short-circuit for direct chats. So every DM accretes into
one ever-growing session with unbounded per-session token usage, with no way to
isolate each DM topic the way groups/channels (and Slack) can.
Add channels.mattermost.dmReplyToMode (same enum as replyToMode), default "off" so
existing flat-DM deployments are unchanged. When set to "first"/"all", a DM @mention
starts an independent, thread-scoped session:
- resolveMattermostReplyToMode: direct chats return config.dmReplyToMode ?? "off".
- resolveMattermostEffectiveReplyToId: direct early-return gated on replyToMode "off"
(the value already reflects dmReplyToMode for direct chats).
- resolveMattermostReplyRootId: direct early-return gated on the absence of a thread
root (a DM carries one only when DM threading is enabled).
- New DM threads start fresh (no parent-session inheritance) so each topic is isolated;
a threaded DM keeps its own history key while a flat DM root keeps none.
Default-off preserves the documented flat-DM contract, so this changes no existing
behavior. Adds unit coverage and documents the opt-in. The only open question is the
knob shape (dmReplyToMode enum vs a boolean dmThreading) — happy to adjust.
Refs #65729.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(mattermost): register dmReplyToMode in config schema
dmReplyToMode was wired into the account config type and reply
resolution but never added to MattermostConfigSchema, so setting it in
openclaw.json failed config validation ("must not have additional
properties: dmReplyToMode") and the gateway refused to start — the knob
could not actually be enabled. Register it in the schema, regenerate the
bundled channel config metadata and doc baseline, and add a
config-schema test.
* fix(mattermost): thread direct reply transport when dmReplyToMode is on
resolveReplyTransport nulled the direct reply root/thread id
unconditionally, so routed replies and message-tool follow-ups in an
opted-in DM thread were still sent flat even when the effective
replyToMode was first/all. Keep direct transport flat only when the mode
is off (the historical contract) and preserve the thread root otherwise,
matching the monitor-level DM threading gates. Adds channel.test.ts
coverage for resolveReplyTransport.
* fix(mattermost): use per-chat reply mode overrides
* fix(mattermost): bound direct thread history
* chore(config): refresh docs baseline
---------
Co-authored-by: leon <leon@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(skills): keep command spec descriptions UTF-16 safe at the truncation cut
* chore: fix oxlint no-unnecessary-type-assertion in test
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: fix Skill type completeness in test fixture
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(skills): prove UTF-16-safe command truncation
* fix(skills): apply description limits per channel
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- The PR replaces Mattermost draft preview raw UTF-16 slicing with the existing SDK `sliceUtf16Safe` helper and adds a regression test for an emoji that straddles the preview limit.
- PR surface: Source +1, Tests +26. Total +27 across 2 files.
- Reproducibility: yes. Source inspection shows current main raw-slices at `maxChars - 3`; driving `createMatt ... at UTF-16 indices 8-9 reaches the lone-surrogate path, though I did not run tests in this read-only sweep.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(mattermost): truncate draft previews on code-point boundaries
Validation:
- ClawSweeper review passed for head 86d0dd2a06.
- Required merge gates passed before the squash merge.
Prepared head SHA: 86d0dd2a06
Review: https://github.com/openclaw/openclaw/pull/97472#issuecomment-4825788514
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
* fix(mattermost): bound successful REST JSON/text response reads
The Mattermost REST client already bounds error bodies
(readResponseTextLimited) and streams guarded responses without buffering,
but the success path still called `await res.json()` / `await res.text()`,
reading the whole body into memory before parsing. A self-hosted or
compromised Mattermost server can return an arbitrarily large (or
never-terminating, content-length-less) JSON/text body and force the plugin
to buffer it unbounded.
Read successful JSON through the shared readProviderJsonResponse (16 MiB cap,
cancels the stream and throws a bounded error on overflow, same as the
provider HTTP path) and cap non-JSON success bodies with readResponseTextLimited.
uploadMattermostFile's file-info JSON is bounded the same way.
Symmetric follow-up to the #95103 / #95108 response-limit campaign.
AI-assisted.
* fix(mattermost): bound probe success JSON reads
* fix(mattermost): reject oversized success text bodies
* chore(release): close out 2026.6.10 on main
* chore(release): align native app metadata for 2026.6.10
* chore(release): sync Android 2026.6.10 notes
* docs(changelog): preserve 2026.6.9 history
* docs(changelog): preserve 2026.6.9 history
Add oc_queue to DEFAULT_COMMAND_SPECS so the native slash-command registrar exposes /oc_queue, mapped (originalName: queue) to the core /queue directive via the existing trigger-map path. Additive; rides the same registration/callback machinery as the other oc_* commands.
Replace the hardcoded Mattermost-only open-DM config check with a generic,
plugin-agnostic warning driven by a single shared evaluator
(evaluateDmPolicyAllowFromDependency) reused by the Zod refinements and the
CLI validator. Surface warnings at 'config validate' and on config load.
Remove the Mattermost-specific status-issues module now covered generically;
keep the runtime drop-log diagnostic.