@openclaw/mattermost 2026.7.1 → 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.
- package/dist/{accounts-ITTlduDO.js → accounts-B2NRPlqr.js} +56 -26
- package/dist/channel-plugin-api.js +3 -3
- package/dist/{channel-plugin-runtime-DhS8rwfs.js → channel-plugin-runtime-BkzZgYxP.js} +61 -82
- package/dist/channel-plugin-runtime.js +1 -1
- package/dist/{channel.runtime-D0SJSEei.js → channel.runtime-Cbs451W2.js} +499 -278
- package/dist/doctor-contract-BaUvVh8e.js +19 -0
- package/dist/doctor-contract-api.js +1 -1
- package/dist/gateway-auth-api.js +1 -1
- package/dist/{gateway-auth-bypass-BIXLORHU.js → gateway-auth-bypass-CL1PxV3z.js} +3 -2
- package/dist/secret-contract-B4R5Bmhv.js +28 -0
- package/dist/secret-contract-api.js +1 -1
- package/dist/slash-route-api.js +1 -1
- package/dist/{slash-state-BfOSlkmn.js → slash-state-Cr58Xd12.js} +118 -99
- package/npm-shrinkwrap.json +3 -3
- package/openclaw.plugin.json +189 -217
- package/package.json +4 -4
- package/dist/doctor-contract-ttH0DCuq.js +0 -7
- package/dist/secret-contract-Cx0LUNXy.js +0 -44
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { C as resolveChannelMediaMaxBytes, E as warnMissingProviderGroupPolicyFallbackOnce, S as resolveAllowlistProviderRuntimeGroupPolicy, T as resolveDefaultGroupPolicy, _ as logInboundDrop, c as buildAgentMediaPayload, d as createChannelMessageReplyPipeline, f as createChannelPairingController, h as listSkillCommandsForAgents, i as normalizeMattermostAllowEntry, l as buildModelsProviderData, n as formatMattermostDirectMessageDropLog, o as resolveMattermostMonitorInboundAccess, s as DEFAULT_GROUP_HISTORY_LIMIT, t as authorizeMattermostCommandInvocation, u as createChannelHistoryWindow, v as logTypingFailure, x as registerPluginHttpRoute, y as parseTcpPort } from "./monitor-auth-BiDuyvOc.js";
|
|
2
|
-
import { _ as readMattermostError, a as MattermostPostSchema, b as updateMattermostPost, c as createMattermostPost, f as fetchMattermostMe, g as normalizeMattermostBaseUrl, h as fetchMattermostUserTeams, i as resolveMattermostReplyToMode, l as deleteMattermostPost, o as createMattermostClient, p as fetchMattermostUser, r as resolveMattermostAccount, t as listMattermostAccountIds, u as fetchMattermostChannel, y as sendMattermostTyping } from "./accounts-
|
|
2
|
+
import { _ as readMattermostError, a as MattermostPostSchema, b as updateMattermostPost, c as createMattermostPost, f as fetchMattermostMe, g as normalizeMattermostBaseUrl, h as fetchMattermostUserTeams, i as resolveMattermostReplyToMode, l as deleteMattermostPost, o as createMattermostClient, p as fetchMattermostUser, r as resolveMattermostAccount, t as listMattermostAccountIds, u as fetchMattermostChannel, y as sendMattermostTyping } from "./accounts-B2NRPlqr.js";
|
|
3
3
|
import { n as getOptionalMattermostRuntime, t as getMattermostRuntime } from "./runtime-CNB4YGqJ.js";
|
|
4
|
-
import { C as registerSlashCommands, S as isSlashCommandsEnabled, T as resolveSlashCommandConfig, _ as renderMattermostModelsPickerView, a as sendMessageMattermost, b as DEFAULT_COMMAND_SPECS, c as computeInteractionCallbackUrl, d as setInteractionCallbackUrl, f as setInteractionSecret, g as parseMattermostModelPickerContext, h as buildMattermostAllowedModelRefs, l as createMattermostInteractionHandler, m as deliverMattermostReplyPayload, n as deactivateSlashCommands, o as resolveMattermostOpaqueTarget, p as createMattermostReplyDeliveryBarrier, r as getSlashCommandState, s as buildButtonProps, t as activateSlashCommands, u as resolveInteractionCallbackPath, v as renderMattermostProviderPickerView, w as resolveCallbackUrl, x as cleanupSlashCommands, y as resolveMattermostModelPickerCurrentModel } from "./slash-state-
|
|
4
|
+
import { C as registerSlashCommands, S as isSlashCommandsEnabled, T as resolveSlashCommandConfig, _ as renderMattermostModelsPickerView, a as sendMessageMattermost, b as DEFAULT_COMMAND_SPECS, c as computeInteractionCallbackUrl, d as setInteractionCallbackUrl, f as setInteractionSecret, g as parseMattermostModelPickerContext, h as buildMattermostAllowedModelRefs, l as createMattermostInteractionHandler, m as deliverMattermostReplyPayload, n as deactivateSlashCommands, o as resolveMattermostOpaqueTarget, p as createMattermostReplyDeliveryBarrier, r as getSlashCommandState, s as buildButtonProps, t as activateSlashCommands, u as resolveInteractionCallbackPath, v as renderMattermostProviderPickerView, w as resolveCallbackUrl, x as cleanupSlashCommands, y as resolveMattermostModelPickerCurrentModel } from "./slash-state-Cr58Xd12.js";
|
|
5
5
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries, normalizeTrimmedStringList, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import { formatInboundFromLabel, formatInboundMediaUnavailableText } from "openclaw/plugin-sdk/channel-inbound";
|
|
7
7
|
import { buildChannelProgressDraftLineForEntry, createChannelProgressDraftCompositor, createFinalizableDraftLifecycle, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, resolveChannelStreamingPreviewToolProgress } from "openclaw/plugin-sdk/channel-outbound";
|
|
@@ -14,10 +14,12 @@ import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
import { resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
|
16
16
|
import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
17
|
+
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
17
18
|
import { randomUUID } from "node:crypto";
|
|
19
|
+
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
18
20
|
import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
|
|
21
|
+
import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
|
|
19
22
|
import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
20
|
-
import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
21
23
|
import { captureWsEvent, createDebugProxyWebSocketAgent, resolveDebugProxySettings } from "openclaw/plugin-sdk/proxy-capture";
|
|
22
24
|
import WebSocket from "ws";
|
|
23
25
|
import { createPersistentDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
@@ -111,11 +113,14 @@ async function listMattermostDirectoryPeers(params) {
|
|
|
111
113
|
const clients = buildClients(params);
|
|
112
114
|
if (!clients.length) return [];
|
|
113
115
|
const client = clients[0];
|
|
116
|
+
if (!client) return [];
|
|
114
117
|
try {
|
|
115
118
|
const me = await fetchMattermostMe(client);
|
|
116
119
|
const teams = await client.request("/users/me/teams");
|
|
117
120
|
if (!teams.length) return [];
|
|
118
|
-
const
|
|
121
|
+
const team = teams[0];
|
|
122
|
+
if (!team) return [];
|
|
123
|
+
const teamId = team.id;
|
|
119
124
|
const q = normalizeLowercaseStringOrEmpty(params.query);
|
|
120
125
|
let users;
|
|
121
126
|
if (q) users = await client.request("/users/search", {
|
|
@@ -165,6 +170,20 @@ function normalizeMattermostDraftText(text, maxChars) {
|
|
|
165
170
|
if (trimmed.length <= maxChars) return trimmed;
|
|
166
171
|
return `${sliceUtf16Safe(trimmed, 0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
167
172
|
}
|
|
173
|
+
function createMattermostDraftPreviewBoundaryController(params) {
|
|
174
|
+
let hasStreamedContent = false;
|
|
175
|
+
return {
|
|
176
|
+
noteUpdate() {
|
|
177
|
+
hasStreamedContent = true;
|
|
178
|
+
},
|
|
179
|
+
async noteBoundary() {
|
|
180
|
+
if (!params.enabled) return;
|
|
181
|
+
if (!hasStreamedContent) return;
|
|
182
|
+
hasStreamedContent = false;
|
|
183
|
+
await params.forceNewMessage();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
168
187
|
function createMattermostDraftStream(params) {
|
|
169
188
|
const maxChars = Math.min(params.maxChars ?? MATTERMOST_STREAM_MAX_CHARS, MATTERMOST_STREAM_MAX_CHARS);
|
|
170
189
|
const throttleMs = Math.max(250, params.throttleMs ?? DEFAULT_THROTTLE_MS);
|
|
@@ -172,15 +191,22 @@ function createMattermostDraftStream(params) {
|
|
|
172
191
|
stopped: false,
|
|
173
192
|
final: false
|
|
174
193
|
};
|
|
175
|
-
let
|
|
176
|
-
|
|
194
|
+
let currentGeneration = {
|
|
195
|
+
lastSentText: "",
|
|
196
|
+
latestSourceText: "",
|
|
197
|
+
ready: Promise.resolve()
|
|
198
|
+
};
|
|
199
|
+
const sealedAssistantTexts = [];
|
|
177
200
|
const sendOrEditStreamMessage = async (text) => {
|
|
178
201
|
if (streamState.stopped && !streamState.final) return false;
|
|
202
|
+
const target = currentGeneration;
|
|
179
203
|
const normalized = normalizeMattermostDraftText(params.renderText?.(text) ?? text, maxChars);
|
|
180
204
|
if (!normalized) return false;
|
|
181
|
-
|
|
205
|
+
await target.ready;
|
|
206
|
+
if (streamState.stopped && !streamState.final) return false;
|
|
207
|
+
if (normalized === target.lastSentText) return true;
|
|
182
208
|
try {
|
|
183
|
-
if (
|
|
209
|
+
if (target.postId) await updateMattermostPost(params.client, target.postId, { message: normalized });
|
|
184
210
|
else {
|
|
185
211
|
const postId = (await createMattermostPost(params.client, {
|
|
186
212
|
channelId: params.channelId,
|
|
@@ -192,9 +218,9 @@ function createMattermostDraftStream(params) {
|
|
|
192
218
|
params.warn?.("mattermost stream preview stopped (missing post id from create)");
|
|
193
219
|
return false;
|
|
194
220
|
}
|
|
195
|
-
|
|
221
|
+
target.postId = postId;
|
|
196
222
|
}
|
|
197
|
-
lastSentText = normalized;
|
|
223
|
+
target.lastSentText = normalized;
|
|
198
224
|
return true;
|
|
199
225
|
} catch (err) {
|
|
200
226
|
streamState.stopped = true;
|
|
@@ -202,40 +228,333 @@ function createMattermostDraftStream(params) {
|
|
|
202
228
|
return false;
|
|
203
229
|
}
|
|
204
230
|
};
|
|
205
|
-
const
|
|
231
|
+
const clearMessageId = () => {
|
|
232
|
+
currentGeneration.postId = void 0;
|
|
233
|
+
};
|
|
234
|
+
const isValidMessageId = (value) => typeof value === "string" && value.length > 0;
|
|
235
|
+
const deleteMessage = async (postId) => {
|
|
236
|
+
await deleteMattermostPost(params.client, postId);
|
|
237
|
+
};
|
|
238
|
+
const { loop, update: updateLifecycle, stop: stopLifecycle, stopForClear, clearWithStop, seal: sealLifecycle } = createFinalizableDraftLifecycle({
|
|
206
239
|
throttleMs,
|
|
207
240
|
state: streamState,
|
|
208
241
|
sendOrEditStreamMessage,
|
|
209
|
-
readMessageId: () =>
|
|
210
|
-
clearMessageId
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
isValidMessageId: (value) => typeof value === "string" && value.length > 0,
|
|
214
|
-
deleteMessage: async (postId) => {
|
|
215
|
-
await deleteMattermostPost(params.client, postId);
|
|
216
|
-
},
|
|
242
|
+
readMessageId: () => currentGeneration.postId,
|
|
243
|
+
clearMessageId,
|
|
244
|
+
isValidMessageId,
|
|
245
|
+
deleteMessage,
|
|
217
246
|
warn: params.warn,
|
|
218
247
|
warnPrefix: "mattermost stream preview cleanup failed"
|
|
219
248
|
});
|
|
220
249
|
const forceNewMessage = () => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
loop.
|
|
250
|
+
if (streamState.stopped || streamState.final) return Promise.resolve();
|
|
251
|
+
const pendingText = loop.takePending();
|
|
252
|
+
const inFlightAtBoundary = loop.waitForInFlight();
|
|
253
|
+
const sealed = currentGeneration;
|
|
254
|
+
const boundary = (async () => {
|
|
255
|
+
try {
|
|
256
|
+
await sealed.ready;
|
|
257
|
+
await inFlightAtBoundary;
|
|
258
|
+
if (streamState.stopped && !streamState.final) return;
|
|
259
|
+
const sourceText = pendingText.trim() ? pendingText : sealed.latestSourceText;
|
|
260
|
+
const finalizedText = (params.renderText?.(sourceText) ?? sourceText).trim();
|
|
261
|
+
const chunks = params.chunkText?.(finalizedText) ?? chunkMarkdownTextWithMode(finalizedText, maxChars, "length");
|
|
262
|
+
const firstChunk = chunks[0];
|
|
263
|
+
if (!firstChunk) return;
|
|
264
|
+
if (sealed.postId) {
|
|
265
|
+
if (firstChunk !== sealed.lastSentText) await updateMattermostPost(params.client, sealed.postId, { message: firstChunk });
|
|
266
|
+
} else await createMattermostPost(params.client, {
|
|
267
|
+
channelId: params.channelId,
|
|
268
|
+
message: firstChunk,
|
|
269
|
+
rootId: params.rootId
|
|
270
|
+
});
|
|
271
|
+
for (const chunk of chunks.slice(1)) await createMattermostPost(params.client, {
|
|
272
|
+
channelId: params.channelId,
|
|
273
|
+
message: chunk,
|
|
274
|
+
rootId: params.rootId
|
|
275
|
+
});
|
|
276
|
+
const assistantText = sealed.latestAssistantText?.trim();
|
|
277
|
+
if (assistantText) sealedAssistantTexts.push(assistantText);
|
|
278
|
+
} catch (err) {
|
|
279
|
+
params.warn?.(`mattermost stream preview boundary flush failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
280
|
+
}
|
|
281
|
+
})();
|
|
282
|
+
currentGeneration = {
|
|
283
|
+
lastSentText: "",
|
|
284
|
+
latestSourceText: "",
|
|
285
|
+
ready: boundary
|
|
286
|
+
};
|
|
224
287
|
loop.resetThrottleWindow();
|
|
288
|
+
return boundary;
|
|
289
|
+
};
|
|
290
|
+
const flush = async () => {
|
|
291
|
+
await loop.flush();
|
|
292
|
+
await currentGeneration.ready;
|
|
293
|
+
};
|
|
294
|
+
const discardPending = async () => {
|
|
295
|
+
await stopForClear();
|
|
296
|
+
await currentGeneration.ready;
|
|
297
|
+
};
|
|
298
|
+
const clear = async () => {
|
|
299
|
+
await clearWithStop(discardPending);
|
|
300
|
+
};
|
|
301
|
+
const seal = async () => {
|
|
302
|
+
await sealLifecycle();
|
|
303
|
+
await currentGeneration.ready;
|
|
304
|
+
};
|
|
305
|
+
const stop = async () => {
|
|
306
|
+
await stopLifecycle();
|
|
307
|
+
await currentGeneration.ready;
|
|
308
|
+
};
|
|
309
|
+
const update = (text) => {
|
|
310
|
+
currentGeneration.latestSourceText = text;
|
|
311
|
+
currentGeneration.latestAssistantText = void 0;
|
|
312
|
+
updateLifecycle(text);
|
|
313
|
+
};
|
|
314
|
+
const updateAssistantText = (text) => {
|
|
315
|
+
currentGeneration.latestSourceText = text;
|
|
316
|
+
currentGeneration.latestAssistantText = text;
|
|
317
|
+
updateLifecycle(text);
|
|
318
|
+
};
|
|
319
|
+
const settleBoundaries = async () => {
|
|
320
|
+
await currentGeneration.ready;
|
|
321
|
+
};
|
|
322
|
+
const resolveFinalText = (text) => {
|
|
323
|
+
if (sealedAssistantTexts.length === 0) return {
|
|
324
|
+
kind: "full",
|
|
325
|
+
text
|
|
326
|
+
};
|
|
327
|
+
let remainingText = text.trim();
|
|
328
|
+
for (const sealedText of sealedAssistantTexts) {
|
|
329
|
+
const completed = sealedText.trim();
|
|
330
|
+
if (!completed || !remainingText.startsWith(completed)) return {
|
|
331
|
+
kind: "full",
|
|
332
|
+
text
|
|
333
|
+
};
|
|
334
|
+
const suffix = remainingText.slice(completed.length);
|
|
335
|
+
if (suffix && !/^\r?\n/.test(suffix)) return {
|
|
336
|
+
kind: "full",
|
|
337
|
+
text
|
|
338
|
+
};
|
|
339
|
+
remainingText = suffix.replace(/^(?:\r?\n)+/, "");
|
|
340
|
+
}
|
|
341
|
+
const currentText = currentGeneration.latestAssistantText?.trim() ?? "";
|
|
342
|
+
const remaining = remainingText.trim();
|
|
343
|
+
if (currentText && !remaining.startsWith(currentText)) return {
|
|
344
|
+
kind: "full",
|
|
345
|
+
text
|
|
346
|
+
};
|
|
347
|
+
return remaining ? {
|
|
348
|
+
kind: "remaining",
|
|
349
|
+
text: remaining
|
|
350
|
+
} : { kind: "already-delivered" };
|
|
225
351
|
};
|
|
226
352
|
params.log?.(`mattermost stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
|
|
227
353
|
return {
|
|
228
354
|
update,
|
|
229
|
-
|
|
230
|
-
|
|
355
|
+
updateAssistantText,
|
|
356
|
+
flush,
|
|
357
|
+
postId: () => currentGeneration.postId,
|
|
231
358
|
clear,
|
|
232
359
|
discardPending,
|
|
233
360
|
seal,
|
|
234
361
|
stop,
|
|
235
|
-
forceNewMessage
|
|
362
|
+
forceNewMessage,
|
|
363
|
+
settleBoundaries,
|
|
364
|
+
resolveFinalText
|
|
236
365
|
};
|
|
237
366
|
}
|
|
238
367
|
//#endregion
|
|
368
|
+
//#region extensions/mattermost/src/mattermost/monitor-helpers.ts
|
|
369
|
+
const formatInboundFromLabel$1 = formatInboundFromLabel;
|
|
370
|
+
function resolveThreadSessionKeys$1(params) {
|
|
371
|
+
return resolveThreadSessionKeys({
|
|
372
|
+
...params,
|
|
373
|
+
normalizeThreadId: (threadId) => threadId
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Strip bot mention from message text while preserving newlines and
|
|
378
|
+
* block-level Markdown formatting (headings, lists, blockquotes).
|
|
379
|
+
*/
|
|
380
|
+
function normalizeMention(text, mention) {
|
|
381
|
+
if (!mention) return text.trim();
|
|
382
|
+
const escaped = mention.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
383
|
+
const hasMentionRe = new RegExp(`@${escaped}\\b`, "i");
|
|
384
|
+
const leadingMentionRe = new RegExp(`^([\\t ]*)@${escaped}\\b[\\t ]*`, "i");
|
|
385
|
+
const trailingMentionRe = new RegExp(`[\\t ]*@${escaped}\\b[\\t ]*$`, "i");
|
|
386
|
+
const normalizedLines = text.split("\n").map((line) => {
|
|
387
|
+
const hadMention = hasMentionRe.test(line);
|
|
388
|
+
const normalizedLine = line.replace(leadingMentionRe, "$1").replace(trailingMentionRe, "").replace(new RegExp(`@${escaped}\\b`, "gi"), "").replace(/(\S)[ \t]{2,}/g, "$1 ");
|
|
389
|
+
return {
|
|
390
|
+
text: normalizedLine,
|
|
391
|
+
mentionOnlyBlank: hadMention && normalizedLine.trim() === ""
|
|
392
|
+
};
|
|
393
|
+
});
|
|
394
|
+
while (normalizedLines[0]?.mentionOnlyBlank) normalizedLines.shift();
|
|
395
|
+
while (normalizedLines.at(-1)?.text.trim() === "") normalizedLines.pop();
|
|
396
|
+
return normalizedLines.map((line) => line.text).join("\n");
|
|
397
|
+
}
|
|
398
|
+
function shouldDropEmptyMattermostBody(params) {
|
|
399
|
+
if (/[^\p{White_Space}\p{Cc}\p{Cf}\p{M}]/u.test(params.bodyText)) return false;
|
|
400
|
+
const botUsername = normalizeLowercaseStringOrEmpty(params.botUsername ?? "");
|
|
401
|
+
const bareMention = params.rawText.match(/^[ \t]*(@\S+)[ \t]*$/u)?.[1];
|
|
402
|
+
return !botUsername || normalizeLowercaseStringOrEmpty(bareMention ?? "") !== `@${botUsername}`;
|
|
403
|
+
}
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region extensions/mattermost/src/mattermost/no-visible-reply-diagnostic.ts
|
|
406
|
+
/**
|
|
407
|
+
* Detects the #80501 symptom: `deliverMattermostReplyPayload` accepted a
|
|
408
|
+
* substantive (non-reasoning) payload, called the underlying
|
|
409
|
+
* `deliverTextOrMediaReply`, and the outcome was `"empty"` — meaning the
|
|
410
|
+
* payload had no text and no media to send, so no Mattermost API call
|
|
411
|
+
* happened. The agent's run completes successfully, but no visible
|
|
412
|
+
* channel/thread reply ever surfaces to the user.
|
|
413
|
+
*
|
|
414
|
+
* Returns a structured violation when the outcome is `"empty"` for a payload
|
|
415
|
+
* that nominally carried user-facing content (text or media bytes that ended
|
|
416
|
+
* up dropped by `resolveSendableOutboundReplyParts`/`sendMediaWithLeadingCaption`).
|
|
417
|
+
* Returns `null` for `"reasoning_skipped"` (intentional suppression),
|
|
418
|
+
* `"text"`, or `"media"` (successful visible sends).
|
|
419
|
+
*/
|
|
420
|
+
function evaluateMattermostNoVisibleReply(params) {
|
|
421
|
+
if (params.outcome !== "empty") return null;
|
|
422
|
+
const finalText = typeof params.payload.text === "string" ? params.payload.text.trim() : "";
|
|
423
|
+
const mediaUrlCount = countOutboundMedia(params.payload);
|
|
424
|
+
if (finalText.length === 0 && mediaUrlCount === 0) return null;
|
|
425
|
+
return {
|
|
426
|
+
reason: "no-visible-reply-after-final-delivery",
|
|
427
|
+
outcome: params.outcome,
|
|
428
|
+
finalTextLength: finalText.length,
|
|
429
|
+
mediaUrlCount
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
function formatMattermostNoVisibleReplyLog(params) {
|
|
433
|
+
return `mattermost no-visible-reply: ${params.violation.reason} to=${params.to} accountId=${params.accountId} agentId=${params.agentId ?? "unknown"} outcome=${params.violation.outcome} finalTextLength=${params.violation.finalTextLength} mediaUrlCount=${params.violation.mediaUrlCount}`;
|
|
434
|
+
}
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region extensions/mattermost/src/mattermost/monitor-context.ts
|
|
437
|
+
function shouldUpdateMattermostDraftToolProgress(account) {
|
|
438
|
+
return account.streamingMode !== "off" && resolveChannelStreamingPreviewToolProgress(account.config);
|
|
439
|
+
}
|
|
440
|
+
function shouldSuppressMattermostDefaultToolProgressMessages(account) {
|
|
441
|
+
return account.streamingMode !== "off";
|
|
442
|
+
}
|
|
443
|
+
function buildMattermostModelPickerSelectMessageSid(params) {
|
|
444
|
+
const provider = normalizeLowercaseStringOrEmpty(params.provider);
|
|
445
|
+
const model = normalizeLowercaseStringOrEmpty(params.model);
|
|
446
|
+
return `interaction:${params.postId}:select:${provider}/${model}`;
|
|
447
|
+
}
|
|
448
|
+
function resolveMattermostReplyRootId(params) {
|
|
449
|
+
const threadRootId = normalizeOptionalString(params.threadRootId);
|
|
450
|
+
if (params.kind === "direct" && !threadRootId) return;
|
|
451
|
+
if (threadRootId) return threadRootId;
|
|
452
|
+
return normalizeOptionalString(params.replyToId);
|
|
453
|
+
}
|
|
454
|
+
function canFinalizeMattermostPreviewInPlace(params) {
|
|
455
|
+
return resolveMattermostReplyRootId({
|
|
456
|
+
kind: params.kind,
|
|
457
|
+
threadRootId: params.threadRootId,
|
|
458
|
+
replyToId: params.replyToId
|
|
459
|
+
}) === params.previewRootId?.trim();
|
|
460
|
+
}
|
|
461
|
+
function formatMattermostFinalDeliveryOutcomeLog(params) {
|
|
462
|
+
const violation = evaluateMattermostNoVisibleReply({
|
|
463
|
+
outcome: params.outcome,
|
|
464
|
+
payload: params.payload
|
|
465
|
+
});
|
|
466
|
+
if (violation) return formatMattermostNoVisibleReplyLog({
|
|
467
|
+
violation,
|
|
468
|
+
to: params.to,
|
|
469
|
+
accountId: params.accountId,
|
|
470
|
+
agentId: params.agentId
|
|
471
|
+
});
|
|
472
|
+
if (params.outcome === "text" || params.outcome === "media") return `delivered reply to ${params.to}`;
|
|
473
|
+
}
|
|
474
|
+
function resolveMattermostEffectiveReplyToId(params) {
|
|
475
|
+
if (params.kind === "direct" && params.replyToMode === "off") return;
|
|
476
|
+
const threadRootId = normalizeOptionalString(params.threadRootId);
|
|
477
|
+
if (threadRootId) return threadRootId;
|
|
478
|
+
const postId = normalizeOptionalString(params.postId);
|
|
479
|
+
if (!postId) return;
|
|
480
|
+
return params.replyToMode === "all" || params.replyToMode === "first" || params.replyToMode === "batched" ? postId : void 0;
|
|
481
|
+
}
|
|
482
|
+
function resolveMattermostThreadSessionContext(params) {
|
|
483
|
+
const effectiveReplyToId = resolveMattermostEffectiveReplyToId({
|
|
484
|
+
kind: params.kind,
|
|
485
|
+
postId: params.postId,
|
|
486
|
+
replyToMode: params.replyToMode,
|
|
487
|
+
threadRootId: params.threadRootId
|
|
488
|
+
});
|
|
489
|
+
const threadKeys = resolveThreadSessionKeys$1({
|
|
490
|
+
baseSessionKey: params.baseSessionKey,
|
|
491
|
+
threadId: effectiveReplyToId,
|
|
492
|
+
parentSessionKey: effectiveReplyToId && params.kind !== "direct" ? params.baseSessionKey : void 0
|
|
493
|
+
});
|
|
494
|
+
return {
|
|
495
|
+
effectiveReplyToId,
|
|
496
|
+
sessionKey: threadKeys.sessionKey,
|
|
497
|
+
parentSessionKey: threadKeys.parentSessionKey
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function resolveMattermostPendingHistoryKey(params) {
|
|
501
|
+
return params.kind === "direct" ? null : params.sessionKey;
|
|
502
|
+
}
|
|
503
|
+
function resolveMattermostReactionChannelId(payload) {
|
|
504
|
+
return normalizeOptionalString(payload.broadcast?.channel_id) ?? normalizeOptionalString(payload.data?.channel_id);
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region extensions/mattermost/src/mattermost/monitor-draft-delivery.ts
|
|
508
|
+
async function deliverMattermostReplyWithDraftPreview(params) {
|
|
509
|
+
if (isReasoningReplyPayload(params.payload)) return;
|
|
510
|
+
await deliverWithFinalizableLivePreviewAdapter({
|
|
511
|
+
kind: params.info.kind,
|
|
512
|
+
payload: params.payload,
|
|
513
|
+
adapter: defineFinalizableLivePreviewAdapter({
|
|
514
|
+
draft: {
|
|
515
|
+
flush: params.draftStream.flush,
|
|
516
|
+
clear: params.draftStream.clear,
|
|
517
|
+
discardPending: params.draftStream.discardPending,
|
|
518
|
+
seal: params.draftStream.seal,
|
|
519
|
+
id: params.draftStream.postId
|
|
520
|
+
},
|
|
521
|
+
buildFinalEdit: (payload) => {
|
|
522
|
+
const hasMedia = Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
|
|
523
|
+
const ttsSupplement = getReplyPayloadTtsSupplement(payload);
|
|
524
|
+
const previewFinalText = params.resolvePreviewFinalText(payload.text ?? ttsSupplement?.spokenText);
|
|
525
|
+
if (hasMedia && !ttsSupplement || typeof previewFinalText !== "string" || payload.isError || !canFinalizeMattermostPreviewInPlace({
|
|
526
|
+
kind: params.kind,
|
|
527
|
+
previewRootId: params.effectiveReplyToId,
|
|
528
|
+
threadRootId: params.effectiveReplyToId,
|
|
529
|
+
replyToId: payload.replyToId
|
|
530
|
+
})) return;
|
|
531
|
+
return { message: previewFinalText };
|
|
532
|
+
},
|
|
533
|
+
editFinal: async (previewPostId, edit) => {
|
|
534
|
+
await updateMattermostPost(params.client, previewPostId, edit);
|
|
535
|
+
},
|
|
536
|
+
onPreviewFinalized: () => {
|
|
537
|
+
params.previewState.finalizedViaPreviewPost = true;
|
|
538
|
+
params.recordThreadParticipation?.();
|
|
539
|
+
},
|
|
540
|
+
buildSupplementalPayload: (payload) => getReplyPayloadTtsSupplement(payload) ? buildTtsSupplementMediaPayload(payload) : void 0,
|
|
541
|
+
deliverSupplemental: async (payload) => {
|
|
542
|
+
await params.deliverPayload(payload);
|
|
543
|
+
},
|
|
544
|
+
logPreviewEditFailure: (err) => {
|
|
545
|
+
params.logVerboseMessage(`mattermost preview final edit failed; falling back to normal send (${String(err)})`);
|
|
546
|
+
}
|
|
547
|
+
}),
|
|
548
|
+
deliverNormally: async (payload) => {
|
|
549
|
+
const supplement = getReplyPayloadTtsSupplement(payload);
|
|
550
|
+
await params.deliverPayload(supplement && !payload.text?.trim() && supplement.visibleTextAlreadyDelivered !== true ? {
|
|
551
|
+
...payload,
|
|
552
|
+
text: supplement.spokenText
|
|
553
|
+
} : payload);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
//#endregion
|
|
239
558
|
//#region extensions/mattermost/src/mattermost/monitor-gating.ts
|
|
240
559
|
function mapMattermostChannelTypeToChatType(channelType) {
|
|
241
560
|
const normalized = channelType?.trim().toUpperCase();
|
|
@@ -279,43 +598,6 @@ function evaluateMattermostMentionGate(params) {
|
|
|
279
598
|
};
|
|
280
599
|
}
|
|
281
600
|
//#endregion
|
|
282
|
-
//#region extensions/mattermost/src/mattermost/monitor-helpers.ts
|
|
283
|
-
const formatInboundFromLabel$1 = formatInboundFromLabel;
|
|
284
|
-
function resolveThreadSessionKeys$1(params) {
|
|
285
|
-
return resolveThreadSessionKeys({
|
|
286
|
-
...params,
|
|
287
|
-
normalizeThreadId: (threadId) => threadId
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Strip bot mention from message text while preserving newlines and
|
|
292
|
-
* block-level Markdown formatting (headings, lists, blockquotes).
|
|
293
|
-
*/
|
|
294
|
-
function normalizeMention(text, mention) {
|
|
295
|
-
if (!mention) return text.trim();
|
|
296
|
-
const escaped = mention.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
297
|
-
const hasMentionRe = new RegExp(`@${escaped}\\b`, "i");
|
|
298
|
-
const leadingMentionRe = new RegExp(`^([\\t ]*)@${escaped}\\b[\\t ]*`, "i");
|
|
299
|
-
const trailingMentionRe = new RegExp(`[\\t ]*@${escaped}\\b[\\t ]*$`, "i");
|
|
300
|
-
const normalizedLines = text.split("\n").map((line) => {
|
|
301
|
-
const hadMention = hasMentionRe.test(line);
|
|
302
|
-
const normalizedLine = line.replace(leadingMentionRe, "$1").replace(trailingMentionRe, "").replace(new RegExp(`@${escaped}\\b`, "gi"), "").replace(/(\S)[ \t]{2,}/g, "$1 ");
|
|
303
|
-
return {
|
|
304
|
-
text: normalizedLine,
|
|
305
|
-
mentionOnlyBlank: hadMention && normalizedLine.trim() === ""
|
|
306
|
-
};
|
|
307
|
-
});
|
|
308
|
-
while (normalizedLines[0]?.mentionOnlyBlank) normalizedLines.shift();
|
|
309
|
-
while (normalizedLines.at(-1)?.text.trim() === "") normalizedLines.pop();
|
|
310
|
-
return normalizedLines.map((line) => line.text).join("\n");
|
|
311
|
-
}
|
|
312
|
-
function shouldDropEmptyMattermostBody(params) {
|
|
313
|
-
if (/[^\p{White_Space}\p{Cc}\p{Cf}\p{M}]/u.test(params.bodyText)) return false;
|
|
314
|
-
const botUsername = normalizeLowercaseStringOrEmpty(params.botUsername ?? "");
|
|
315
|
-
const bareMention = params.rawText.match(/^[ \t]*(@\S+)[ \t]*$/u)?.[1];
|
|
316
|
-
return !botUsername || normalizeLowercaseStringOrEmpty(bareMention ?? "") !== `@${botUsername}`;
|
|
317
|
-
}
|
|
318
|
-
//#endregion
|
|
319
601
|
//#region extensions/mattermost/src/mattermost/monitor-onchar.ts
|
|
320
602
|
const DEFAULT_ONCHAR_PREFIXES = [">", "!"];
|
|
321
603
|
function resolveOncharPrefixes(prefixes) {
|
|
@@ -337,6 +619,37 @@ function stripOncharPrefix(text, prefixes) {
|
|
|
337
619
|
};
|
|
338
620
|
}
|
|
339
621
|
//#endregion
|
|
622
|
+
//#region extensions/mattermost/src/mattermost/monitor-replay.ts
|
|
623
|
+
const recentInboundMessages = createClaimableDedupe({
|
|
624
|
+
ttlMs: 5 * 6e4,
|
|
625
|
+
memoryMaxSize: 2e3
|
|
626
|
+
});
|
|
627
|
+
function buildMattermostInboundReplayKeys(params) {
|
|
628
|
+
return uniqueStrings(params.messageIds.map((id) => `${params.accountId}:${id.trim()}`)).filter((key) => !key.endsWith(":"));
|
|
629
|
+
}
|
|
630
|
+
async function processMattermostReplayGuardedPost(params) {
|
|
631
|
+
const replayGuard = params.replayGuard ?? recentInboundMessages;
|
|
632
|
+
const replayKeys = buildMattermostInboundReplayKeys({
|
|
633
|
+
accountId: params.accountId,
|
|
634
|
+
messageIds: params.messageIds
|
|
635
|
+
});
|
|
636
|
+
if (replayKeys.length === 0) {
|
|
637
|
+
await params.handlePost();
|
|
638
|
+
return "processed";
|
|
639
|
+
}
|
|
640
|
+
const claimedKeys = [];
|
|
641
|
+
for (const replayKey of replayKeys) if ((await replayGuard.claim(replayKey)).kind === "claimed") claimedKeys.push(replayKey);
|
|
642
|
+
if (claimedKeys.length === 0) return "duplicate";
|
|
643
|
+
try {
|
|
644
|
+
await params.handlePost();
|
|
645
|
+
await Promise.all(claimedKeys.map((replayKey) => replayGuard.commit(replayKey)));
|
|
646
|
+
return "processed";
|
|
647
|
+
} catch (error) {
|
|
648
|
+
await Promise.all(claimedKeys.map((replayKey) => replayGuard.commit(replayKey)));
|
|
649
|
+
throw error;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
//#endregion
|
|
340
653
|
//#region extensions/mattermost/src/mattermost/monitor-resources.ts
|
|
341
654
|
function formatMattermostInboundMediaText(params) {
|
|
342
655
|
const unavailableCount = Math.max(0, params.expectedCount - params.mediaCount);
|
|
@@ -349,6 +662,9 @@ function formatMattermostInboundMediaText(params) {
|
|
|
349
662
|
}
|
|
350
663
|
const CHANNEL_CACHE_TTL_MS = 5 * 6e4;
|
|
351
664
|
const USER_CACHE_TTL_MS = 10 * 6e4;
|
|
665
|
+
const MONITOR_RESOURCE_CACHE_MAX_ENTRIES = 1e3;
|
|
666
|
+
const MATTERMOST_MEDIA_RESPONSE_HEADER_TIMEOUT_MS = 12e4;
|
|
667
|
+
const MATTERMOST_MEDIA_READ_IDLE_TIMEOUT_MS = 3e4;
|
|
352
668
|
function createMattermostMonitorResources(params) {
|
|
353
669
|
const { accountId, callbackUrl, client, logger, mediaMaxBytes, saveRemoteMedia, mediaKindFromMime } = params;
|
|
354
670
|
const channelCache = /* @__PURE__ */ new Map();
|
|
@@ -361,10 +677,14 @@ function createMattermostMonitorResources(params) {
|
|
|
361
677
|
};
|
|
362
678
|
const setCachedValue = (cache, key, value, ttlMs, rawNowMs) => {
|
|
363
679
|
const expiresAt = resolveExpiresAtMsFromDurationMs(ttlMs, { nowMs: rawNowMs });
|
|
364
|
-
if (expiresAt !== void 0)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
680
|
+
if (expiresAt !== void 0) {
|
|
681
|
+
cache.delete(key);
|
|
682
|
+
cache.set(key, {
|
|
683
|
+
value,
|
|
684
|
+
expiresAt
|
|
685
|
+
});
|
|
686
|
+
pruneMapToMaxSize(cache, MONITOR_RESOURCE_CACHE_MAX_ENTRIES);
|
|
687
|
+
}
|
|
368
688
|
};
|
|
369
689
|
const resolveMattermostMedia = async (fileIds) => {
|
|
370
690
|
const ids = normalizeStringEntries(fileIds ?? []);
|
|
@@ -376,7 +696,9 @@ function createMattermostMonitorResources(params) {
|
|
|
376
696
|
requestInit: { headers: { Authorization: `Bearer ${client.token}` } },
|
|
377
697
|
filePathHint: fileId,
|
|
378
698
|
maxBytes: mediaMaxBytes,
|
|
379
|
-
ssrfPolicy: { allowedHostnames: [new URL(client.baseUrl).hostname] }
|
|
699
|
+
ssrfPolicy: { allowedHostnames: [new URL(client.baseUrl).hostname] },
|
|
700
|
+
responseHeaderTimeoutMs: MATTERMOST_MEDIA_RESPONSE_HEADER_TIMEOUT_MS,
|
|
701
|
+
readIdleTimeoutMs: MATTERMOST_MEDIA_READ_IDLE_TIMEOUT_MS
|
|
380
702
|
});
|
|
381
703
|
const contentType = saved.contentType ?? void 0;
|
|
382
704
|
out.push({
|
|
@@ -849,37 +1171,6 @@ function reasonToString(reason) {
|
|
|
849
1171
|
return reason.length > 0 ? reason.toString("utf8") : "";
|
|
850
1172
|
}
|
|
851
1173
|
//#endregion
|
|
852
|
-
//#region extensions/mattermost/src/mattermost/no-visible-reply-diagnostic.ts
|
|
853
|
-
/**
|
|
854
|
-
* Detects the #80501 symptom: `deliverMattermostReplyPayload` accepted a
|
|
855
|
-
* substantive (non-reasoning) payload, called the underlying
|
|
856
|
-
* `deliverTextOrMediaReply`, and the outcome was `"empty"` — meaning the
|
|
857
|
-
* payload had no text and no media to send, so no Mattermost API call
|
|
858
|
-
* happened. The agent's run completes successfully, but no visible
|
|
859
|
-
* channel/thread reply ever surfaces to the user.
|
|
860
|
-
*
|
|
861
|
-
* Returns a structured violation when the outcome is `"empty"` for a payload
|
|
862
|
-
* that nominally carried user-facing content (text or media bytes that ended
|
|
863
|
-
* up dropped by `resolveSendableOutboundReplyParts`/`sendMediaWithLeadingCaption`).
|
|
864
|
-
* Returns `null` for `"reasoning_skipped"` (intentional suppression),
|
|
865
|
-
* `"text"`, or `"media"` (successful visible sends).
|
|
866
|
-
*/
|
|
867
|
-
function evaluateMattermostNoVisibleReply(params) {
|
|
868
|
-
if (params.outcome !== "empty") return null;
|
|
869
|
-
const finalText = typeof params.payload.text === "string" ? params.payload.text.trim() : "";
|
|
870
|
-
const mediaUrlCount = countOutboundMedia(params.payload);
|
|
871
|
-
if (finalText.length === 0 && mediaUrlCount === 0) return null;
|
|
872
|
-
return {
|
|
873
|
-
reason: "no-visible-reply-after-final-delivery",
|
|
874
|
-
outcome: params.outcome,
|
|
875
|
-
finalTextLength: finalText.length,
|
|
876
|
-
mediaUrlCount
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
function formatMattermostNoVisibleReplyLog(params) {
|
|
880
|
-
return `mattermost no-visible-reply: ${params.violation.reason} to=${params.to} accountId=${params.accountId} agentId=${params.agentId ?? "unknown"} outcome=${params.violation.outcome} finalTextLength=${params.violation.finalTextLength} mediaUrlCount=${params.violation.mediaUrlCount}`;
|
|
881
|
-
}
|
|
882
|
-
//#endregion
|
|
883
1174
|
//#region extensions/mattermost/src/mattermost/reconnect.ts
|
|
884
1175
|
/**
|
|
885
1176
|
* Reconnection loop with exponential backoff.
|
|
@@ -992,58 +1283,9 @@ async function hasMattermostThreadParticipationWithPersistence(params) {
|
|
|
992
1283
|
}
|
|
993
1284
|
//#endregion
|
|
994
1285
|
//#region extensions/mattermost/src/mattermost/monitor.ts
|
|
995
|
-
function shouldUpdateMattermostDraftToolProgress(account) {
|
|
996
|
-
return account.streamingMode !== "off" && resolveChannelStreamingPreviewToolProgress(account.config);
|
|
997
|
-
}
|
|
998
|
-
function shouldSuppressMattermostDefaultToolProgressMessages(account) {
|
|
999
|
-
return account.streamingMode !== "off";
|
|
1000
|
-
}
|
|
1001
|
-
const RECENT_MATTERMOST_MESSAGE_TTL_MS = 5 * 6e4;
|
|
1002
|
-
const RECENT_MATTERMOST_MESSAGE_MAX = 2e3;
|
|
1003
1286
|
function normalizeInteractionSourceIps(values) {
|
|
1004
1287
|
return normalizeTrimmedStringList(values);
|
|
1005
1288
|
}
|
|
1006
|
-
const recentInboundMessages = createClaimableDedupe({
|
|
1007
|
-
ttlMs: RECENT_MATTERMOST_MESSAGE_TTL_MS,
|
|
1008
|
-
memoryMaxSize: RECENT_MATTERMOST_MESSAGE_MAX
|
|
1009
|
-
});
|
|
1010
|
-
var MattermostRetryableInboundError = class extends Error {
|
|
1011
|
-
constructor(message, options) {
|
|
1012
|
-
super(message, options);
|
|
1013
|
-
this.name = "MattermostRetryableInboundError";
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
function buildMattermostModelPickerSelectMessageSid(params) {
|
|
1017
|
-
const provider = normalizeLowercaseStringOrEmpty(params.provider);
|
|
1018
|
-
const model = normalizeLowercaseStringOrEmpty(params.model);
|
|
1019
|
-
return `interaction:${params.postId}:select:${provider}/${model}`;
|
|
1020
|
-
}
|
|
1021
|
-
function buildMattermostInboundReplayKeys(params) {
|
|
1022
|
-
return uniqueStrings(params.messageIds.map((id) => `${params.accountId}:${id.trim()}`)).filter((key) => !key.endsWith(":"));
|
|
1023
|
-
}
|
|
1024
|
-
async function processMattermostReplayGuardedPost(params) {
|
|
1025
|
-
const replayGuard = params.replayGuard ?? recentInboundMessages;
|
|
1026
|
-
const replayKeys = buildMattermostInboundReplayKeys({
|
|
1027
|
-
accountId: params.accountId,
|
|
1028
|
-
messageIds: params.messageIds
|
|
1029
|
-
});
|
|
1030
|
-
if (replayKeys.length === 0) {
|
|
1031
|
-
await params.handlePost();
|
|
1032
|
-
return "processed";
|
|
1033
|
-
}
|
|
1034
|
-
const claimedKeys = [];
|
|
1035
|
-
for (const replayKey of replayKeys) if ((await replayGuard.claim(replayKey)).kind === "claimed") claimedKeys.push(replayKey);
|
|
1036
|
-
if (claimedKeys.length === 0) return "duplicate";
|
|
1037
|
-
try {
|
|
1038
|
-
await params.handlePost();
|
|
1039
|
-
await Promise.all(claimedKeys.map((replayKey) => replayGuard.commit(replayKey)));
|
|
1040
|
-
return "processed";
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
if (error instanceof MattermostRetryableInboundError) claimedKeys.forEach((replayKey) => replayGuard.release(replayKey, { error }));
|
|
1043
|
-
else await Promise.all(claimedKeys.map((replayKey) => replayGuard.commit(replayKey)));
|
|
1044
|
-
throw error;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
1289
|
function resolveRuntime(opts) {
|
|
1048
1290
|
return opts.runtime ?? {
|
|
1049
1291
|
log: console.log,
|
|
@@ -1061,126 +1303,32 @@ function channelChatType(kind) {
|
|
|
1061
1303
|
if (kind === "group") return "group";
|
|
1062
1304
|
return "channel";
|
|
1063
1305
|
}
|
|
1064
|
-
function resolveMattermostReplyRootId(params) {
|
|
1065
|
-
if (params.kind === "direct") return;
|
|
1066
|
-
const threadRootId = normalizeOptionalString(params.threadRootId);
|
|
1067
|
-
if (threadRootId) return threadRootId;
|
|
1068
|
-
return normalizeOptionalString(params.replyToId);
|
|
1069
|
-
}
|
|
1070
|
-
function canFinalizeMattermostPreviewInPlace(params) {
|
|
1071
|
-
return resolveMattermostReplyRootId({
|
|
1072
|
-
kind: params.kind,
|
|
1073
|
-
threadRootId: params.threadRootId,
|
|
1074
|
-
replyToId: params.replyToId
|
|
1075
|
-
}) === params.previewRootId?.trim();
|
|
1076
|
-
}
|
|
1077
1306
|
function createDisabledMattermostDraftStream() {
|
|
1078
1307
|
const noopAsync = async () => {};
|
|
1079
1308
|
return {
|
|
1080
1309
|
update: () => {},
|
|
1310
|
+
updateAssistantText: () => {},
|
|
1081
1311
|
flush: noopAsync,
|
|
1082
1312
|
postId: () => void 0,
|
|
1083
1313
|
clear: noopAsync,
|
|
1084
1314
|
discardPending: noopAsync,
|
|
1085
1315
|
seal: noopAsync,
|
|
1086
1316
|
stop: noopAsync,
|
|
1087
|
-
forceNewMessage:
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
kind: params.info.kind,
|
|
1094
|
-
payload: params.payload,
|
|
1095
|
-
adapter: defineFinalizableLivePreviewAdapter({
|
|
1096
|
-
draft: {
|
|
1097
|
-
flush: params.draftStream.flush,
|
|
1098
|
-
clear: params.draftStream.clear,
|
|
1099
|
-
discardPending: params.draftStream.discardPending,
|
|
1100
|
-
seal: params.draftStream.seal,
|
|
1101
|
-
id: params.draftStream.postId
|
|
1102
|
-
},
|
|
1103
|
-
buildFinalEdit: (payload) => {
|
|
1104
|
-
const hasMedia = Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
|
|
1105
|
-
const ttsSupplement = getReplyPayloadTtsSupplement(payload);
|
|
1106
|
-
const previewFinalText = params.resolvePreviewFinalText(payload.text ?? ttsSupplement?.spokenText);
|
|
1107
|
-
if (hasMedia && !ttsSupplement || typeof previewFinalText !== "string" || payload.isError || !canFinalizeMattermostPreviewInPlace({
|
|
1108
|
-
kind: params.kind,
|
|
1109
|
-
previewRootId: params.effectiveReplyToId,
|
|
1110
|
-
threadRootId: params.effectiveReplyToId,
|
|
1111
|
-
replyToId: payload.replyToId
|
|
1112
|
-
})) return;
|
|
1113
|
-
return { message: previewFinalText };
|
|
1114
|
-
},
|
|
1115
|
-
editFinal: async (previewPostId, edit) => {
|
|
1116
|
-
await updateMattermostPost(params.client, previewPostId, edit);
|
|
1117
|
-
},
|
|
1118
|
-
onPreviewFinalized: () => {
|
|
1119
|
-
params.previewState.finalizedViaPreviewPost = true;
|
|
1120
|
-
params.recordThreadParticipation?.();
|
|
1121
|
-
},
|
|
1122
|
-
buildSupplementalPayload: (payload) => getReplyPayloadTtsSupplement(payload) ? buildTtsSupplementMediaPayload(payload) : void 0,
|
|
1123
|
-
deliverSupplemental: async (payload) => {
|
|
1124
|
-
await params.deliverPayload(payload);
|
|
1125
|
-
},
|
|
1126
|
-
logPreviewEditFailure: (err) => {
|
|
1127
|
-
params.logVerboseMessage(`mattermost preview final edit failed; falling back to normal send (${String(err)})`);
|
|
1128
|
-
}
|
|
1129
|
-
}),
|
|
1130
|
-
deliverNormally: async (payload) => {
|
|
1131
|
-
const supplement = getReplyPayloadTtsSupplement(payload);
|
|
1132
|
-
await params.deliverPayload(supplement && !payload.text?.trim() && supplement.visibleTextAlreadyDelivered !== true ? {
|
|
1133
|
-
...payload,
|
|
1134
|
-
text: supplement.spokenText
|
|
1135
|
-
} : payload);
|
|
1136
|
-
}
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
function formatMattermostFinalDeliveryOutcomeLog(params) {
|
|
1140
|
-
const violation = evaluateMattermostNoVisibleReply({
|
|
1141
|
-
outcome: params.outcome,
|
|
1142
|
-
payload: params.payload
|
|
1143
|
-
});
|
|
1144
|
-
if (violation) return formatMattermostNoVisibleReplyLog({
|
|
1145
|
-
violation,
|
|
1146
|
-
to: params.to,
|
|
1147
|
-
accountId: params.accountId,
|
|
1148
|
-
agentId: params.agentId
|
|
1149
|
-
});
|
|
1150
|
-
if (params.outcome === "text" || params.outcome === "media") return `delivered reply to ${params.to}`;
|
|
1151
|
-
}
|
|
1152
|
-
function resolveMattermostEffectiveReplyToId(params) {
|
|
1153
|
-
if (params.kind === "direct") return;
|
|
1154
|
-
const threadRootId = normalizeOptionalString(params.threadRootId);
|
|
1155
|
-
if (threadRootId) return threadRootId;
|
|
1156
|
-
const postId = normalizeOptionalString(params.postId);
|
|
1157
|
-
if (!postId) return;
|
|
1158
|
-
return params.replyToMode === "all" || params.replyToMode === "first" || params.replyToMode === "batched" ? postId : void 0;
|
|
1159
|
-
}
|
|
1160
|
-
function resolveMattermostThreadSessionContext(params) {
|
|
1161
|
-
const effectiveReplyToId = resolveMattermostEffectiveReplyToId({
|
|
1162
|
-
kind: params.kind,
|
|
1163
|
-
postId: params.postId,
|
|
1164
|
-
replyToMode: params.replyToMode,
|
|
1165
|
-
threadRootId: params.threadRootId
|
|
1166
|
-
});
|
|
1167
|
-
const threadKeys = resolveThreadSessionKeys$1({
|
|
1168
|
-
baseSessionKey: params.baseSessionKey,
|
|
1169
|
-
threadId: effectiveReplyToId,
|
|
1170
|
-
parentSessionKey: effectiveReplyToId ? params.baseSessionKey : void 0
|
|
1171
|
-
});
|
|
1172
|
-
return {
|
|
1173
|
-
effectiveReplyToId,
|
|
1174
|
-
sessionKey: threadKeys.sessionKey,
|
|
1175
|
-
parentSessionKey: threadKeys.parentSessionKey
|
|
1317
|
+
forceNewMessage: noopAsync,
|
|
1318
|
+
settleBoundaries: noopAsync,
|
|
1319
|
+
resolveFinalText: (text) => ({
|
|
1320
|
+
kind: "full",
|
|
1321
|
+
text
|
|
1322
|
+
})
|
|
1176
1323
|
};
|
|
1177
1324
|
}
|
|
1178
|
-
function resolveMattermostReactionChannelId(payload) {
|
|
1179
|
-
return normalizeOptionalString(payload.broadcast?.channel_id) ?? normalizeOptionalString(payload.data?.channel_id);
|
|
1180
|
-
}
|
|
1181
1325
|
function buildMattermostAttachmentPlaceholder(mediaList) {
|
|
1182
1326
|
if (mediaList.length === 0) return "";
|
|
1183
|
-
if (mediaList.length === 1)
|
|
1327
|
+
if (mediaList.length === 1) {
|
|
1328
|
+
const media = mediaList[0];
|
|
1329
|
+
if (!media) return "";
|
|
1330
|
+
return `<media:${media.kind === "unknown" ? "document" : media.kind}>`;
|
|
1331
|
+
}
|
|
1184
1332
|
const allImages = mediaList.every((media) => media.kind === "image");
|
|
1185
1333
|
const label = allImages ? "image" : "file";
|
|
1186
1334
|
const suffix = mediaList.length === 1 ? label : `${label}s`;
|
|
@@ -1442,17 +1590,22 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
1442
1590
|
},
|
|
1443
1591
|
onReplyStart: typingCallbacks?.onReplyStart
|
|
1444
1592
|
});
|
|
1445
|
-
await core.channel.reply.
|
|
1446
|
-
ctx: ctxPayload,
|
|
1447
|
-
cfg,
|
|
1593
|
+
await core.channel.reply.withReplyDispatcher({
|
|
1448
1594
|
dispatcher,
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1595
|
+
onSettled: () => {
|
|
1596
|
+
markDispatchIdle();
|
|
1597
|
+
},
|
|
1598
|
+
run: () => core.channel.reply.dispatchReplyFromConfig({
|
|
1599
|
+
ctx: ctxPayload,
|
|
1600
|
+
cfg,
|
|
1601
|
+
dispatcher,
|
|
1602
|
+
replyOptions: {
|
|
1603
|
+
...replyOptions,
|
|
1604
|
+
disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
|
|
1605
|
+
onModelSelected
|
|
1606
|
+
}
|
|
1607
|
+
})
|
|
1454
1608
|
});
|
|
1455
|
-
markDispatchIdle();
|
|
1456
1609
|
},
|
|
1457
1610
|
log: (msg) => runtime.log?.(msg)
|
|
1458
1611
|
}),
|
|
@@ -1912,7 +2065,10 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
1912
2065
|
replyToMode,
|
|
1913
2066
|
threadRootId
|
|
1914
2067
|
});
|
|
1915
|
-
const historyKey =
|
|
2068
|
+
const historyKey = resolveMattermostPendingHistoryKey({
|
|
2069
|
+
kind,
|
|
2070
|
+
sessionKey
|
|
2071
|
+
});
|
|
1916
2072
|
const mentionRegexes = core.channel.mentions.buildMentionRegexes(cfg, route.agentId);
|
|
1917
2073
|
const wasMentioned = kind !== "direct" && ((botUsername ? normalizeLowercaseStringOrEmpty(rawText).includes(`@${normalizeLowercaseStringOrEmpty(botUsername)}`) : false) || core.channel.mentions.matchesMentionPatterns(rawText, mentionRegexes));
|
|
1918
2074
|
const pendingBody = rawText || (post.file_ids?.length ? `[Mattermost ${post.file_ids.length === 1 ? "file" : "files"}]` : "");
|
|
@@ -2074,7 +2230,7 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2074
2230
|
normalizeEntry: normalizeMattermostAllowEntry
|
|
2075
2231
|
}) : null;
|
|
2076
2232
|
const storePath = core.channel.session.resolveStorePath(cfg.session?.store, { agentId: route.agentId });
|
|
2077
|
-
const previewLine = bodyText
|
|
2233
|
+
const previewLine = truncateUtf16Safe(bodyText, 200).replace(/\n/g, "\\n");
|
|
2078
2234
|
logVerboseMessage(`mattermost inbound: from=${ctxPayload.From} len=${bodyText.length} preview="${previewLine}"`);
|
|
2079
2235
|
const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
|
|
2080
2236
|
const tableMode = core.channel.text.resolveMarkdownTableMode({
|
|
@@ -2082,7 +2238,8 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2082
2238
|
channel: "mattermost",
|
|
2083
2239
|
accountId: account.accountId
|
|
2084
2240
|
});
|
|
2085
|
-
const
|
|
2241
|
+
const chunkMode = core.channel.text.resolveChunkMode(cfg, "mattermost", account.accountId);
|
|
2242
|
+
const { onModelSelected, typingCallbacks, resolveResponsePrefix, ...replyPipeline } = createChannelMessageReplyPipeline({
|
|
2086
2243
|
cfg,
|
|
2087
2244
|
agentId: route.agentId,
|
|
2088
2245
|
channel: "mattermost",
|
|
@@ -2107,10 +2264,22 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2107
2264
|
channelId,
|
|
2108
2265
|
rootId: effectiveReplyToId,
|
|
2109
2266
|
throttleMs: 1200,
|
|
2267
|
+
chunkText: (value) => core.channel.text.chunkMarkdownTextWithMode(core.channel.text.convertMarkdownTables(value, tableMode), textLimit, chunkMode),
|
|
2110
2268
|
log: logVerboseMessage,
|
|
2111
2269
|
warn: logVerboseMessage
|
|
2112
2270
|
}) : createDisabledMattermostDraftStream();
|
|
2271
|
+
const previewBoundaryController = createMattermostDraftPreviewBoundaryController({
|
|
2272
|
+
enabled: draftPreviewEnabled && account.streamingMode === "block",
|
|
2273
|
+
forceNewMessage: async () => {
|
|
2274
|
+
await draftStream.forceNewMessage();
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2113
2277
|
let lastPartialText = "";
|
|
2278
|
+
let firstAssistantPreviewPrefix;
|
|
2279
|
+
let firstAssistantPreviewPrefixPending = true;
|
|
2280
|
+
let currentAssistantPreviewUsesPrefix = false;
|
|
2281
|
+
let blockPreviewActivity = "none";
|
|
2282
|
+
let blockPreviewAssistantMessagePending = false;
|
|
2114
2283
|
const progressDraft = createChannelProgressDraftCompositor({
|
|
2115
2284
|
entry: account.config,
|
|
2116
2285
|
mode: account.streamingMode,
|
|
@@ -2121,11 +2290,28 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2121
2290
|
if (options?.flush) await draftStream.flush();
|
|
2122
2291
|
}
|
|
2123
2292
|
});
|
|
2293
|
+
const enterBlockPreviewActivity = (activity) => {
|
|
2294
|
+
if (account.streamingMode !== "block") return;
|
|
2295
|
+
const continuesCurrentActivity = activity === "tool" && blockPreviewActivity === "tool" || activity === "text" && blockPreviewActivity === "text" && !blockPreviewAssistantMessagePending || activity === "reasoning" && blockPreviewActivity === "reasoning";
|
|
2296
|
+
const startsNewGeneration = !continuesCurrentActivity && !(blockPreviewActivity === "reasoning");
|
|
2297
|
+
if (startsNewGeneration) currentAssistantPreviewUsesPrefix = false;
|
|
2298
|
+
const boundarySettled = startsNewGeneration ? previewBoundaryController.noteBoundary() : void 0;
|
|
2299
|
+
if (!continuesCurrentActivity) progressDraft.reset();
|
|
2300
|
+
blockPreviewActivity = activity;
|
|
2301
|
+
blockPreviewAssistantMessagePending = false;
|
|
2302
|
+
if (activity === "tool") lastPartialText = "";
|
|
2303
|
+
return boundarySettled;
|
|
2304
|
+
};
|
|
2124
2305
|
const previewState = { finalizedViaPreviewPost: false };
|
|
2125
|
-
const
|
|
2306
|
+
const resolveFinalDeliveryText = (text) => {
|
|
2126
2307
|
if (typeof text !== "string") return;
|
|
2127
|
-
const
|
|
2128
|
-
|
|
2308
|
+
const resolution = draftStream.resolveFinalText(text);
|
|
2309
|
+
return resolution.kind === "already-delivered" ? "" : resolution.text;
|
|
2310
|
+
};
|
|
2311
|
+
const resolvePreviewFinalText = (text) => {
|
|
2312
|
+
const deliveryText = resolveFinalDeliveryText(text);
|
|
2313
|
+
if (typeof deliveryText !== "string") return;
|
|
2314
|
+
const formatted = core.channel.text.convertMarkdownTables(deliveryText, tableMode);
|
|
2129
2315
|
const chunks = core.channel.text.chunkMarkdownTextWithMode(formatted, textLimit, chunkMode);
|
|
2130
2316
|
if (!chunks.length && formatted) chunks.push(formatted);
|
|
2131
2317
|
if (chunks.length != 1) return;
|
|
@@ -2139,8 +2325,17 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2139
2325
|
if (!cleaned) return;
|
|
2140
2326
|
if (cleaned === lastPartialText) return;
|
|
2141
2327
|
if (lastPartialText && lastPartialText.startsWith(cleaned) && cleaned.length < lastPartialText.length) return;
|
|
2328
|
+
const boundarySettled = enterBlockPreviewActivity("text");
|
|
2142
2329
|
lastPartialText = cleaned;
|
|
2143
|
-
|
|
2330
|
+
if (firstAssistantPreviewPrefixPending) {
|
|
2331
|
+
firstAssistantPreviewPrefix = resolveResponsePrefix?.();
|
|
2332
|
+
firstAssistantPreviewPrefixPending = false;
|
|
2333
|
+
currentAssistantPreviewUsesPrefix = Boolean(firstAssistantPreviewPrefix);
|
|
2334
|
+
}
|
|
2335
|
+
const previewText = currentAssistantPreviewUsesPrefix && firstAssistantPreviewPrefix ? cleaned.startsWith(firstAssistantPreviewPrefix) ? cleaned : `${firstAssistantPreviewPrefix} ${cleaned}` : cleaned;
|
|
2336
|
+
draftStream.updateAssistantText(previewText);
|
|
2337
|
+
previewBoundaryController.noteUpdate();
|
|
2338
|
+
return boundarySettled;
|
|
2144
2339
|
};
|
|
2145
2340
|
const deliveryBarrier = createMattermostReplyDeliveryBarrier({
|
|
2146
2341
|
isDirect: kind === "direct",
|
|
@@ -2153,7 +2348,11 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2153
2348
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
|
|
2154
2349
|
typingCallbacks,
|
|
2155
2350
|
deliver: async (payloadEntry, info) => {
|
|
2156
|
-
if (info.kind === "final")
|
|
2351
|
+
if (info.kind === "final") {
|
|
2352
|
+
await enterBlockPreviewActivity("text");
|
|
2353
|
+
await draftStream.settleBoundaries();
|
|
2354
|
+
progressDraft.markFinalReplyStarted();
|
|
2355
|
+
}
|
|
2157
2356
|
const markThreadParticipation = () => {
|
|
2158
2357
|
if (kind !== "direct" && effectiveReplyToId) recordMattermostThreadParticipation(account.accountId, channelId, effectiveReplyToId, { agentId: route.agentId });
|
|
2159
2358
|
};
|
|
@@ -2169,10 +2368,15 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2169
2368
|
logVerboseMessage,
|
|
2170
2369
|
recordThreadParticipation: markThreadParticipation,
|
|
2171
2370
|
deliverPayload: async (payloadToDeliver) => {
|
|
2371
|
+
const finalTextResolution = info.kind === "final" && !payloadToDeliver.isError && typeof payloadToDeliver.text === "string" ? draftStream.resolveFinalText(payloadToDeliver.text) : void 0;
|
|
2372
|
+
const resolvedPayload = finalTextResolution ? {
|
|
2373
|
+
...payloadToDeliver,
|
|
2374
|
+
text: finalTextResolution.kind === "already-delivered" ? "" : finalTextResolution.text
|
|
2375
|
+
} : payloadToDeliver;
|
|
2172
2376
|
const outcome = await deliverMattermostReplyPayload({
|
|
2173
2377
|
core,
|
|
2174
2378
|
cfg,
|
|
2175
|
-
payload:
|
|
2379
|
+
payload: resolvedPayload,
|
|
2176
2380
|
to,
|
|
2177
2381
|
accountId: account.accountId,
|
|
2178
2382
|
agentId: route.agentId,
|
|
@@ -2187,9 +2391,10 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2187
2391
|
onDmChannelResolution: deliveryBarrier.trackDmChannelResolution
|
|
2188
2392
|
});
|
|
2189
2393
|
if (outcome === "text" || outcome === "media") markThreadParticipation();
|
|
2394
|
+
else if (outcome === "empty" && finalTextResolution?.kind === "already-delivered") markThreadParticipation();
|
|
2190
2395
|
const deliveryLog = formatMattermostFinalDeliveryOutcomeLog({
|
|
2191
2396
|
outcome,
|
|
2192
|
-
payload:
|
|
2397
|
+
payload: resolvedPayload,
|
|
2193
2398
|
to,
|
|
2194
2399
|
accountId: account.accountId,
|
|
2195
2400
|
agentId: route.agentId
|
|
@@ -2275,33 +2480,44 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2275
2480
|
replyOptions: {
|
|
2276
2481
|
...replyOptions,
|
|
2277
2482
|
allowProgressCallbacksWhenSourceDeliverySuppressed: draftToolProgressEnabled ? true : void 0,
|
|
2483
|
+
preserveProgressCallbackStartOrder: draftPreviewEnabled ? true : void 0,
|
|
2278
2484
|
onObservedReplyDelivery: draftToolProgressEnabled ? () => draftStream.clear() : void 0,
|
|
2279
|
-
disableBlockStreaming: true,
|
|
2485
|
+
disableBlockStreaming: draftPreviewEnabled ? true : typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
|
|
2280
2486
|
...suppressDefaultToolProgressMessages ? { suppressDefaultToolProgressMessages: true } : {},
|
|
2281
2487
|
onModelSelected,
|
|
2282
2488
|
onPartialReply: (payloadResult) => {
|
|
2283
|
-
if (account.streamingMode !== "progress") updateDraftFromPartial(payloadResult.text);
|
|
2489
|
+
if (account.streamingMode !== "progress") return updateDraftFromPartial(payloadResult.text);
|
|
2284
2490
|
},
|
|
2285
2491
|
onAssistantMessageStart: () => {
|
|
2286
2492
|
lastPartialText = "";
|
|
2287
2493
|
progressDraft.resetReasoningProgress();
|
|
2494
|
+
if (account.streamingMode === "block") {
|
|
2495
|
+
blockPreviewAssistantMessagePending = true;
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2288
2498
|
if (account.streamingMode !== "progress") progressDraft.reset();
|
|
2289
2499
|
},
|
|
2290
2500
|
onReasoningEnd: () => {
|
|
2291
2501
|
lastPartialText = "";
|
|
2292
2502
|
progressDraft.resetReasoningProgress();
|
|
2293
|
-
if (account.streamingMode !== "progress") progressDraft.reset();
|
|
2503
|
+
if (account.streamingMode !== "block" && account.streamingMode !== "progress") progressDraft.reset();
|
|
2294
2504
|
},
|
|
2295
2505
|
onReasoningStream: async (payloadResult) => {
|
|
2296
2506
|
if (account.streamingMode === "progress") {
|
|
2297
2507
|
await progressDraft.pushReasoningProgress(payloadResult.text || "Thinking…", { snapshot: payloadResult.isReasoningSnapshot === true });
|
|
2298
2508
|
return;
|
|
2299
2509
|
}
|
|
2300
|
-
if (!lastPartialText)
|
|
2510
|
+
if (!lastPartialText) {
|
|
2511
|
+
const boundarySettled = enterBlockPreviewActivity("reasoning");
|
|
2512
|
+
draftStream.update("Thinking…");
|
|
2513
|
+
previewBoundaryController.noteUpdate();
|
|
2514
|
+
await boundarySettled;
|
|
2515
|
+
}
|
|
2301
2516
|
},
|
|
2302
2517
|
onToolStart: async (payloadValue) => {
|
|
2303
2518
|
if (!draftToolProgressEnabled) return;
|
|
2304
|
-
|
|
2519
|
+
const boundarySettled = enterBlockPreviewActivity("tool");
|
|
2520
|
+
const progressSettled = progressDraft.pushToolProgress(buildChannelProgressDraftLineForEntry(account.config, {
|
|
2305
2521
|
event: "tool",
|
|
2306
2522
|
itemId: payloadValue.itemId,
|
|
2307
2523
|
toolCallId: payloadValue.toolCallId,
|
|
@@ -2309,10 +2525,13 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2309
2525
|
phase: payloadValue.phase,
|
|
2310
2526
|
args: payloadValue.args
|
|
2311
2527
|
}, payloadValue.detailMode ? { detailMode: payloadValue.detailMode } : void 0), { startImmediately: true });
|
|
2528
|
+
previewBoundaryController.noteUpdate();
|
|
2529
|
+
await Promise.all([boundarySettled, progressSettled]);
|
|
2312
2530
|
},
|
|
2313
2531
|
onItemEvent: async (payloadLocal) => {
|
|
2314
2532
|
if (!draftToolProgressEnabled) return;
|
|
2315
|
-
|
|
2533
|
+
const boundarySettled = enterBlockPreviewActivity("tool");
|
|
2534
|
+
const progressSettled = progressDraft.pushToolProgress(buildChannelProgressDraftLineForEntry(account.config, {
|
|
2316
2535
|
event: "item",
|
|
2317
2536
|
itemId: payloadLocal.itemId,
|
|
2318
2537
|
itemKind: payloadLocal.kind,
|
|
@@ -2324,6 +2543,8 @@ async function monitorMattermostProvider(opts = {}) {
|
|
|
2324
2543
|
progressText: payloadLocal.progressText,
|
|
2325
2544
|
meta: payloadLocal.meta
|
|
2326
2545
|
}), { startImmediately: true });
|
|
2546
|
+
previewBoundaryController.noteUpdate();
|
|
2547
|
+
await Promise.all([boundarySettled, progressSettled]);
|
|
2327
2548
|
}
|
|
2328
2549
|
}
|
|
2329
2550
|
})
|