@openclaw/msteams 2026.7.2-beta.1 → 2026.7.2-beta.3
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/api.cjs +2 -2
- package/dist/{channel-BSyPdpGN.cjs → channel-oNBYNcJT.cjs} +15 -21
- package/dist/channel-plugin-api.cjs +1 -1
- package/dist/{channel.runtime-DQTOzzet.cjs → channel.runtime-Sn3hY2Lu.cjs} +13 -7
- package/dist/delegated-state-DOSCHaAB.cjs +98 -0
- package/dist/directory-contract-api.cjs +1 -1
- package/dist/doctor-contract-api.cjs +86 -7
- package/dist/{messenger-DtS9gqkV.cjs → messenger-Do4oje4J.cjs} +90 -52
- package/dist/{oauth-C5E5powI.cjs → oauth-BUFvYhIw.cjs} +1 -1
- package/dist/{polls-BhoI4VaN.cjs → polls-CWExq2xZ.cjs} +24 -40
- package/dist/{resolve-allowlist-CyQ57MfH.cjs → resolve-allowlist-Dk-1CqBm.cjs} +41 -27
- package/dist/runtime-api.cjs +0 -7
- package/dist/secret-contract-api.cjs +6 -0
- package/dist/setup-plugin-api.cjs +2 -2
- package/dist/{setup-surface-BDTPN3dX.cjs → setup-surface-BcRVoUY8.cjs} +6 -14
- package/dist/{src-CUMz2B6f.cjs → src-BQM9QUTF.cjs} +455 -311
- package/dist/{sso-token-store-CktCz88k.cjs → sso-token-store-OSg229re.cjs} +4 -4
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/dist/rolldown-runtime-D6vf50IK.cjs +0 -28
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_delegated_state = require("./delegated-state-DOSCHaAB.cjs");
|
|
2
2
|
const require_runtime = require("./runtime-gZTAuFux.cjs");
|
|
3
3
|
require("./runtime-api.cjs");
|
|
4
|
-
const require_resolve_allowlist = require("./resolve-allowlist-
|
|
5
|
-
const require_channel = require("./channel-
|
|
6
|
-
const require_setup_surface = require("./setup-surface-
|
|
7
|
-
const require_polls = require("./polls-
|
|
8
|
-
const require_sso_token_store = require("./sso-token-store-
|
|
9
|
-
const require_messenger = require("./messenger-
|
|
4
|
+
const require_resolve_allowlist = require("./resolve-allowlist-Dk-1CqBm.cjs");
|
|
5
|
+
const require_channel = require("./channel-oNBYNcJT.cjs");
|
|
6
|
+
const require_setup_surface = require("./setup-surface-BcRVoUY8.cjs");
|
|
7
|
+
const require_polls = require("./polls-CWExq2xZ.cjs");
|
|
8
|
+
const require_sso_token_store = require("./sso-token-store-OSg229re.cjs");
|
|
9
|
+
const require_messenger = require("./messenger-Do4oje4J.cjs");
|
|
10
10
|
let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
|
|
11
11
|
let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
|
|
12
12
|
let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
|
|
13
13
|
let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
|
|
14
|
-
let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
|
|
15
14
|
let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
|
|
16
15
|
let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
|
|
17
16
|
let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
|
|
18
17
|
let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
|
|
19
|
-
let node_path = require("node:path");
|
|
20
|
-
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
21
|
-
let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
|
|
22
18
|
let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
|
|
23
19
|
let node_crypto = require("node:crypto");
|
|
24
|
-
node_crypto =
|
|
20
|
+
node_crypto = require_delegated_state.__toESM(node_crypto, 1);
|
|
21
|
+
let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
|
|
25
22
|
let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
|
|
26
23
|
let openclaw_plugin_sdk_context_visibility_runtime = require("openclaw/plugin-sdk/context-visibility-runtime");
|
|
27
24
|
let openclaw_plugin_sdk_reply_history = require("openclaw/plugin-sdk/reply-history");
|
|
25
|
+
let openclaw_plugin_sdk_agent_runtime = require("openclaw/plugin-sdk/agent-runtime");
|
|
28
26
|
let openclaw_plugin_sdk_dedupe_runtime = require("openclaw/plugin-sdk/dedupe-runtime");
|
|
29
27
|
let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
|
|
30
28
|
let openclaw_plugin_sdk_channel_pairing = require("openclaw/plugin-sdk/channel-pairing");
|
|
@@ -33,100 +31,24 @@ let openclaw_plugin_sdk_runtime_group_policy = require("openclaw/plugin-sdk/runt
|
|
|
33
31
|
let openclaw_plugin_sdk_dangerous_name_runtime = require("openclaw/plugin-sdk/dangerous-name-runtime");
|
|
34
32
|
let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
|
|
35
33
|
let openclaw_plugin_sdk_webhook_ingress = require("openclaw/plugin-sdk/webhook-ingress");
|
|
36
|
-
//#region extensions/msteams/src/feedback-reflection-
|
|
37
|
-
/** Max chars of the thumbed-down response to include in the reflection prompt. */
|
|
38
|
-
const MAX_RESPONSE_CHARS = 500;
|
|
39
|
-
function buildReflectionPrompt(params) {
|
|
40
|
-
const parts = ["A user indicated your previous response wasn't helpful."];
|
|
41
|
-
if (params.thumbedDownResponse) {
|
|
42
|
-
const truncated = params.thumbedDownResponse.length > MAX_RESPONSE_CHARS ? `${(0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(params.thumbedDownResponse, MAX_RESPONSE_CHARS)}...` : params.thumbedDownResponse;
|
|
43
|
-
parts.push(`\nYour response was:\n> ${truncated}`);
|
|
44
|
-
}
|
|
45
|
-
if (params.userComment) parts.push(`\nUser's comment: "${params.userComment}"`);
|
|
46
|
-
parts.push("\nBriefly reflect: what could you improve? Consider tone, length, accuracy, relevance, and specificity. Reply with a single JSON object only, no markdown or prose, using this exact shape:\n{\"learning\":\"...\",\"followUp\":false,\"userMessage\":\"\"}\n- learning: a short internal adjustment note (1-2 sentences) for your future behavior in this conversation.\n- followUp: true only if the user needs a direct follow-up message.\n- userMessage: only the exact user-facing message to send; empty string when followUp is false.");
|
|
47
|
-
return parts.join("\n");
|
|
48
|
-
}
|
|
49
|
-
function parseBooleanLike(value) {
|
|
50
|
-
if (typeof value === "boolean") return value;
|
|
51
|
-
if (typeof value === "string") {
|
|
52
|
-
const normalized = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(value);
|
|
53
|
-
if (normalized === "true" || normalized === "yes") return true;
|
|
54
|
-
if (normalized === "false" || normalized === "no") return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
function parseStructuredReflectionValue(value) {
|
|
58
|
-
if (value == null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
59
|
-
const candidate = value;
|
|
60
|
-
const learning = typeof candidate.learning === "string" ? candidate.learning.trim() : void 0;
|
|
61
|
-
if (!learning) return null;
|
|
62
|
-
return {
|
|
63
|
-
learning,
|
|
64
|
-
followUp: parseBooleanLike(candidate.followUp) ?? false,
|
|
65
|
-
userMessage: typeof candidate.userMessage === "string" && candidate.userMessage.trim() ? candidate.userMessage.trim() : void 0
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function parseReflectionResponse(text) {
|
|
69
|
-
const trimmed = text.trim();
|
|
70
|
-
if (!trimmed) return null;
|
|
71
|
-
const candidates = [trimmed, ...trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i)?.slice(1, 2) ?? []];
|
|
72
|
-
for (const candidateText of candidates) {
|
|
73
|
-
const candidate = candidateText.trim();
|
|
74
|
-
if (!candidate) continue;
|
|
75
|
-
try {
|
|
76
|
-
const parsed = parseStructuredReflectionValue(JSON.parse(candidate));
|
|
77
|
-
if (parsed) return parsed;
|
|
78
|
-
} catch {}
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
learning: trimmed,
|
|
82
|
-
followUp: false
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/** Tracks last reflection time per session to enforce cooldown. */
|
|
86
|
-
const lastReflectionBySession = /* @__PURE__ */ new Map();
|
|
87
|
-
/** Maximum cooldown entries before pruning expired ones. */
|
|
88
|
-
const MAX_COOLDOWN_ENTRIES = 500;
|
|
34
|
+
//#region extensions/msteams/src/feedback-reflection-store.ts
|
|
89
35
|
const LEARNINGS_NAMESPACE = "feedback-learnings";
|
|
90
36
|
const MAX_LEARNING_ENTRIES = 1e4;
|
|
91
37
|
function learningStoreKey(storePath, sessionKey) {
|
|
92
38
|
return node_crypto.default.createHash("sha256").update(`${storePath}\0${sessionKey}`, "utf8").digest("hex");
|
|
93
39
|
}
|
|
94
|
-
function
|
|
95
|
-
|
|
40
|
+
async function storeSessionLearning(params) {
|
|
41
|
+
const store = require_runtime.getMSTeamsRuntime().state.openKeyedStore({
|
|
96
42
|
namespace: LEARNINGS_NAMESPACE,
|
|
97
43
|
maxEntries: MAX_LEARNING_ENTRIES
|
|
98
44
|
});
|
|
99
|
-
}
|
|
100
|
-
/** Prune expired cooldown entries to prevent unbounded memory growth. */
|
|
101
|
-
function pruneExpiredCooldowns(cooldownMs) {
|
|
102
|
-
if (lastReflectionBySession.size <= MAX_COOLDOWN_ENTRIES) return;
|
|
103
|
-
const now = Date.now();
|
|
104
|
-
for (const [key, time] of lastReflectionBySession) if (now - time >= cooldownMs) lastReflectionBySession.delete(key);
|
|
105
|
-
}
|
|
106
|
-
/** Check if a reflection is allowed (cooldown not active). */
|
|
107
|
-
function isReflectionAllowed(sessionKey, cooldownMs) {
|
|
108
|
-
const cooldown = cooldownMs ?? 3e5;
|
|
109
|
-
const lastTime = lastReflectionBySession.get(sessionKey);
|
|
110
|
-
if (lastTime == null) return true;
|
|
111
|
-
return Date.now() - lastTime >= cooldown;
|
|
112
|
-
}
|
|
113
|
-
/** Record that a reflection was run for a session. */
|
|
114
|
-
function recordReflectionTime(sessionKey, cooldownMs) {
|
|
115
|
-
lastReflectionBySession.set(sessionKey, Date.now());
|
|
116
|
-
pruneExpiredCooldowns(cooldownMs ?? 3e5);
|
|
117
|
-
}
|
|
118
|
-
/** Store a learning derived from feedback reflection. */
|
|
119
|
-
async function storeSessionLearning(params) {
|
|
120
|
-
const store = openLearningStore();
|
|
121
45
|
const key = learningStoreKey(params.storePath, params.sessionKey);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (learnings.length > 10) learnings = learnings.slice(-10);
|
|
125
|
-
await store.register(key, {
|
|
46
|
+
if (!store.update) throw new Error("plugin state atomic update is unavailable");
|
|
47
|
+
await store.update(key, (existing) => ({
|
|
126
48
|
sessionKey: params.sessionKey,
|
|
127
|
-
learnings,
|
|
49
|
+
learnings: [...existing?.learnings ?? [], params.learning].slice(-10),
|
|
128
50
|
updatedAt: Date.now()
|
|
129
|
-
});
|
|
51
|
+
}));
|
|
130
52
|
}
|
|
131
53
|
//#endregion
|
|
132
54
|
//#region extensions/msteams/src/feedback-reflection.ts
|
|
@@ -143,144 +65,68 @@ function buildFeedbackEvent(params) {
|
|
|
143
65
|
conversationId: params.conversationId
|
|
144
66
|
};
|
|
145
67
|
}
|
|
146
|
-
function buildReflectionContext(params) {
|
|
147
|
-
const core = require_runtime.getMSTeamsRuntime();
|
|
148
|
-
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(params.cfg);
|
|
149
|
-
const body = core.channel.reply.formatAgentEnvelope({
|
|
150
|
-
channel: "Teams",
|
|
151
|
-
from: "system",
|
|
152
|
-
body: params.reflectionPrompt,
|
|
153
|
-
envelope: envelopeOptions
|
|
154
|
-
});
|
|
155
|
-
return { ctxPayload: core.channel.reply.finalizeInboundContext({
|
|
156
|
-
Body: body,
|
|
157
|
-
BodyForAgent: params.reflectionPrompt,
|
|
158
|
-
RawBody: params.reflectionPrompt,
|
|
159
|
-
CommandBody: params.reflectionPrompt,
|
|
160
|
-
From: `msteams:system:${params.conversationId}`,
|
|
161
|
-
To: `conversation:${params.conversationId}`,
|
|
162
|
-
SessionKey: params.sessionKey,
|
|
163
|
-
ChatType: "direct",
|
|
164
|
-
SenderName: "system",
|
|
165
|
-
SenderId: "system",
|
|
166
|
-
Provider: "msteams",
|
|
167
|
-
Surface: "msteams",
|
|
168
|
-
Timestamp: Date.now(),
|
|
169
|
-
WasMentioned: true,
|
|
170
|
-
CommandAuthorized: false,
|
|
171
|
-
OriginatingChannel: "msteams",
|
|
172
|
-
OriginatingTo: `conversation:${params.conversationId}`
|
|
173
|
-
}) };
|
|
174
|
-
}
|
|
175
|
-
function createReflectionCaptureDispatcher(params) {
|
|
176
|
-
const core = require_runtime.getMSTeamsRuntime();
|
|
177
|
-
let response = "";
|
|
178
|
-
const { dispatcher, replyOptions } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
179
|
-
deliver: async (payload) => {
|
|
180
|
-
if (payload.text) response += (response ? "\n" : "") + payload.text;
|
|
181
|
-
},
|
|
182
|
-
typingCallbacks: {
|
|
183
|
-
onReplyStart: async () => {},
|
|
184
|
-
onIdle: () => {},
|
|
185
|
-
onCleanup: () => {}
|
|
186
|
-
},
|
|
187
|
-
humanDelay: core.channel.reply.resolveHumanDelayConfig(params.cfg, params.agentId),
|
|
188
|
-
onError: (err) => {
|
|
189
|
-
params.log.debug?.("reflection reply error", { error: require_setup_surface.formatUnknownError(err) });
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
return {
|
|
193
|
-
dispatcher,
|
|
194
|
-
replyOptions,
|
|
195
|
-
readResponse: () => response
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
async function sendReflectionFollowUp(params) {
|
|
199
|
-
const baseRef = require_messenger.buildConversationReference(params.conversationRef);
|
|
200
|
-
await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, {
|
|
201
|
-
type: "message",
|
|
202
|
-
text: params.userMessage
|
|
203
|
-
}, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(params.cfg.channels?.msteams) });
|
|
204
|
-
}
|
|
205
68
|
/**
|
|
206
69
|
* Run a background reflection after negative feedback.
|
|
207
70
|
* This is designed to be called fire-and-forget (don't await in the invoke handler).
|
|
208
71
|
*/
|
|
209
72
|
async function runFeedbackReflection(params) {
|
|
210
73
|
const { cfg, log, sessionKey } = params;
|
|
211
|
-
const cooldownMs = cfg.channels?.msteams?.feedbackReflectionCooldownMs ??
|
|
212
|
-
|
|
213
|
-
log.debug?.("skipping reflection (cooldown active)", { sessionKey });
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const reflectionPrompt = buildReflectionPrompt({
|
|
217
|
-
thumbedDownResponse: params.thumbedDownResponse,
|
|
218
|
-
userComment: params.userComment
|
|
219
|
-
});
|
|
220
|
-
const storePath = require_runtime.getMSTeamsRuntime().channel.session.resolveStorePath(cfg.session?.store, { agentId: params.agentId });
|
|
221
|
-
const { ctxPayload } = buildReflectionContext({
|
|
222
|
-
cfg,
|
|
223
|
-
conversationId: params.conversationId,
|
|
224
|
-
sessionKey: params.sessionKey,
|
|
225
|
-
reflectionPrompt
|
|
226
|
-
});
|
|
227
|
-
const capture = createReflectionCaptureDispatcher({
|
|
228
|
-
cfg,
|
|
229
|
-
agentId: params.agentId,
|
|
230
|
-
log
|
|
231
|
-
});
|
|
74
|
+
const cooldownMs = cfg.channels?.msteams?.feedbackReflectionCooldownMs ?? openclaw_plugin_sdk_channel_inbound.DEFAULT_CHANNEL_FEEDBACK_REFLECTION_COOLDOWN_MS;
|
|
75
|
+
let reflection;
|
|
232
76
|
try {
|
|
233
|
-
await (0, openclaw_plugin_sdk_channel_inbound.
|
|
234
|
-
ctxPayload,
|
|
77
|
+
reflection = await (0, openclaw_plugin_sdk_channel_inbound.runChannelFeedbackReflection)({
|
|
235
78
|
cfg,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
79
|
+
channel: "msteams",
|
|
80
|
+
channelLabel: "Teams",
|
|
81
|
+
agentId: params.agentId,
|
|
82
|
+
sessionKey,
|
|
83
|
+
conversationId: params.conversationId,
|
|
84
|
+
conversationKind: params.conversationKind,
|
|
85
|
+
thumbedDownResponse: params.thumbedDownResponse,
|
|
86
|
+
userComment: params.userComment,
|
|
87
|
+
cooldownMs,
|
|
88
|
+
onRecordError: (err) => log.debug?.("reflection session record failed", { error: require_setup_surface.formatUnknownError(err) }),
|
|
89
|
+
onDispatchError: (err) => log.debug?.("reflection reply error", { error: require_setup_surface.formatUnknownError(err) })
|
|
239
90
|
});
|
|
240
91
|
} catch (err) {
|
|
241
92
|
log.error("reflection dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
242
93
|
return;
|
|
243
94
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
log.debug?.("reflection produced no output");
|
|
95
|
+
if (reflection.status === "cooldown") {
|
|
96
|
+
log.debug?.("skipping reflection (cooldown active)", { sessionKey });
|
|
247
97
|
return;
|
|
248
98
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
log.debug?.("reflection produced no structured output");
|
|
99
|
+
if (reflection.status === "empty") {
|
|
100
|
+
log.debug?.("reflection produced no output");
|
|
252
101
|
return;
|
|
253
102
|
}
|
|
254
|
-
recordReflectionTime(sessionKey, cooldownMs);
|
|
255
103
|
log.info("reflection complete", {
|
|
256
104
|
sessionKey,
|
|
257
|
-
responseLength:
|
|
258
|
-
followUp:
|
|
105
|
+
responseLength: reflection.responseLength,
|
|
106
|
+
followUp: reflection.followUp
|
|
259
107
|
});
|
|
260
108
|
try {
|
|
261
109
|
await storeSessionLearning({
|
|
262
|
-
storePath,
|
|
263
|
-
sessionKey
|
|
264
|
-
learning:
|
|
110
|
+
storePath: reflection.storePath,
|
|
111
|
+
sessionKey,
|
|
112
|
+
learning: reflection.learning
|
|
265
113
|
});
|
|
266
114
|
} catch (err) {
|
|
267
115
|
log.debug?.("failed to store reflection learning", { error: require_setup_surface.formatUnknownError(err) });
|
|
268
116
|
}
|
|
269
117
|
const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
|
|
270
|
-
if (!(conversationType === "personal" &&
|
|
271
|
-
if (
|
|
118
|
+
if (!(conversationType === "personal" && reflection.followUp && Boolean(reflection.userMessage))) {
|
|
119
|
+
if (reflection.followUp && conversationType !== "personal") log.debug?.("skipping reflection follow-up outside direct message", {
|
|
272
120
|
sessionKey,
|
|
273
121
|
conversationType
|
|
274
122
|
});
|
|
275
123
|
return;
|
|
276
124
|
}
|
|
277
125
|
try {
|
|
278
|
-
await
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
userMessage: parsedReflection.userMessage
|
|
283
|
-
});
|
|
126
|
+
await require_messenger.sendMSTeamsActivityWithReference(params.app, require_messenger.buildConversationReference(params.conversationRef), {
|
|
127
|
+
type: "message",
|
|
128
|
+
text: reflection.userMessage
|
|
129
|
+
}, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(cfg.channels?.msteams) });
|
|
284
130
|
log.info("sent reflection follow-up", { sessionKey });
|
|
285
131
|
} catch (err) {
|
|
286
132
|
log.debug?.("failed to send reflection follow-up", { error: require_setup_surface.formatUnknownError(err) });
|
|
@@ -1461,6 +1307,8 @@ function createTeamsReplyStreamController(params) {
|
|
|
1461
1307
|
let streamFailed = false;
|
|
1462
1308
|
let lastInformativeText = "";
|
|
1463
1309
|
let progressLines = [];
|
|
1310
|
+
let latestPlan;
|
|
1311
|
+
let latestPlanExplanation;
|
|
1464
1312
|
let pendingFinalPayload;
|
|
1465
1313
|
let emittedText = "";
|
|
1466
1314
|
const wasCanceled = () => canceledLocally || Boolean(stream?.canceled);
|
|
@@ -1482,7 +1330,9 @@ function createTeamsReplyStreamController(params) {
|
|
|
1482
1330
|
entry: params.msteamsConfig,
|
|
1483
1331
|
lines: shouldStreamPreviewToolProgress ? progressLines : [],
|
|
1484
1332
|
seed: params.progressSeed,
|
|
1485
|
-
bullet: "-"
|
|
1333
|
+
bullet: "-",
|
|
1334
|
+
narration: latestPlanExplanation,
|
|
1335
|
+
plan: latestPlan
|
|
1486
1336
|
});
|
|
1487
1337
|
if (!informativeText || informativeText === lastInformativeText) return;
|
|
1488
1338
|
lastInformativeText = informativeText;
|
|
@@ -1554,6 +1404,14 @@ function createTeamsReplyStreamController(params) {
|
|
|
1554
1404
|
const progressActive = await progressDraftGate.noteWork();
|
|
1555
1405
|
if ((hadStarted || progressActive) && progressDraftGate.hasStarted) renderInformativeUpdate();
|
|
1556
1406
|
},
|
|
1407
|
+
async pushPlanProgress(steps, options) {
|
|
1408
|
+
if (!stream || streamMode !== "progress" || streamFinalizationPending) return;
|
|
1409
|
+
latestPlan = steps?.length ? steps.map((entry) => ({ ...entry })) : void 0;
|
|
1410
|
+
latestPlanExplanation = options?.explanation?.replace(/\s+/g, " ").trim() || void 0;
|
|
1411
|
+
const hadStarted = progressDraftGate.hasStarted;
|
|
1412
|
+
await progressDraftGate.startNow();
|
|
1413
|
+
if (hadStarted && progressDraftGate.hasStarted) renderInformativeUpdate();
|
|
1414
|
+
},
|
|
1557
1415
|
preparePayload(payload) {
|
|
1558
1416
|
if (!stream) return payload;
|
|
1559
1417
|
if (wasCanceled()) return;
|
|
@@ -1803,14 +1661,16 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1803
1661
|
}
|
|
1804
1662
|
if (ids.length > 0) params.onSentMessageIds?.(ids);
|
|
1805
1663
|
};
|
|
1806
|
-
const
|
|
1664
|
+
const dispatcherOptions = {
|
|
1807
1665
|
...replyPipeline,
|
|
1808
|
-
humanDelay:
|
|
1666
|
+
humanDelay: (0, openclaw_plugin_sdk_agent_runtime.resolveHumanDelayConfig)(params.cfg, params.agentId),
|
|
1809
1667
|
onReplyStart: async () => {
|
|
1810
1668
|
await streamController.onReplyStart();
|
|
1811
1669
|
if (typingIndicatorEnabled) await typingCallbacks?.onReplyStart?.();
|
|
1812
1670
|
},
|
|
1813
|
-
typingCallbacks
|
|
1671
|
+
typingCallbacks
|
|
1672
|
+
};
|
|
1673
|
+
const delivery = {
|
|
1814
1674
|
deliver: async (payload) => {
|
|
1815
1675
|
const preparedPayload = streamController.preparePayload(payload);
|
|
1816
1676
|
if (!preparedPayload) return;
|
|
@@ -1829,8 +1689,8 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1829
1689
|
hint
|
|
1830
1690
|
});
|
|
1831
1691
|
}
|
|
1832
|
-
}
|
|
1833
|
-
const
|
|
1692
|
+
};
|
|
1693
|
+
const settleDelivery = () => {
|
|
1834
1694
|
return flushPendingMessages().catch((err) => {
|
|
1835
1695
|
const errMsg = require_setup_surface.formatUnknownError(err);
|
|
1836
1696
|
const classification = require_setup_surface.classifyMSTeamsSendError(err);
|
|
@@ -1849,8 +1709,6 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1849
1709
|
queueReplyPayload(fallbackPayload);
|
|
1850
1710
|
await flushPendingMessages();
|
|
1851
1711
|
}
|
|
1852
|
-
}).finally(() => {
|
|
1853
|
-
baseMarkDispatchIdle();
|
|
1854
1712
|
});
|
|
1855
1713
|
};
|
|
1856
1714
|
const previewToolProgressEnabled = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(msteamsCfg);
|
|
@@ -1901,13 +1759,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1901
1759
|
},
|
|
1902
1760
|
onPlanUpdate: async (payload) => {
|
|
1903
1761
|
if (payload?.phase !== "update") return;
|
|
1904
|
-
await streamController.
|
|
1905
|
-
event: "plan",
|
|
1906
|
-
phase: payload.phase,
|
|
1907
|
-
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
1908
|
-
...typeof payload?.explanation === "string" ? { explanation: payload.explanation } : {},
|
|
1909
|
-
...Array.isArray(payload?.steps) && payload.steps.every((s) => typeof s === "string") ? { steps: payload.steps } : {}
|
|
1910
|
-
}));
|
|
1762
|
+
await streamController.pushPlanProgress((0, openclaw_plugin_sdk_channel_outbound.normalizeAgentPlanSteps)(payload.steps), { explanation: typeof payload.explanation === "string" ? payload.explanation : void 0 });
|
|
1911
1763
|
},
|
|
1912
1764
|
onApprovalEvent: async (payload) => {
|
|
1913
1765
|
if (payload?.phase !== "requested") return;
|
|
@@ -1950,16 +1802,18 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1950
1802
|
}
|
|
1951
1803
|
} : {};
|
|
1952
1804
|
return {
|
|
1953
|
-
|
|
1805
|
+
dispatcherOptions: {
|
|
1806
|
+
...dispatcherOptions,
|
|
1807
|
+
onSettled: settleDelivery
|
|
1808
|
+
},
|
|
1809
|
+
delivery,
|
|
1954
1810
|
replyOptions: {
|
|
1955
|
-
...replyOptions,
|
|
1956
1811
|
...streamController.hasStream() ? { onPartialReply: (payload) => streamController.onPartialReply(payload) } : {},
|
|
1957
1812
|
...progressCallbacks,
|
|
1958
1813
|
...shouldSuppressDefaultToolProgressMessages ? { suppressDefaultToolProgressMessages: true } : {},
|
|
1959
1814
|
disableBlockStreaming: blockStreamingResolved == null ? void 0 : !blockStreamingResolved,
|
|
1960
1815
|
onModelSelected
|
|
1961
|
-
}
|
|
1962
|
-
markDispatchIdle
|
|
1816
|
+
}
|
|
1963
1817
|
};
|
|
1964
1818
|
}
|
|
1965
1819
|
//#endregion
|
|
@@ -2575,12 +2429,17 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2575
2429
|
const replies = repliesResult.status === "fulfilled" ? repliesResult.value : [];
|
|
2576
2430
|
if (parentResult.status === "rejected") log.debug?.("failed to fetch parent message", { error: require_setup_surface.formatUnknownError(parentResult.reason) });
|
|
2577
2431
|
if (repliesResult.status === "rejected") log.debug?.("failed to fetch thread replies", { error: require_setup_surface.formatUnknownError(repliesResult.reason) });
|
|
2578
|
-
const isThreadSenderAllowed = (msg) =>
|
|
2432
|
+
const isThreadSenderAllowed = (msg) => (0, openclaw_plugin_sdk_channel_inbound.resolveInboundSupplementalSenderAllowed)({
|
|
2433
|
+
isGroup: isChannel,
|
|
2434
|
+
groupPolicy,
|
|
2579
2435
|
allowFrom: effectiveGroupAllowFrom,
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2436
|
+
isSenderAllowed: (allowFrom) => require_channel.resolveMSTeamsAllowlistMatch({
|
|
2437
|
+
allowFrom,
|
|
2438
|
+
senderId: msg.from?.user?.id ?? "",
|
|
2439
|
+
senderName: msg.from?.user?.displayName,
|
|
2440
|
+
allowNameMatching
|
|
2441
|
+
}).allowed
|
|
2442
|
+
});
|
|
2584
2443
|
const parentSummary = summarizeParentMessage(parentMsg);
|
|
2585
2444
|
const visibleParentMessages = parentMsg ? (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
|
|
2586
2445
|
items: [parentMsg],
|
|
@@ -2611,17 +2470,14 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2611
2470
|
}
|
|
2612
2471
|
quoteSenderName ??= quoteInfo?.sender;
|
|
2613
2472
|
const envelopeFrom = isDirectMessage ? senderName : conversationType;
|
|
2614
|
-
const
|
|
2473
|
+
const buildEnvelope = (0, openclaw_plugin_sdk_channel_inbound.createChannelInboundEnvelopeBuilder)({
|
|
2615
2474
|
cfg,
|
|
2616
|
-
|
|
2617
|
-
sessionKey: route.sessionKey
|
|
2475
|
+
route
|
|
2618
2476
|
});
|
|
2619
|
-
let combinedBody =
|
|
2477
|
+
let combinedBody = buildEnvelope({
|
|
2620
2478
|
channel: "Teams",
|
|
2621
2479
|
from: envelopeFrom,
|
|
2622
2480
|
timestamp,
|
|
2623
|
-
previousTimestamp,
|
|
2624
|
-
envelope: envelopeOptions,
|
|
2625
2481
|
body: agentBody
|
|
2626
2482
|
});
|
|
2627
2483
|
const isRoomish = !isDirectMessage;
|
|
@@ -2630,12 +2486,12 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2630
2486
|
historyKey,
|
|
2631
2487
|
limit: historyLimit,
|
|
2632
2488
|
currentMessage: combinedBody,
|
|
2633
|
-
formatEntry: (entry) =>
|
|
2489
|
+
formatEntry: (entry) => buildEnvelope({
|
|
2634
2490
|
channel: "Teams",
|
|
2635
2491
|
from: conversationType,
|
|
2636
2492
|
timestamp: entry.timestamp,
|
|
2637
|
-
|
|
2638
|
-
|
|
2493
|
+
previousTimestamp: null,
|
|
2494
|
+
body: `${entry.sender}: ${entry.body}${entry.messageId ? ` [id:${entry.messageId}]` : ""}`
|
|
2639
2495
|
})
|
|
2640
2496
|
});
|
|
2641
2497
|
const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildInboundHistory({
|
|
@@ -2643,17 +2499,21 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2643
2499
|
limit: historyLimit
|
|
2644
2500
|
}) : void 0;
|
|
2645
2501
|
const commandBody = text.trim();
|
|
2646
|
-
const quoteSenderAllowed = quoteInfo && quoteInfo.sender ?
|
|
2502
|
+
const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? (0, openclaw_plugin_sdk_channel_inbound.resolveInboundSupplementalSenderAllowed)({
|
|
2503
|
+
isGroup: !isDirectMessage,
|
|
2504
|
+
groupPolicy,
|
|
2647
2505
|
allowFrom: effectiveGroupAllowFrom,
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2506
|
+
isSenderAllowed: (allowFrom) => require_channel.resolveMSTeamsAllowlistMatch({
|
|
2507
|
+
allowFrom,
|
|
2508
|
+
senderId: quoteSenderId ?? "",
|
|
2509
|
+
senderName: quoteSenderName,
|
|
2510
|
+
allowNameMatching
|
|
2511
|
+
}).allowed
|
|
2512
|
+
}) : true;
|
|
2652
2513
|
const bodyForAgent = threadContext ? `[Thread history]\n${threadContext}\n[/Thread history]\n\n${agentBody}` : agentBody;
|
|
2653
2514
|
const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
|
|
2654
2515
|
const ctxPayload = (0, openclaw_plugin_sdk_channel_inbound.buildChannelInboundEventContext)({
|
|
2655
2516
|
channel: "msteams",
|
|
2656
|
-
finalize: core.channel.reply.finalizeInboundContext,
|
|
2657
2517
|
contextVisibility: contextVisibilityMode,
|
|
2658
2518
|
supplemental: { quote: quoteInfo ? {
|
|
2659
2519
|
id: quoteInfo.id ?? activity.replyToId ?? void 0,
|
|
@@ -2678,6 +2538,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2678
2538
|
},
|
|
2679
2539
|
route: {
|
|
2680
2540
|
agentId: route.agentId,
|
|
2541
|
+
dmScope: route.dmScope,
|
|
2681
2542
|
accountId: route.accountId,
|
|
2682
2543
|
routeSessionKey: route.sessionKey
|
|
2683
2544
|
},
|
|
@@ -2698,7 +2559,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2698
2559
|
canDetectMention: !isDirectMessage,
|
|
2699
2560
|
wasMentioned: isDirectMessage || mentionDecision.effectiveWasMentioned
|
|
2700
2561
|
},
|
|
2701
|
-
commands: { authorized: commandAuthorized }
|
|
2562
|
+
commands: { authorized: commandAuthorized === true }
|
|
2702
2563
|
},
|
|
2703
2564
|
extra: {
|
|
2704
2565
|
GroupSubject: !isDirectMessage ? conversationType : void 0,
|
|
@@ -2708,7 +2569,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2708
2569
|
});
|
|
2709
2570
|
logVerboseMessage(`msteams inbound: from=${ctxPayload.From} preview="${preview}"`);
|
|
2710
2571
|
const sharePointSiteId = msteamsCfg?.sharePointSiteId;
|
|
2711
|
-
const {
|
|
2572
|
+
const { dispatcherOptions, delivery, replyOptions } = createMSTeamsReplyDispatcher({
|
|
2712
2573
|
cfg,
|
|
2713
2574
|
agentId: route.agentId,
|
|
2714
2575
|
sessionKey: route.sessionKey,
|
|
@@ -2728,10 +2589,16 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2728
2589
|
sharePointSiteId
|
|
2729
2590
|
});
|
|
2730
2591
|
const senderTimezone = (activity.entities?.find((e) => e.type === "clientInfo"))?.timezone || conversationRef.timezone;
|
|
2731
|
-
const
|
|
2732
|
-
...cfg
|
|
2733
|
-
|
|
2734
|
-
|
|
2592
|
+
const turnConfig = senderTimezone && !cfg.agents?.defaults?.userTimezone ? {
|
|
2593
|
+
...cfg,
|
|
2594
|
+
agents: {
|
|
2595
|
+
...cfg.agents,
|
|
2596
|
+
defaults: {
|
|
2597
|
+
...cfg.agents?.defaults,
|
|
2598
|
+
userTimezone: senderTimezone
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
} : cfg;
|
|
2735
2602
|
log.info("dispatching to agent", { sessionKey: route.sessionKey });
|
|
2736
2603
|
try {
|
|
2737
2604
|
const turnResult = await core.channel.inbound.run({
|
|
@@ -2748,12 +2615,14 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2748
2615
|
raw: activity
|
|
2749
2616
|
}),
|
|
2750
2617
|
resolveTurn: () => ({
|
|
2618
|
+
cfg: turnConfig,
|
|
2751
2619
|
channel: "msteams",
|
|
2752
2620
|
accountId: route.accountId,
|
|
2753
|
-
|
|
2754
|
-
|
|
2621
|
+
route: {
|
|
2622
|
+
agentId: route.agentId,
|
|
2623
|
+
sessionKey: route.sessionKey
|
|
2624
|
+
},
|
|
2755
2625
|
ctxPayload,
|
|
2756
|
-
recordInboundSession: core.channel.session.recordInboundSession,
|
|
2757
2626
|
record: { onRecordError: (err) => {
|
|
2758
2627
|
logVerboseMessage(`msteams: failed updating session meta: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2759
2628
|
} },
|
|
@@ -2763,18 +2632,12 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2763
2632
|
historyMap: conversationHistories,
|
|
2764
2633
|
limit: historyLimit
|
|
2765
2634
|
},
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
ctxPayload,
|
|
2773
|
-
dispatcher,
|
|
2774
|
-
onSettled: () => markDispatchIdle(),
|
|
2775
|
-
replyOptions,
|
|
2776
|
-
configOverride
|
|
2777
|
-
})
|
|
2635
|
+
dispatcherOptions,
|
|
2636
|
+
delivery,
|
|
2637
|
+
replyOptions: {
|
|
2638
|
+
...replyOptions,
|
|
2639
|
+
...params.turnAdoptionLifecycle ? (0, openclaw_plugin_sdk_channel_outbound.bindIngressLifecycleToReplyOptions)(params.turnAdoptionLifecycle) : {}
|
|
2640
|
+
}
|
|
2778
2641
|
})
|
|
2779
2642
|
}
|
|
2780
2643
|
});
|
|
@@ -2792,11 +2655,47 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2792
2655
|
} catch (err) {
|
|
2793
2656
|
log.error("dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
2794
2657
|
runtime.error(`msteams dispatch failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2658
|
+
if (params.turnAdoptionLifecycle) throw err;
|
|
2795
2659
|
try {
|
|
2796
2660
|
await context.sendActivity("⚠️ Something went wrong. Please try again.");
|
|
2797
2661
|
} catch {}
|
|
2798
2662
|
}
|
|
2799
2663
|
};
|
|
2664
|
+
function buildFlushIngressLifecycle(entries) {
|
|
2665
|
+
const lifecycles = entries.map((entry) => entry.turnAdoptionLifecycle).filter((lifecycle) => lifecycle !== void 0);
|
|
2666
|
+
const [firstLifecycle] = lifecycles;
|
|
2667
|
+
if (!firstLifecycle) return {
|
|
2668
|
+
lifecycle: void 0,
|
|
2669
|
+
settle: async () => {}
|
|
2670
|
+
};
|
|
2671
|
+
let handedOff = false;
|
|
2672
|
+
const adoptAll = async () => {
|
|
2673
|
+
for (const lifecycle of lifecycles) await lifecycle.onAdopted();
|
|
2674
|
+
};
|
|
2675
|
+
return {
|
|
2676
|
+
lifecycle: {
|
|
2677
|
+
abortSignal: lifecycles.length === 1 ? firstLifecycle.abortSignal : AbortSignal.any(lifecycles.map((lifecycle) => lifecycle.abortSignal)),
|
|
2678
|
+
onAdopted: async () => {
|
|
2679
|
+
handedOff = true;
|
|
2680
|
+
await adoptAll();
|
|
2681
|
+
},
|
|
2682
|
+
onDeferred: () => {
|
|
2683
|
+
handedOff = true;
|
|
2684
|
+
for (const lifecycle of lifecycles) lifecycle.onDeferred();
|
|
2685
|
+
},
|
|
2686
|
+
onAdoptionFinalizing: () => {
|
|
2687
|
+
for (const lifecycle of lifecycles) lifecycle.onAdoptionFinalizing();
|
|
2688
|
+
},
|
|
2689
|
+
onAbandoned: async () => {
|
|
2690
|
+
handedOff = true;
|
|
2691
|
+
for (const lifecycle of lifecycles) await lifecycle.onAbandoned();
|
|
2692
|
+
}
|
|
2693
|
+
},
|
|
2694
|
+
settle: async () => {
|
|
2695
|
+
if (!handedOff) await adoptAll();
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2800
2699
|
const inboundDebouncer = core.channel.debounce.createInboundDebouncer({
|
|
2801
2700
|
debounceMs: inboundDebounceMs,
|
|
2802
2701
|
buildKey: (entry) => {
|
|
@@ -2813,29 +2712,40 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2813
2712
|
onFlush: async (entries) => {
|
|
2814
2713
|
const last = entries.at(-1);
|
|
2815
2714
|
if (!last) return;
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2715
|
+
const { lifecycle, settle } = buildFlushIngressLifecycle(entries);
|
|
2716
|
+
try {
|
|
2717
|
+
if (entries.length === 1) await handleTeamsMessageNow(lifecycle ? {
|
|
2718
|
+
...last,
|
|
2719
|
+
turnAdoptionLifecycle: lifecycle
|
|
2720
|
+
} : last);
|
|
2721
|
+
else {
|
|
2722
|
+
const combinedText = entries.map((entry) => entry.text).filter(Boolean).join("\n");
|
|
2723
|
+
if (combinedText.trim()) {
|
|
2724
|
+
const combinedRawText = entries.map((entry) => entry.rawText).filter(Boolean).join("\n");
|
|
2725
|
+
const wasMentioned = entries.some((entry) => entry.wasMentioned);
|
|
2726
|
+
const implicitMentionKinds = entries.flatMap((entry) => entry.implicitMentionKinds);
|
|
2727
|
+
await handleTeamsMessageNow({
|
|
2728
|
+
context: last.context,
|
|
2729
|
+
rawText: combinedRawText,
|
|
2730
|
+
text: combinedText,
|
|
2731
|
+
attachments: [],
|
|
2732
|
+
wasMentioned,
|
|
2733
|
+
implicitMentionKinds,
|
|
2734
|
+
...lifecycle ? { turnAdoptionLifecycle: lifecycle } : {}
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
await settle();
|
|
2739
|
+
} catch (err) {
|
|
2740
|
+
await lifecycle?.onAbandoned();
|
|
2741
|
+
throw err;
|
|
2819
2742
|
}
|
|
2820
|
-
const combinedText = entries.map((entry) => entry.text).filter(Boolean).join("\n");
|
|
2821
|
-
if (!combinedText.trim()) return;
|
|
2822
|
-
const combinedRawText = entries.map((entry) => entry.rawText).filter(Boolean).join("\n");
|
|
2823
|
-
const wasMentioned = entries.some((entry) => entry.wasMentioned);
|
|
2824
|
-
const implicitMentionKinds = entries.flatMap((entry) => entry.implicitMentionKinds);
|
|
2825
|
-
await handleTeamsMessageNow({
|
|
2826
|
-
context: last.context,
|
|
2827
|
-
rawText: combinedRawText,
|
|
2828
|
-
text: combinedText,
|
|
2829
|
-
attachments: [],
|
|
2830
|
-
wasMentioned,
|
|
2831
|
-
implicitMentionKinds
|
|
2832
|
-
});
|
|
2833
2743
|
},
|
|
2834
2744
|
onError: (err) => {
|
|
2835
2745
|
runtime.error(`msteams debounce flush failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2836
2746
|
}
|
|
2837
2747
|
});
|
|
2838
|
-
return async function handleTeamsMessage(context) {
|
|
2748
|
+
return async function handleTeamsMessage(context, turnAdoptionLifecycle) {
|
|
2839
2749
|
const activity = context.activity;
|
|
2840
2750
|
const attachments = Array.isArray(activity.attachments) ? activity.attachments : [];
|
|
2841
2751
|
const rawText = activity.text?.trim() ?? "";
|
|
@@ -2855,8 +2765,10 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2855
2765
|
text,
|
|
2856
2766
|
attachments,
|
|
2857
2767
|
wasMentioned,
|
|
2858
|
-
implicitMentionKinds
|
|
2768
|
+
implicitMentionKinds,
|
|
2769
|
+
turnAdoptionLifecycle
|
|
2859
2770
|
});
|
|
2771
|
+
if (turnAdoptionLifecycle) return { kind: "deferred" };
|
|
2860
2772
|
};
|
|
2861
2773
|
}
|
|
2862
2774
|
//#endregion
|
|
@@ -3053,29 +2965,37 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3053
2965
|
const handleTeamsMessage = createMSTeamsMessageHandler(deps);
|
|
3054
2966
|
const handleReaction = createMSTeamsReactionHandler(deps);
|
|
3055
2967
|
const originalRun = handler.run;
|
|
3056
|
-
if (originalRun) handler.run = async (context) => {
|
|
2968
|
+
if (originalRun) handler.run = async (context, turnAdoptionLifecycle) => {
|
|
3057
2969
|
const ctx = context;
|
|
3058
2970
|
if (ctx.activity?.type === "invoke" && ctx.activity?.name === "adaptiveCard/action") {
|
|
3059
2971
|
const text = serializeMSTeamsAdaptiveCardActionValue(ctx.activity?.value);
|
|
3060
|
-
if (text) await handleTeamsMessage({
|
|
2972
|
+
if (text) return await handleTeamsMessage({
|
|
3061
2973
|
...ctx,
|
|
3062
2974
|
activity: {
|
|
3063
2975
|
...ctx.activity,
|
|
3064
2976
|
type: "message",
|
|
3065
2977
|
text
|
|
3066
2978
|
}
|
|
3067
|
-
});
|
|
2979
|
+
}, turnAdoptionLifecycle);
|
|
3068
2980
|
return;
|
|
3069
2981
|
}
|
|
3070
|
-
return originalRun.call(handler, context);
|
|
2982
|
+
return originalRun.call(handler, context, turnAdoptionLifecycle);
|
|
3071
2983
|
};
|
|
3072
|
-
handler.onMessage(async (context, next) => {
|
|
2984
|
+
handler.onMessage(async (context, next, turnAdoptionLifecycle) => {
|
|
2985
|
+
let nextRan = false;
|
|
2986
|
+
const runNext = async () => {
|
|
2987
|
+
nextRan = true;
|
|
2988
|
+
await next();
|
|
2989
|
+
};
|
|
3073
2990
|
try {
|
|
3074
|
-
await handleTeamsMessage(context);
|
|
2991
|
+
const result = await handleTeamsMessage(context, turnAdoptionLifecycle);
|
|
2992
|
+
await runNext();
|
|
2993
|
+
return result;
|
|
3075
2994
|
} catch (err) {
|
|
2995
|
+
if (turnAdoptionLifecycle) throw err;
|
|
3076
2996
|
deps.runtime.error(`msteams handler failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
3077
2997
|
}
|
|
3078
|
-
await
|
|
2998
|
+
if (!nextRan) await runNext();
|
|
3079
2999
|
});
|
|
3080
3000
|
handler.onMembersAdded(async (context, next) => {
|
|
3081
3001
|
const ctx = context;
|
|
@@ -3176,8 +3096,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3176
3096
|
const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
|
|
3177
3097
|
const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
|
|
3178
3098
|
const isChannel = convType === "channel";
|
|
3179
|
-
const
|
|
3180
|
-
const route = core.channel.routing.resolveAgentRoute({
|
|
3099
|
+
const route = require_runtime.getMSTeamsRuntime().channel.routing.resolveAgentRoute({
|
|
3181
3100
|
cfg: deps.cfg,
|
|
3182
3101
|
channel: "msteams",
|
|
3183
3102
|
peer: {
|
|
@@ -3206,13 +3125,12 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3206
3125
|
hasComment: Boolean(userComment)
|
|
3207
3126
|
});
|
|
3208
3127
|
try {
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
}).catch(() => {});
|
|
3128
|
+
await (0, openclaw_plugin_sdk_channel_inbound.recordChannelFeedbackEvent)({
|
|
3129
|
+
cfg: deps.cfg,
|
|
3130
|
+
agentId: route.agentId,
|
|
3131
|
+
sessionKey: route.sessionKey,
|
|
3132
|
+
event: feedbackEvent
|
|
3133
|
+
});
|
|
3216
3134
|
} catch {}
|
|
3217
3135
|
const conversationRef = {
|
|
3218
3136
|
activityId: activity.id,
|
|
@@ -3237,12 +3155,11 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3237
3155
|
if (isNegative && msteamsCfg?.feedbackReflection !== false) runFeedbackReflection({
|
|
3238
3156
|
cfg: deps.cfg,
|
|
3239
3157
|
app: deps.app,
|
|
3240
|
-
appId: deps.appId,
|
|
3241
3158
|
conversationRef,
|
|
3242
3159
|
sessionKey: route.sessionKey,
|
|
3243
3160
|
agentId: route.agentId,
|
|
3244
3161
|
conversationId,
|
|
3245
|
-
|
|
3162
|
+
conversationKind: isDirectMessage ? "direct" : isChannel ? "channel" : "group",
|
|
3246
3163
|
userComment,
|
|
3247
3164
|
log: deps.log
|
|
3248
3165
|
}).catch((err) => {
|
|
@@ -3360,6 +3277,175 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
|
|
|
3360
3277
|
}
|
|
3361
3278
|
}
|
|
3362
3279
|
//#endregion
|
|
3280
|
+
//#region extensions/msteams/src/msteams-ingress.ts
|
|
3281
|
+
const MSTEAMS_INGRESS_VERSION = 1;
|
|
3282
|
+
const MSTEAMS_INGRESS_DRAIN_INTERVAL_MS = 500;
|
|
3283
|
+
const MSTEAMS_INGRESS_MAX_CONCURRENT_DELIVERIES = 8;
|
|
3284
|
+
const MSTEAMS_INGRESS_SCAN_LIMIT = 100;
|
|
3285
|
+
const MSTEAMS_INGRESS_TOMBSTONE_TTL_MS = 720 * 60 * 6e4;
|
|
3286
|
+
const MSTEAMS_INGRESS_COMPLETED_MAX_ENTRIES = 2e4;
|
|
3287
|
+
const MSTEAMS_INGRESS_FAILED_MAX_ENTRIES = 4096;
|
|
3288
|
+
var MSTeamsIngressPayloadError = class extends Error {
|
|
3289
|
+
constructor(reason, message, options) {
|
|
3290
|
+
super(message, options);
|
|
3291
|
+
this.reason = reason;
|
|
3292
|
+
this.name = "MSTeamsIngressPayloadError";
|
|
3293
|
+
}
|
|
3294
|
+
};
|
|
3295
|
+
function nonEmptyString(value) {
|
|
3296
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
3297
|
+
}
|
|
3298
|
+
function isDispatchableActivity(activity) {
|
|
3299
|
+
return activity.type === "message" || activity.type === "invoke" && activity.name === "adaptiveCard/action";
|
|
3300
|
+
}
|
|
3301
|
+
function inspectMSTeamsIngressActivity(activity) {
|
|
3302
|
+
if (!isDispatchableActivity(activity)) return null;
|
|
3303
|
+
const eventId = nonEmptyString(activity.id);
|
|
3304
|
+
if (!eventId) throw new MSTeamsIngressPayloadError("invalid-activity", "Microsoft Teams dispatchable activity is missing activity.id.");
|
|
3305
|
+
const laneKey = nonEmptyString(activity.conversation?.id);
|
|
3306
|
+
if (!laneKey) throw new MSTeamsIngressPayloadError("invalid-activity", "Microsoft Teams dispatchable activity is missing conversation.id.");
|
|
3307
|
+
return {
|
|
3308
|
+
eventId,
|
|
3309
|
+
laneKey
|
|
3310
|
+
};
|
|
3311
|
+
}
|
|
3312
|
+
function parseClaimedActivity(payload, claimedId) {
|
|
3313
|
+
if (payload.version !== MSTEAMS_INGRESS_VERSION || typeof payload.rawActivity !== "string" || !Number.isFinite(payload.receivedAt)) throw new MSTeamsIngressPayloadError("invalid-activity", "Microsoft Teams ingress payload is invalid.");
|
|
3314
|
+
let activity;
|
|
3315
|
+
try {
|
|
3316
|
+
activity = JSON.parse(payload.rawActivity);
|
|
3317
|
+
} catch (error) {
|
|
3318
|
+
throw new MSTeamsIngressPayloadError("invalid-json", "Microsoft Teams ingress activity JSON is invalid.", { cause: error });
|
|
3319
|
+
}
|
|
3320
|
+
if (!activity || typeof activity !== "object" || Array.isArray(activity)) throw new MSTeamsIngressPayloadError("invalid-activity", "Microsoft Teams ingress activity must be an object.");
|
|
3321
|
+
const parsed = activity;
|
|
3322
|
+
const facts = inspectMSTeamsIngressActivity(parsed);
|
|
3323
|
+
if (!facts) throw new MSTeamsIngressPayloadError("unsupported-activity", "Microsoft Teams ingress row is not an agent-turn activity.");
|
|
3324
|
+
if (facts.eventId !== claimedId) throw new MSTeamsIngressPayloadError("invalid-activity", "Microsoft Teams activity id changed after durable admission.");
|
|
3325
|
+
return parsed;
|
|
3326
|
+
}
|
|
3327
|
+
function errorText(error) {
|
|
3328
|
+
return error instanceof Error ? error.message : String(error);
|
|
3329
|
+
}
|
|
3330
|
+
function createMSTeamsIngress(options) {
|
|
3331
|
+
const queue = options.queue ?? require_runtime.getMSTeamsRuntime().state.openChannelIngressQueue({ accountId: options.accountId });
|
|
3332
|
+
const shutdown = new AbortController();
|
|
3333
|
+
const liveContexts = /* @__PURE__ */ new Map();
|
|
3334
|
+
const activeDeliveries = /* @__PURE__ */ new Set();
|
|
3335
|
+
const drain = (0, openclaw_plugin_sdk_channel_outbound.createChannelIngressDrain)({
|
|
3336
|
+
queue,
|
|
3337
|
+
abortSignal: shutdown.signal,
|
|
3338
|
+
orderBy: "received",
|
|
3339
|
+
scanLimit: MSTEAMS_INGRESS_SCAN_LIMIT,
|
|
3340
|
+
startLimit: MSTEAMS_INGRESS_MAX_CONCURRENT_DELIVERIES,
|
|
3341
|
+
retryPolicy: {
|
|
3342
|
+
maxAttempts: openclaw_plugin_sdk_channel_outbound.DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS,
|
|
3343
|
+
deadLetterMinAgeMs: openclaw_plugin_sdk_channel_outbound.DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS
|
|
3344
|
+
},
|
|
3345
|
+
resolveNonRetryableFailure: (error) => {
|
|
3346
|
+
if (error instanceof MSTeamsIngressPayloadError) return {
|
|
3347
|
+
reason: error.reason,
|
|
3348
|
+
message: error.message
|
|
3349
|
+
};
|
|
3350
|
+
return require_setup_surface.classifyMSTeamsSendError(error).kind === "auth" ? {
|
|
3351
|
+
reason: "authentication-failed",
|
|
3352
|
+
message: errorText(error)
|
|
3353
|
+
} : null;
|
|
3354
|
+
},
|
|
3355
|
+
onLog: (message) => options.runtime.error?.(`msteams: ${message}`),
|
|
3356
|
+
dispatchClaimedEvent: async (claimed, lifecycle) => {
|
|
3357
|
+
const activity = parseClaimedActivity(claimed.payload, claimed.id);
|
|
3358
|
+
const liveContext = liveContexts.get(claimed.id);
|
|
3359
|
+
liveContexts.delete(claimed.id);
|
|
3360
|
+
const delivery = Promise.resolve(options.dispatch(activity, lifecycle, liveContext));
|
|
3361
|
+
activeDeliveries.add(delivery);
|
|
3362
|
+
try {
|
|
3363
|
+
return await delivery;
|
|
3364
|
+
} finally {
|
|
3365
|
+
activeDeliveries.delete(delivery);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
});
|
|
3369
|
+
let running = false;
|
|
3370
|
+
let stopped = false;
|
|
3371
|
+
let drainRequested = false;
|
|
3372
|
+
let drainTask;
|
|
3373
|
+
let drainTimer;
|
|
3374
|
+
const requestDrain = () => {
|
|
3375
|
+
if (!running || shutdown.signal.aborted) return;
|
|
3376
|
+
drainRequested = true;
|
|
3377
|
+
if (drainTask) return;
|
|
3378
|
+
drainTask = (async () => {
|
|
3379
|
+
while (drainRequested && !shutdown.signal.aborted) {
|
|
3380
|
+
if (!running) break;
|
|
3381
|
+
drainRequested = false;
|
|
3382
|
+
await drain.drainOnce({ shouldStop: () => !running || shutdown.signal.aborted || activeDeliveries.size >= MSTEAMS_INGRESS_MAX_CONCURRENT_DELIVERIES });
|
|
3383
|
+
}
|
|
3384
|
+
})().catch((error) => {
|
|
3385
|
+
options.runtime.error?.(`msteams ingress drain failed: ${errorText(error)}`);
|
|
3386
|
+
}).finally(() => {
|
|
3387
|
+
drainTask = void 0;
|
|
3388
|
+
if (running && drainRequested && !shutdown.signal.aborted) requestDrain();
|
|
3389
|
+
});
|
|
3390
|
+
};
|
|
3391
|
+
return {
|
|
3392
|
+
accept: async (activity, liveContext) => {
|
|
3393
|
+
const facts = inspectMSTeamsIngressActivity(activity);
|
|
3394
|
+
if (!facts) return;
|
|
3395
|
+
await queue.prune({
|
|
3396
|
+
completedTtlMs: MSTEAMS_INGRESS_TOMBSTONE_TTL_MS,
|
|
3397
|
+
completedMaxEntries: MSTEAMS_INGRESS_COMPLETED_MAX_ENTRIES,
|
|
3398
|
+
failedTtlMs: MSTEAMS_INGRESS_TOMBSTONE_TTL_MS,
|
|
3399
|
+
failedMaxEntries: MSTEAMS_INGRESS_FAILED_MAX_ENTRIES
|
|
3400
|
+
});
|
|
3401
|
+
const receivedAt = Date.now();
|
|
3402
|
+
const installedLiveContext = Boolean(liveContext) && !liveContexts.has(facts.eventId);
|
|
3403
|
+
if (liveContext && installedLiveContext) liveContexts.set(facts.eventId, liveContext);
|
|
3404
|
+
const uninstallLiveContext = () => {
|
|
3405
|
+
if (installedLiveContext && liveContexts.get(facts.eventId) === liveContext) liveContexts.delete(facts.eventId);
|
|
3406
|
+
};
|
|
3407
|
+
let result;
|
|
3408
|
+
try {
|
|
3409
|
+
result = await queue.enqueue(facts.eventId, {
|
|
3410
|
+
version: MSTEAMS_INGRESS_VERSION,
|
|
3411
|
+
receivedAt,
|
|
3412
|
+
rawActivity: JSON.stringify(activity)
|
|
3413
|
+
}, {
|
|
3414
|
+
receivedAt,
|
|
3415
|
+
laneKey: facts.laneKey
|
|
3416
|
+
});
|
|
3417
|
+
} catch (error) {
|
|
3418
|
+
uninstallLiveContext();
|
|
3419
|
+
throw error;
|
|
3420
|
+
}
|
|
3421
|
+
if (!(result.kind === "accepted" || result.kind === "pending")) uninstallLiveContext();
|
|
3422
|
+
requestDrain();
|
|
3423
|
+
},
|
|
3424
|
+
start: () => {
|
|
3425
|
+
if (running || stopped) return;
|
|
3426
|
+
running = true;
|
|
3427
|
+
requestDrain();
|
|
3428
|
+
drainTimer = setInterval(requestDrain, MSTEAMS_INGRESS_DRAIN_INTERVAL_MS);
|
|
3429
|
+
drainTimer.unref?.();
|
|
3430
|
+
},
|
|
3431
|
+
stop: async () => {
|
|
3432
|
+
if (stopped) return;
|
|
3433
|
+
stopped = true;
|
|
3434
|
+
running = false;
|
|
3435
|
+
if (drainTimer) {
|
|
3436
|
+
clearInterval(drainTimer);
|
|
3437
|
+
drainTimer = void 0;
|
|
3438
|
+
}
|
|
3439
|
+
await drainTask;
|
|
3440
|
+
await Promise.allSettled(activeDeliveries);
|
|
3441
|
+
await drain.waitForIdle();
|
|
3442
|
+
shutdown.abort();
|
|
3443
|
+
drain.dispose();
|
|
3444
|
+
liveContexts.clear();
|
|
3445
|
+
}
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
//#endregion
|
|
3363
3449
|
//#region extensions/msteams/src/webhook-timeouts.ts
|
|
3364
3450
|
const MSTEAMS_WEBHOOK_INACTIVITY_TIMEOUT_MS = 3e4;
|
|
3365
3451
|
const MSTEAMS_WEBHOOK_REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -3542,6 +3628,15 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3542
3628
|
log
|
|
3543
3629
|
};
|
|
3544
3630
|
registerMSTeamsHandlers(handler, handlerDeps);
|
|
3631
|
+
const ingress = createMSTeamsIngress({
|
|
3632
|
+
accountId: appId,
|
|
3633
|
+
runtime,
|
|
3634
|
+
dispatch: async (activity, lifecycle, liveContext) => {
|
|
3635
|
+
if (liveContext) liveContext.activity = activity;
|
|
3636
|
+
const context = liveContext ?? createMSTeamsReplayContext(activity, app, require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg));
|
|
3637
|
+
return await handler.run(context, lifecycle);
|
|
3638
|
+
}
|
|
3639
|
+
});
|
|
3545
3640
|
app.on("card.action", async (ctx) => {
|
|
3546
3641
|
const adaptedCtx = adaptSdkContext(ctx, app);
|
|
3547
3642
|
try {
|
|
@@ -3618,9 +3713,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3618
3713
|
};
|
|
3619
3714
|
}
|
|
3620
3715
|
}
|
|
3621
|
-
|
|
3622
|
-
log.error("msteams card.action dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3623
|
-
});
|
|
3716
|
+
await ingress.accept(activity, adaptedCtx);
|
|
3624
3717
|
return {
|
|
3625
3718
|
statusCode: 200,
|
|
3626
3719
|
type: "application/vnd.microsoft.activity.message",
|
|
@@ -3702,24 +3795,30 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3702
3795
|
if (!await runMSTeamsFeedbackInvokeHandler(adaptSdkContext(ctx, app), handlerDeps)) await ctx.next?.call(ctx);
|
|
3703
3796
|
});
|
|
3704
3797
|
app.on("activity", async (ctx) => {
|
|
3798
|
+
const adaptedCtx = adaptSdkContext(ctx, app);
|
|
3799
|
+
const activity = adaptedCtx.activity;
|
|
3800
|
+
if (activity?.type === "invoke") {
|
|
3801
|
+
if (activity?.name === "adaptiveCard/action") return;
|
|
3802
|
+
if (activity?.name === "fileConsent/invoke") return;
|
|
3803
|
+
if (activity?.name === "signin/tokenExchange" || activity?.name === "signin/verifyState") return;
|
|
3804
|
+
}
|
|
3805
|
+
if (activity?.type === "message") {
|
|
3806
|
+
await ingress.accept(activity, adaptedCtx);
|
|
3807
|
+
return;
|
|
3808
|
+
}
|
|
3705
3809
|
try {
|
|
3706
|
-
const adaptedCtx = adaptSdkContext(ctx, app);
|
|
3707
|
-
const activity = adaptedCtx.activity;
|
|
3708
|
-
if (activity?.type === "invoke") {
|
|
3709
|
-
if (activity?.name === "adaptiveCard/action") return;
|
|
3710
|
-
if (activity?.name === "fileConsent/invoke") return;
|
|
3711
|
-
if (activity?.name === "signin/tokenExchange" || activity?.name === "signin/verifyState") return;
|
|
3712
|
-
}
|
|
3713
3810
|
await handler.run(adaptedCtx);
|
|
3714
3811
|
} catch (err) {
|
|
3715
|
-
log.error("msteams
|
|
3812
|
+
log.error("msteams non-turn activity failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3716
3813
|
}
|
|
3717
3814
|
});
|
|
3718
3815
|
await app.initialize();
|
|
3816
|
+
ingress.start();
|
|
3719
3817
|
const httpServer = await new Promise((resolve, reject) => {
|
|
3720
3818
|
const server = expressApp.listen(port, (err) => err ? reject(err) : resolve(server));
|
|
3721
|
-
}).catch((err) => {
|
|
3819
|
+
}).catch(async (err) => {
|
|
3722
3820
|
log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3821
|
+
await ingress.stop();
|
|
3723
3822
|
throw err;
|
|
3724
3823
|
});
|
|
3725
3824
|
log.info(`msteams provider started on port ${port}`);
|
|
@@ -3729,12 +3828,13 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3729
3828
|
});
|
|
3730
3829
|
const shutdown = async () => {
|
|
3731
3830
|
log.info("shutting down msteams provider");
|
|
3732
|
-
|
|
3831
|
+
await new Promise((resolve) => {
|
|
3733
3832
|
httpServer.close((err) => {
|
|
3734
3833
|
if (err) log.debug?.("msteams server close error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3735
3834
|
resolve();
|
|
3736
3835
|
});
|
|
3737
3836
|
});
|
|
3837
|
+
await ingress.stop();
|
|
3738
3838
|
};
|
|
3739
3839
|
await (0, openclaw_plugin_sdk_channel_outbound.keepHttpServerTaskAlive)({
|
|
3740
3840
|
server: httpServer,
|
|
@@ -3772,11 +3872,14 @@ function buildActivityHandler() {
|
|
|
3772
3872
|
reactionsRemovedHandlers.push(cb);
|
|
3773
3873
|
return handler;
|
|
3774
3874
|
},
|
|
3775
|
-
async run(context) {
|
|
3875
|
+
async run(context, turnAdoptionLifecycle) {
|
|
3776
3876
|
const ctx = context;
|
|
3777
3877
|
const activityType = ctx?.activity?.type;
|
|
3778
3878
|
const noop = async () => {};
|
|
3779
|
-
if (activityType === "message") for (const h of messageHandlers)
|
|
3879
|
+
if (activityType === "message") for (const h of messageHandlers) {
|
|
3880
|
+
const result = await h(context, noop, turnAdoptionLifecycle);
|
|
3881
|
+
if (result) return result;
|
|
3882
|
+
}
|
|
3780
3883
|
else if (activityType === "conversationUpdate") for (const h of membersAddedHandlers) await h(context, noop);
|
|
3781
3884
|
else if (activityType === "messageReaction") {
|
|
3782
3885
|
const activity = ctx?.activity;
|
|
@@ -3787,6 +3890,47 @@ function buildActivityHandler() {
|
|
|
3787
3890
|
};
|
|
3788
3891
|
return handler;
|
|
3789
3892
|
}
|
|
3893
|
+
function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
|
|
3894
|
+
const rawConversationId = activity.conversation?.id ?? "";
|
|
3895
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
|
|
3896
|
+
const conversationType = activity.conversation?.conversationType ?? "personal";
|
|
3897
|
+
const threadActivityId = conversationType.toLowerCase() === "channel" ? require_channel.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId : void 0;
|
|
3898
|
+
const tenantId = activity.channelData?.tenant?.id ?? activity.conversation?.tenantId;
|
|
3899
|
+
const reference = {
|
|
3900
|
+
activityId: activity.id,
|
|
3901
|
+
user: activity.from,
|
|
3902
|
+
agent: activity.recipient,
|
|
3903
|
+
conversation: {
|
|
3904
|
+
id: conversationId,
|
|
3905
|
+
conversationType,
|
|
3906
|
+
...tenantId ? { tenantId } : {}
|
|
3907
|
+
},
|
|
3908
|
+
channelId: activity.channelId,
|
|
3909
|
+
serviceUrl: activity.serviceUrl,
|
|
3910
|
+
locale: activity.locale,
|
|
3911
|
+
...tenantId ? { tenantId } : {},
|
|
3912
|
+
...activity.from?.aadObjectId ? { aadObjectId: activity.from.aadObjectId } : {}
|
|
3913
|
+
};
|
|
3914
|
+
const proactiveOptions = {
|
|
3915
|
+
...threadActivityId ? { threadActivityId } : {},
|
|
3916
|
+
serviceUrlBoundary
|
|
3917
|
+
};
|
|
3918
|
+
const sendActivity = (outbound) => require_messenger.sendMSTeamsActivityWithReference(app, reference, outbound, proactiveOptions);
|
|
3919
|
+
return {
|
|
3920
|
+
activity,
|
|
3921
|
+
sendActivity,
|
|
3922
|
+
sendActivities: async (activities) => {
|
|
3923
|
+
const results = [];
|
|
3924
|
+
for (const outbound of activities) results.push(await sendActivity(outbound));
|
|
3925
|
+
return results;
|
|
3926
|
+
},
|
|
3927
|
+
updateActivity: async (outbound) => await require_messenger.updateMSTeamsActivityWithReference(app, reference, typeof outbound.id === "string" ? outbound.id : "", outbound, proactiveOptions),
|
|
3928
|
+
deleteActivity: async (activityId) => {
|
|
3929
|
+
await require_messenger.deleteMSTeamsActivityWithReference(app, reference, activityId, proactiveOptions);
|
|
3930
|
+
},
|
|
3931
|
+
getTeamDetails: (teamId) => app.api.teams.getById(teamId)
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3790
3934
|
/**
|
|
3791
3935
|
* Adapt a new @microsoft/teams.apps SDK context to the MSTeamsTurnContext interface
|
|
3792
3936
|
* our handlers expect. The new SDK uses reply()/send() instead of sendActivity().
|