@openclaw/mattermost 2026.7.1-beta.6 → 2026.7.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
2
  import { resolveChannelPreviewStreamMode, resolveChannelStreamingBlockCoalesce, resolveChannelStreamingBlockEnabled, resolveChannelStreamingChunkMode } from "openclaw/plugin-sdk/channel-outbound";
3
3
  import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
4
+ import { buildTimeoutAbortSignal } from "openclaw/plugin-sdk/extension-shared";
4
5
  import { fetchWithSsrFGuard, ssrfPolicyFromPrivateNetworkOptIn } from "openclaw/plugin-sdk/ssrf-runtime";
5
6
  import { createAccountListHelpers, hasConfiguredAccountValue } from "openclaw/plugin-sdk/account-helpers";
6
7
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
@@ -8,10 +9,11 @@ import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resoluti
8
9
  import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
9
10
  import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
10
11
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
11
- import { sleep } from "openclaw/plugin-sdk/runtime-env";
12
+ import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
12
13
  import { z } from "zod";
13
14
  //#region extensions/mattermost/src/mattermost/client.ts
14
15
  const MATTERMOST_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
16
+ const MATTERMOST_REQUEST_TIMEOUT_MS = 3e4;
15
17
  const MATTERMOST_TEXT_RESPONSE_LIMIT_BYTES = 64 * 1024;
16
18
  const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([
17
19
  101,
@@ -103,17 +105,42 @@ function createMattermostClient(params) {
103
105
  if (!baseUrl) throw new Error("Mattermost baseUrl is required");
104
106
  const apiBaseUrl = `${baseUrl}/api/v4`;
105
107
  const token = params.botToken.trim();
108
+ const requestTimeoutMs = resolveTimerTimeoutMs(params.timeoutMs, MATTERMOST_REQUEST_TIMEOUT_MS);
106
109
  const externalFetchImpl = params.fetchImpl;
107
110
  const guardedFetchImpl = async (input, init) => {
111
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
112
+ const { timeoutMs: initTimeoutMs, ...requestInit } = init ?? {};
113
+ const timeoutMs = resolveTimerTimeoutMs(initTimeoutMs, requestTimeoutMs);
108
114
  const { response, release } = await fetchWithSsrFGuard({
109
- url: typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url,
110
- init,
115
+ url,
116
+ init: requestInit,
111
117
  auditContext: "mattermost-api",
112
- policy: ssrfPolicyFromPrivateNetworkOptIn(params.allowPrivateNetwork)
118
+ policy: ssrfPolicyFromPrivateNetworkOptIn(params.allowPrivateNetwork),
119
+ signal: requestInit.signal ?? void 0,
120
+ timeoutMs
113
121
  });
114
122
  return responseWithRelease(response, release);
115
123
  };
116
- const fetchImpl = externalFetchImpl ?? guardedFetchImpl;
124
+ const fetchImpl = (externalFetchImpl ? async (input, init) => {
125
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
126
+ const { timeoutMs: initTimeoutMs, ...requestInit } = init ?? {};
127
+ const { signal: timeoutSignal, cleanup } = buildTimeoutAbortSignal({
128
+ timeoutMs: resolveTimerTimeoutMs(initTimeoutMs, requestTimeoutMs),
129
+ operation: "mattermost-api",
130
+ url
131
+ });
132
+ const callerSignal = requestInit.signal ?? void 0;
133
+ const signal = callerSignal && timeoutSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : callerSignal ?? timeoutSignal;
134
+ try {
135
+ return responseWithRelease(await externalFetchImpl(input, {
136
+ ...requestInit,
137
+ signal
138
+ }), async () => cleanup());
139
+ } catch (error) {
140
+ cleanup();
141
+ throw error;
142
+ }
143
+ } : void 0) ?? guardedFetchImpl;
117
144
  const request = async (path, init) => {
118
145
  const url = buildMattermostApiUrl(baseUrl, path);
119
146
  const headers = new Headers(init?.headers);
@@ -163,11 +190,12 @@ async function sendMattermostTyping(client, params) {
163
190
  body: JSON.stringify(payload)
164
191
  });
165
192
  }
166
- async function createMattermostDirectChannel(client, userIds, signal) {
193
+ async function createMattermostDirectChannel(client, userIds, signal, timeoutMs) {
167
194
  return await client.request("/channels/direct", {
168
195
  method: "POST",
169
196
  body: JSON.stringify(userIds),
170
- signal
197
+ signal,
198
+ timeoutMs
171
199
  });
172
200
  }
173
201
  const DM_REPLY_DELIVERY_BARRIER_SLACK_MS = 6e4;
@@ -229,26 +257,24 @@ const RETRYABLE_NETWORK_MESSAGE_SNIPPETS = [
229
257
  async function createMattermostDirectChannelWithRetry(client, userIds, options = {}) {
230
258
  const { maxRetries = 3, initialDelayMs = 1e3, maxDelayMs = 1e4, timeoutMs: rawTimeoutMs = 3e4, onRetry } = options;
231
259
  const timeoutMs = resolveTimerTimeoutMs(rawTimeoutMs, 3e4);
232
- let lastError;
233
- for (let attempt = 0; attempt <= maxRetries; attempt++) try {
260
+ return await retryAsync(async () => {
234
261
  const controller = new AbortController();
235
262
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
236
263
  try {
237
- return await createMattermostDirectChannel(client, userIds, controller.signal);
264
+ return await createMattermostDirectChannel(client, userIds, controller.signal, timeoutMs);
265
+ } catch (err) {
266
+ throw err instanceof Error ? err : new Error(String(err));
238
267
  } finally {
239
268
  clearTimeout(timeoutId);
240
269
  }
241
- } catch (err) {
242
- lastError = err instanceof Error ? err : new Error(String(err));
243
- if (attempt >= maxRetries) break;
244
- if (!isRetryableError(lastError)) throw lastError;
245
- const exponentialDelay = initialDelayMs * 2 ** attempt;
246
- const jitter = Math.random() * exponentialDelay;
247
- const delayMs = Math.min(exponentialDelay + jitter, maxDelayMs);
248
- if (onRetry) onRetry(attempt + 1, delayMs, lastError);
249
- await sleep(delayMs);
250
- }
251
- throw lastError ?? /* @__PURE__ */ new Error("Failed to create DM channel after retries");
270
+ }, {
271
+ attempts: maxRetries + 1,
272
+ minDelayMs: Math.min(initialDelayMs, maxDelayMs),
273
+ maxDelayMs,
274
+ jitter: "full",
275
+ shouldRetry: (err) => isRetryableError(err),
276
+ onRetry: (info) => onRetry?.(info.attempt, info.delayMs, info.err)
277
+ });
252
278
  }
253
279
  function isRetryableError(error) {
254
280
  const candidates = collectErrorCandidates(error);
@@ -258,7 +284,9 @@ function isRetryableError(error) {
258
284
  for (const message of messages) {
259
285
  const clientErrorMatch = message.match(/mattermost api (4\d{2})\b/);
260
286
  if (!clientErrorMatch) continue;
261
- const statusCode = Number.parseInt(clientErrorMatch[1], 10);
287
+ const statusCodeText = clientErrorMatch[1];
288
+ if (!statusCodeText) continue;
289
+ const statusCode = Number.parseInt(statusCodeText, 10);
262
290
  if (statusCode >= 400 && statusCode < 500) return false;
263
291
  }
264
292
  if (messages.some((message) => /mattermost api \d{3}\b/.test(message))) return false;
@@ -418,17 +446,19 @@ function resolveMattermostAccount(params) {
418
446
  oncharPrefixes: merged.oncharPrefixes,
419
447
  requireMention,
420
448
  textChunkLimit: merged.textChunkLimit,
421
- chunkMode: resolveChannelStreamingChunkMode(merged) ?? merged.chunkMode,
449
+ chunkMode: resolveChannelStreamingChunkMode(merged),
422
450
  streamingMode: resolveChannelPreviewStreamMode(merged, "partial"),
423
- blockStreaming: resolveChannelStreamingBlockEnabled(merged) ?? merged.blockStreaming,
424
- blockStreamingCoalesce: resolveChannelStreamingBlockCoalesce(merged) ?? merged.blockStreamingCoalesce
451
+ blockStreaming: resolveChannelStreamingBlockEnabled(merged),
452
+ blockStreamingCoalesce: resolveChannelStreamingBlockCoalesce(merged)
425
453
  };
426
454
  }
427
455
  /**
428
456
  * Resolve the effective replyToMode for a given chat type.
429
- * Mattermost auto-threading only applies to channel and group messages.
457
+ * Direct messages stay flat unless explicitly opted into a per-chat-type mode.
430
458
  */
431
459
  function resolveMattermostReplyToMode(account, kind) {
460
+ const scopedMode = account.config.replyToModeByChatType?.[kind];
461
+ if (scopedMode !== void 0) return scopedMode;
432
462
  if (kind === "direct") return "off";
433
463
  return account.config.replyToMode ?? "off";
434
464
  }
@@ -1,5 +1,5 @@
1
- import { a as describeMattermostAccount, c as mattermostMeta, i as MattermostChannelConfigSchema, n as mattermostSetupWizard, o as isMattermostConfigured, r as mattermostSetupAdapter, s as mattermostConfigAdapter, t as mattermostPlugin } from "./channel-plugin-runtime-DhS8rwfs.js";
2
- import { t as resolveMattermostGatewayAuthBypassPaths } from "./gateway-auth-bypass-BIXLORHU.js";
1
+ import { a as describeMattermostAccount, c as mattermostMeta, i as MattermostChannelConfigSchema, n as mattermostSetupWizard, o as isMattermostConfigured, r as mattermostSetupAdapter, s as mattermostConfigAdapter, t as mattermostPlugin } from "./channel-plugin-runtime-BkzZgYxP.js";
2
+ import { t as resolveMattermostGatewayAuthBypassPaths } from "./gateway-auth-bypass-CL1PxV3z.js";
3
3
  //#region extensions/mattermost/src/channel.setup.ts
4
4
  const mattermostSetupPlugin = {
5
5
  id: "mattermost",
@@ -23,7 +23,7 @@ const mattermostSetupPlugin = {
23
23
  isConfigured: isMattermostConfigured,
24
24
  describeAccount: describeMattermostAccount
25
25
  },
26
- gateway: { resolveGatewayAuthBypassPaths: ({ cfg }) => resolveMattermostGatewayAuthBypassPaths(cfg) },
26
+ gateway: { resolveGatewayAuthBypassPaths: resolveMattermostGatewayAuthBypassPaths },
27
27
  setup: mattermostSetupAdapter,
28
28
  setupWizard: mattermostSetupWizard
29
29
  };
@@ -1,29 +1,29 @@
1
- import { C as hasConfiguredSecretInput, S as buildSecretInputSchema, g as normalizeMattermostBaseUrl, i as resolveMattermostReplyToMode, n as resolveDefaultMattermostAccountId, r as resolveMattermostAccount, t as listMattermostAccountIds } from "./accounts-ITTlduDO.js";
2
- import { t as resolveMattermostGatewayAuthBypassPaths } from "./gateway-auth-bypass-BIXLORHU.js";
3
- import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-ttH0DCuq.js";
4
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Cx0LUNXy.js";
1
+ import { C as hasConfiguredSecretInput, S as buildSecretInputSchema, g as normalizeMattermostBaseUrl, i as resolveMattermostReplyToMode, n as resolveDefaultMattermostAccountId, r as resolveMattermostAccount, t as listMattermostAccountIds } from "./accounts-B2NRPlqr.js";
2
+ import { t as resolveMattermostGatewayAuthBypassPaths } from "./gateway-auth-bypass-CL1PxV3z.js";
3
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-BaUvVh8e.js";
4
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-B4R5Bmhv.js";
5
5
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
7
7
  import { createLoggedPairingApprovalNotifier } from "openclaw/plugin-sdk/channel-pairing";
8
8
  import { createAccountStatusSink, createChannelMessageAdapterFromOutbound } from "openclaw/plugin-sdk/channel-outbound";
9
9
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, buildChannelOutboundSessionRoute, buildThreadAwareOutboundSessionRoute, stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
10
- import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
11
- import { createDangerousNameMatchingMutableAllowlistWarningCollector, createRestrictSendersChannelSecurity, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
10
+ import { createChannelConfigUiHints, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
11
+ import { buildChannelGroupsScopeTree, createDangerousNameMatchingMutableAllowlistWarningCollector, createRestrictSendersChannelSecurity, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
12
12
  import { attachChannelToResult, createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
13
13
  import { createChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
14
14
  import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
15
- import { normalizeMessagePresentation, renderMessagePresentationFallbackText, resolveMessagePresentationControlValue } from "openclaw/plugin-sdk/interactive-runtime";
15
+ import { normalizeMessagePresentation, renderMessagePresentationFallbackText, resolveMessagePresentationButtonAction, resolveMessagePresentationControlValue } from "openclaw/plugin-sdk/interactive-runtime";
16
16
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
17
17
  import { resolvePayloadMediaUrls, sendTextMediaPayload } from "openclaw/plugin-sdk/reply-payload";
18
18
  import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";
19
19
  import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
20
20
  import { chunkTextForOutbound, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
21
- import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
21
+ import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
22
22
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
23
23
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
24
24
  import { z } from "zod";
25
25
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
26
- import { BlockStreamingCoalesceSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, buildChannelConfigSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-primitives";
26
+ import { BlockStreamingCoalesceSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, buildChannelConfigSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
27
27
  import { applyAccountNameToChannelSection, applySetupAccountConfigPatch, createSetupTranslator, createStandardChannelSetupStatus, formatDocsLink, migrateBaseNameToDefaultAccount } from "openclaw/plugin-sdk/setup";
28
28
  import { createSetupInputPresenceValidator } from "openclaw/plugin-sdk/setup-runtime";
29
29
  //#region extensions/mattermost/src/approval-auth.ts
@@ -32,20 +32,16 @@ function normalizeMattermostApproverId(value) {
32
32
  const lowered = normalizeLowercaseStringOrEmpty(String(value).trim().replace(/^(mattermost|user):/i, "").replace(/^@/, "").trim());
33
33
  return MATTERMOST_USER_ID_RE.test(lowered) ? lowered : void 0;
34
34
  }
35
- const mattermostApprovalAuth = createResolvedApproverActionAuthAdapter({
35
+ const mattermostApprovalAuth = createChannelApprovalAuth({
36
36
  channelLabel: "Mattermost",
37
- resolveApprovers: ({ cfg, accountId }) => {
38
- const account = resolveMattermostAccount({
37
+ resolveInputs: ({ cfg, accountId }) => {
38
+ return { allowFrom: resolveMattermostAccount({
39
39
  cfg,
40
40
  accountId
41
- }).config;
42
- return resolveApprovalApprovers({
43
- allowFrom: account.allowFrom,
44
- normalizeApprover: normalizeMattermostApproverId
45
- });
41
+ }).config.allowFrom };
46
42
  },
47
- normalizeSenderId: (value) => normalizeMattermostApproverId(value)
48
- });
43
+ normalizeApprover: normalizeMattermostApproverId
44
+ }).approvalAuth;
49
45
  //#endregion
50
46
  //#region extensions/mattermost/src/channel-config-shared.ts
51
47
  const mattermostMeta = {
@@ -162,17 +158,24 @@ const MattermostStreamingBlockSchema = z.object({
162
158
  enabled: z.boolean().optional(),
163
159
  coalesce: BlockStreamingCoalesceSchema.optional()
164
160
  }).strict();
165
- const MattermostStreamingSchema = z.union([
166
- MattermostStreamingModeSchema,
167
- z.boolean(),
168
- z.object({
169
- mode: MattermostStreamingModeSchema.optional(),
170
- chunkMode: z.enum(["length", "newline"]).optional(),
171
- preview: MattermostStreamingPreviewSchema.optional(),
172
- progress: MattermostStreamingProgressSchema.optional(),
173
- block: MattermostStreamingBlockSchema.optional()
174
- }).strict()
161
+ const MattermostStreamingSchema = z.object({
162
+ mode: MattermostStreamingModeSchema.optional(),
163
+ chunkMode: z.enum(["length", "newline"]).optional(),
164
+ preview: MattermostStreamingPreviewSchema.optional(),
165
+ progress: MattermostStreamingProgressSchema.optional(),
166
+ block: MattermostStreamingBlockSchema.optional()
167
+ }).strict();
168
+ const MattermostReplyToModeSchema = z.enum([
169
+ "off",
170
+ "first",
171
+ "all",
172
+ "batched"
175
173
  ]);
174
+ const MattermostReplyToModeByChatTypeSchema = z.object({
175
+ direct: MattermostReplyToModeSchema.optional(),
176
+ group: MattermostReplyToModeSchema.optional(),
177
+ channel: MattermostReplyToModeSchema.optional()
178
+ }).strict();
176
179
  const MattermostAccountSchemaBase = z.object({
177
180
  name: z.string().optional(),
178
181
  capabilities: z.array(z.string()).optional(),
@@ -194,16 +197,9 @@ const MattermostAccountSchemaBase = z.object({
194
197
  groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
195
198
  groupPolicy: GroupPolicySchema.optional().default("allowlist"),
196
199
  textChunkLimit: z.number().int().positive().optional(),
197
- chunkMode: z.enum(["length", "newline"]).optional(),
198
200
  streaming: MattermostStreamingSchema.optional(),
199
- blockStreaming: z.boolean().optional(),
200
- blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
201
- replyToMode: z.enum([
202
- "off",
203
- "first",
204
- "all",
205
- "batched"
206
- ]).optional(),
201
+ replyToMode: MattermostReplyToModeSchema.optional(),
202
+ replyToModeByChatType: MattermostReplyToModeByChatTypeSchema.optional(),
207
203
  responsePrefix: z.string().optional(),
208
204
  actions: z.object({ reactions: z.boolean().optional() }).optional(),
209
205
  commands: MattermostSlashCommandsSchema,
@@ -241,10 +237,10 @@ const MattermostChannelConfigSchema = buildChannelConfigSchema(MattermostAccount
241
237
  label: "Mattermost",
242
238
  help: "Mattermost channel provider configuration for bot auth, access policy, slash commands, and preview streaming."
243
239
  },
244
- dmPolicy: {
245
- label: "Mattermost DM Policy",
246
- help: "Direct message access control (\"pairing\" recommended). \"open\" requires channels.mattermost.allowFrom=[\"*\"]."
247
- },
240
+ ...createChannelConfigUiHints({
241
+ channelLabel: "Mattermost",
242
+ dmPolicy: { channelKey: "mattermost" }
243
+ }),
248
244
  streaming: {
249
245
  label: "Mattermost Streaming Mode",
250
246
  help: "Unified Mattermost stream preview mode: \"off\" | \"partial\" | \"block\" | \"progress\". \"progress\" keeps a single editable progress draft until final delivery."
@@ -253,30 +249,10 @@ const MattermostChannelConfigSchema = buildChannelConfigSchema(MattermostAccount
253
249
  label: "Mattermost Streaming Mode",
254
250
  help: "Canonical Mattermost preview mode: \"off\" | \"partial\" | \"block\" | \"progress\"."
255
251
  },
256
- "streaming.progress.label": {
257
- label: "Mattermost Progress Label",
258
- help: "Initial progress draft title. Use \"auto\" for built-in single-word labels, a custom string, or false to hide the title."
259
- },
260
- "streaming.progress.labels": {
261
- label: "Mattermost Progress Label Pool",
262
- help: "Candidate labels for streaming.progress.label=\"auto\". Leave unset to use OpenClaw built-in progress labels."
263
- },
264
- "streaming.progress.maxLines": {
265
- label: "Mattermost Progress Max Lines",
266
- help: "Maximum number of compact progress lines to keep below the draft label (default: 8)."
267
- },
268
- "streaming.progress.maxLineChars": {
269
- label: "Mattermost Progress Max Line Chars",
270
- help: "Maximum characters per compact progress line before truncation (default: 120). Prose cuts at word boundaries; commands and paths keep useful suffixes."
271
- },
272
- "streaming.progress.toolProgress": {
273
- label: "Mattermost Progress Tool Lines",
274
- help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery."
275
- },
276
- "streaming.progress.commandText": {
277
- label: "Mattermost Progress Command Text",
278
- help: "Command/exec detail in progress draft lines: \"raw\" preserves released behavior; \"status\" shows only the tool label."
279
- },
252
+ ...createChannelConfigUiHints({
253
+ channelLabel: "Mattermost",
254
+ progress: {}
255
+ }),
280
256
  "streaming.preview.toolProgress": {
281
257
  label: "Mattermost Draft Tool Progress",
282
258
  help: "Show tool/progress activity in the live draft preview post (default: true). Set false to hide interim tool updates while the draft preview stays active."
@@ -325,13 +301,11 @@ function resolveMattermostGroupRequireMention(params) {
325
301
  cfg: params.cfg,
326
302
  accountId: params.accountId
327
303
  });
328
- const requireMentionOverride = typeof params.requireMentionOverride === "boolean" ? params.requireMentionOverride : account.requireMention;
329
- return resolveChannelGroupRequireMention({
330
- cfg: params.cfg,
331
- channel: "mattermost",
332
- groupId: params.groupId,
333
- accountId: params.accountId,
334
- requireMentionOverride
304
+ return resolveScopeRequireMention({
305
+ tree: buildChannelGroupsScopeTree(params.cfg, "mattermost", params.accountId),
306
+ path: params.groupId ? [params.groupId] : [],
307
+ requireMentionOverride: params.requireMentionOverride ?? account.requireMention,
308
+ overrideOrder: "after-config"
335
309
  });
336
310
  }
337
311
  //#endregion
@@ -568,7 +542,7 @@ const mattermostSetupWizard = {
568
542
  };
569
543
  //#endregion
570
544
  //#region extensions/mattermost/src/channel.ts
571
- const loadMattermostChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-D0SJSEei.js"));
545
+ const loadMattermostChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-Cbs451W2.js"));
572
546
  function buildMattermostPresentationButtons(presentation) {
573
547
  return presentation.blocks.filter((block) => block.type === "buttons").map((block) => block.buttons.flatMap((button) => {
574
548
  if (button.action) return [];
@@ -590,8 +564,11 @@ const MATTERMOST_PRESENTATION_CAPABILITIES = {
590
564
  divider: false,
591
565
  limits: { text: { markdownDialect: "markdown" } }
592
566
  };
593
- function hasMattermostPresentationButtons(presentation) {
594
- return buildMattermostPresentationButtons(presentation).some((row) => row.length > 0);
567
+ function hasMattermostPresentationNavigation(presentation) {
568
+ return presentation.blocks.some((block) => block.type === "buttons" && block.buttons.some((button) => {
569
+ const action = resolveMessagePresentationButtonAction(button);
570
+ return action?.type === "url" || action?.type === "web-app";
571
+ }));
595
572
  }
596
573
  function readMattermostPresentationButtons(payload) {
597
574
  const buttons = (payload.channelData?.mattermost)?.presentationButtons;
@@ -902,20 +879,21 @@ const mattermostOutbound = {
902
879
  renderPresentation: ({ payload, presentation }) => {
903
880
  if (payload.mediaUrls && payload.mediaUrls.length > 1) return null;
904
881
  const buttons = buildMattermostPresentationButtons(presentation);
905
- if (!hasMattermostPresentationButtons(presentation)) return null;
882
+ const hasButtons = buttons.some((row) => row.length > 0);
883
+ if (!hasButtons && !hasMattermostPresentationNavigation(presentation)) return null;
906
884
  return {
907
885
  ...payload,
908
886
  text: renderMessagePresentationFallbackText({
909
887
  text: payload.text,
910
888
  presentation
911
889
  }),
912
- channelData: {
890
+ ...hasButtons ? { channelData: {
913
891
  ...payload.channelData,
914
892
  mattermost: {
915
893
  ...payload.channelData?.mattermost,
916
894
  presentationButtons: buttons
917
895
  }
918
- }
896
+ } } : {}
919
897
  };
920
898
  },
921
899
  sendPayload: async (ctx) => {
@@ -1100,7 +1078,7 @@ const mattermostPlugin = createChatChannelPlugin({
1100
1078
  })
1101
1079
  }),
1102
1080
  gateway: {
1103
- resolveGatewayAuthBypassPaths: ({ cfg }) => resolveMattermostGatewayAuthBypassPaths(cfg),
1081
+ resolveGatewayAuthBypassPaths: resolveMattermostGatewayAuthBypassPaths,
1104
1082
  startAccount: async (ctx) => {
1105
1083
  const account = ctx.account;
1106
1084
  const statusSink = createAccountStatusSink({
@@ -1150,9 +1128,10 @@ const mattermostPlugin = createChatChannelPlugin({
1150
1128
  }),
1151
1129
  resolveReplyTransport: ({ threadId, replyToId, replyToIsExplicit, replyDelivery }) => {
1152
1130
  const ambientThreadId = threadId != null ? String(threadId) : void 0;
1153
- const resolvedThreadId = replyDelivery?.chatType === "direct" ? void 0 : replyDelivery ? replyToIsExplicit ? replyToId ?? ambientThreadId : ambientThreadId ?? replyToId ?? void 0 : ambientThreadId ?? replyToId;
1131
+ const isFlatDirect = replyDelivery?.chatType === "direct" && replyDelivery.replyToMode === "off";
1132
+ const resolvedThreadId = isFlatDirect ? void 0 : replyDelivery ? replyToIsExplicit ? replyToId ?? ambientThreadId : ambientThreadId ?? replyToId ?? void 0 : ambientThreadId ?? replyToId;
1154
1133
  return {
1155
- replyToId: replyDelivery?.chatType === "direct" ? null : resolvedThreadId,
1134
+ replyToId: isFlatDirect ? null : resolvedThreadId,
1156
1135
  threadId: resolvedThreadId ?? null
1157
1136
  };
1158
1137
  }
@@ -1,2 +1,2 @@
1
- import { t as mattermostPlugin } from "./channel-plugin-runtime-DhS8rwfs.js";
1
+ import { t as mattermostPlugin } from "./channel-plugin-runtime-BkzZgYxP.js";
2
2
  export { mattermostPlugin };