@openclaw/msteams 2026.5.2 → 2026.5.3-beta.2
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.js +3 -0
- package/dist/channel-D7hdreTh.js +984 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-BC1ruIfN.js +573 -0
- package/dist/config-schema-B8QezH6t.js +15 -0
- package/dist/contract-api.js +2 -0
- package/dist/graph-users-9uQJepqr.js +1354 -0
- package/dist/index.js +22 -0
- package/dist/oauth-BWJyilR1.js +114 -0
- package/dist/oauth.token-xxpoLWy5.js +115 -0
- package/dist/policy-DTnU2GR7.js +142 -0
- package/dist/probe-D_H8yFps.js +2194 -0
- package/dist/resolve-allowlist-D41JSziq.js +219 -0
- package/dist/runtime-api-DV1iVMn1.js +28 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-BuoEXmPS.js +35 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +64 -0
- package/dist/setup-surface-BLkFQYIQ.js +313 -0
- package/dist/src-CFp1QpFd.js +4064 -0
- package/dist/test-api.js +2 -0
- package/package.json +14 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -2
- package/config-api.ts +0 -4
- package/contract-api.ts +0 -4
- package/index.ts +0 -20
- package/runtime-api.ts +0 -73
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/ai-entity.ts +0 -7
- package/src/approval-auth.ts +0 -44
- package/src/attachments/bot-framework.test.ts +0 -461
- package/src/attachments/bot-framework.ts +0 -362
- package/src/attachments/download.ts +0 -311
- package/src/attachments/graph.test.ts +0 -416
- package/src/attachments/graph.ts +0 -484
- package/src/attachments/html.ts +0 -122
- package/src/attachments/payload.ts +0 -14
- package/src/attachments/remote-media.test.ts +0 -137
- package/src/attachments/remote-media.ts +0 -112
- package/src/attachments/shared.test.ts +0 -530
- package/src/attachments/shared.ts +0 -626
- package/src/attachments/types.ts +0 -47
- package/src/attachments.graph.test.ts +0 -342
- package/src/attachments.helpers.test.ts +0 -246
- package/src/attachments.test-helpers.ts +0 -17
- package/src/attachments.test.ts +0 -687
- package/src/attachments.ts +0 -18
- package/src/block-streaming-config.test.ts +0 -61
- package/src/channel-api.ts +0 -1
- package/src/channel.actions.test.ts +0 -742
- package/src/channel.directory.test.ts +0 -200
- package/src/channel.runtime.ts +0 -56
- package/src/channel.setup.ts +0 -77
- package/src/channel.test.ts +0 -128
- package/src/channel.ts +0 -1136
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -12
- package/src/conversation-store-fs.test.ts +0 -74
- package/src/conversation-store-fs.ts +0 -149
- package/src/conversation-store-helpers.test.ts +0 -202
- package/src/conversation-store-helpers.ts +0 -105
- package/src/conversation-store-memory.ts +0 -51
- package/src/conversation-store.shared.test.ts +0 -225
- package/src/conversation-store.ts +0 -71
- package/src/directory-live.test.ts +0 -156
- package/src/directory-live.ts +0 -111
- package/src/doctor.ts +0 -27
- package/src/errors.test.ts +0 -133
- package/src/errors.ts +0 -246
- package/src/feedback-reflection-prompt.ts +0 -117
- package/src/feedback-reflection-store.ts +0 -114
- package/src/feedback-reflection.test.ts +0 -237
- package/src/feedback-reflection.ts +0 -283
- package/src/file-consent-helpers.test.ts +0 -326
- package/src/file-consent-helpers.ts +0 -126
- package/src/file-consent-invoke.ts +0 -150
- package/src/file-consent.test.ts +0 -363
- package/src/file-consent.ts +0 -287
- package/src/graph-chat.ts +0 -55
- package/src/graph-group-management.test.ts +0 -318
- package/src/graph-group-management.ts +0 -168
- package/src/graph-members.test.ts +0 -89
- package/src/graph-members.ts +0 -48
- package/src/graph-messages.actions.test.ts +0 -243
- package/src/graph-messages.read.test.ts +0 -391
- package/src/graph-messages.search.test.ts +0 -213
- package/src/graph-messages.test-helpers.ts +0 -50
- package/src/graph-messages.ts +0 -534
- package/src/graph-teams.test.ts +0 -215
- package/src/graph-teams.ts +0 -114
- package/src/graph-thread.test.ts +0 -246
- package/src/graph-thread.ts +0 -146
- package/src/graph-upload.test.ts +0 -258
- package/src/graph-upload.ts +0 -531
- package/src/graph-users.ts +0 -29
- package/src/graph.test.ts +0 -516
- package/src/graph.ts +0 -293
- package/src/inbound.test.ts +0 -221
- package/src/inbound.ts +0 -148
- package/src/index.ts +0 -4
- package/src/media-helpers.test.ts +0 -202
- package/src/media-helpers.ts +0 -105
- package/src/mentions.test.ts +0 -244
- package/src/mentions.ts +0 -114
- package/src/messenger.test.ts +0 -865
- package/src/messenger.ts +0 -605
- package/src/monitor-handler/access.ts +0 -125
- package/src/monitor-handler/inbound-media.test.ts +0 -289
- package/src/monitor-handler/inbound-media.ts +0 -180
- package/src/monitor-handler/message-handler-mock-support.test-support.ts +0 -28
- package/src/monitor-handler/message-handler.authz.test.ts +0 -669
- package/src/monitor-handler/message-handler.dm-media.test.ts +0 -54
- package/src/monitor-handler/message-handler.test-support.ts +0 -100
- package/src/monitor-handler/message-handler.thread-parent.test.ts +0 -223
- package/src/monitor-handler/message-handler.thread-session.test.ts +0 -77
- package/src/monitor-handler/message-handler.ts +0 -1000
- package/src/monitor-handler/reaction-handler.test.ts +0 -267
- package/src/monitor-handler/reaction-handler.ts +0 -210
- package/src/monitor-handler/thread-session.ts +0 -17
- package/src/monitor-handler.adaptive-card.test.ts +0 -162
- package/src/monitor-handler.feedback-authz.test.ts +0 -314
- package/src/monitor-handler.file-consent.test.ts +0 -423
- package/src/monitor-handler.sso.test.ts +0 -563
- package/src/monitor-handler.test-helpers.ts +0 -180
- package/src/monitor-handler.ts +0 -534
- package/src/monitor-handler.types.ts +0 -27
- package/src/monitor-types.ts +0 -6
- package/src/monitor.lifecycle.test.ts +0 -278
- package/src/monitor.test.ts +0 -119
- package/src/monitor.ts +0 -442
- package/src/oauth.flow.ts +0 -77
- package/src/oauth.shared.ts +0 -37
- package/src/oauth.test.ts +0 -305
- package/src/oauth.token.ts +0 -158
- package/src/oauth.ts +0 -130
- package/src/outbound.test.ts +0 -130
- package/src/outbound.ts +0 -71
- package/src/pending-uploads-fs.test.ts +0 -246
- package/src/pending-uploads-fs.ts +0 -235
- package/src/pending-uploads.test.ts +0 -173
- package/src/pending-uploads.ts +0 -121
- package/src/policy.test.ts +0 -240
- package/src/policy.ts +0 -262
- package/src/polls-store-memory.ts +0 -32
- package/src/polls.test.ts +0 -160
- package/src/polls.ts +0 -323
- package/src/presentation.ts +0 -68
- package/src/probe.test.ts +0 -77
- package/src/probe.ts +0 -132
- package/src/reply-dispatcher.test.ts +0 -437
- package/src/reply-dispatcher.ts +0 -346
- package/src/reply-stream-controller.test.ts +0 -235
- package/src/reply-stream-controller.ts +0 -147
- package/src/resolve-allowlist.test.ts +0 -250
- package/src/resolve-allowlist.ts +0 -309
- package/src/revoked-context.ts +0 -17
- package/src/runtime.ts +0 -9
- package/src/sdk-types.ts +0 -59
- package/src/sdk.test.ts +0 -666
- package/src/sdk.ts +0 -884
- package/src/secret-contract.ts +0 -49
- package/src/secret-input.ts +0 -7
- package/src/send-context.ts +0 -231
- package/src/send.test.ts +0 -493
- package/src/send.ts +0 -637
- package/src/sent-message-cache.test.ts +0 -15
- package/src/sent-message-cache.ts +0 -56
- package/src/session-route.ts +0 -40
- package/src/setup-core.ts +0 -160
- package/src/setup-surface.test.ts +0 -202
- package/src/setup-surface.ts +0 -320
- package/src/sso-token-store.test.ts +0 -72
- package/src/sso-token-store.ts +0 -166
- package/src/sso.ts +0 -300
- package/src/storage.ts +0 -25
- package/src/store-fs.ts +0 -44
- package/src/streaming-message.test.ts +0 -262
- package/src/streaming-message.ts +0 -297
- package/src/test-runtime.ts +0 -16
- package/src/thread-parent-context.test.ts +0 -224
- package/src/thread-parent-context.ts +0 -159
- package/src/token-response.ts +0 -11
- package/src/token.test.ts +0 -259
- package/src/token.ts +0 -195
- package/src/user-agent.test.ts +0 -86
- package/src/user-agent.ts +0 -53
- package/src/webhook-timeouts.ts +0 -27
- package/src/welcome-card.test.ts +0 -81
- package/src/welcome-card.ts +0 -57
- package/test-api.ts +0 -1
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,984 @@
|
|
|
1
|
+
import { o as buildProbeChannelStatusSummary, r as PAIRING_APPROVED_MESSAGE, s as chunkTextForOutbound, t as DEFAULT_ACCOUNT_ID, u as createDefaultChannelRuntimeState } from "./runtime-api-DV1iVMn1.js";
|
|
2
|
+
import { h as resolveMSTeamsCredentials } from "./graph-users-9uQJepqr.js";
|
|
3
|
+
import { a as parseMSTeamsTeamChannelInput, c as resolveMSTeamsUserAllowlist, i as parseMSTeamsConversationId, n as normalizeMSTeamsMessagingTarget, r as normalizeMSTeamsUserInput, s as resolveMSTeamsChannelAllowlist, t as looksLikeMSTeamsTargetId } from "./resolve-allowlist-D41JSziq.js";
|
|
4
|
+
import { t as MSTeamsChannelConfigSchema } from "./config-schema-B8QezH6t.js";
|
|
5
|
+
import { r as resolveMSTeamsGroupToolPolicy } from "./policy-DTnU2GR7.js";
|
|
6
|
+
import { i as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-BLkFQYIQ.js";
|
|
7
|
+
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
8
|
+
import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
|
9
|
+
import { createTopLevelChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
10
|
+
import { buildChannelOutboundSessionRoute, createChatChannelPlugin, stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/channel-core";
|
|
11
|
+
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
12
|
+
import { createAllowlistProviderGroupPolicyWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector, projectConfigWarningCollector } from "openclaw/plugin-sdk/channel-policy";
|
|
13
|
+
import { createChannelDirectoryAdapter, createRuntimeDirectoryLiveAdapter, listDirectoryEntriesFromSources } from "openclaw/plugin-sdk/directory-runtime";
|
|
14
|
+
import { normalizeMessagePresentation } from "openclaw/plugin-sdk/interactive-runtime";
|
|
15
|
+
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
16
|
+
import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
|
|
17
|
+
import { createComputedAccountStatusAdapter } from "openclaw/plugin-sdk/status-helpers";
|
|
18
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
|
19
|
+
import { Type } from "typebox";
|
|
20
|
+
import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
21
|
+
//#region extensions/msteams/src/approval-auth.ts
|
|
22
|
+
const MSTEAMS_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23
|
+
function normalizeMSTeamsApproverId(value) {
|
|
24
|
+
const normalized = normalizeMSTeamsMessagingTarget(String(value));
|
|
25
|
+
if (!normalized?.startsWith("user:")) return;
|
|
26
|
+
const id = normalizeOptionalLowercaseString(normalized.slice(5));
|
|
27
|
+
if (!id) return;
|
|
28
|
+
return MSTEAMS_ID_RE.test(id) ? id : void 0;
|
|
29
|
+
}
|
|
30
|
+
function resolveMSTeamsChannelConfig$1(cfg) {
|
|
31
|
+
return cfg.channels?.msteams;
|
|
32
|
+
}
|
|
33
|
+
const msTeamsApprovalAuth = createResolvedApproverActionAuthAdapter({
|
|
34
|
+
channelLabel: "Microsoft Teams",
|
|
35
|
+
resolveApprovers: ({ cfg }) => {
|
|
36
|
+
const channel = resolveMSTeamsChannelConfig$1(cfg);
|
|
37
|
+
return resolveApprovalApprovers({
|
|
38
|
+
allowFrom: channel?.allowFrom,
|
|
39
|
+
defaultTo: channel?.defaultTo,
|
|
40
|
+
normalizeApprover: normalizeMSTeamsApproverId
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
normalizeSenderId: (value) => {
|
|
44
|
+
const trimmed = normalizeOptionalLowercaseString(value);
|
|
45
|
+
if (!trimmed) return;
|
|
46
|
+
return MSTEAMS_ID_RE.test(trimmed) ? trimmed : void 0;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region extensions/msteams/src/doctor.ts
|
|
51
|
+
function isMSTeamsMutableAllowEntry(raw) {
|
|
52
|
+
const text = raw.trim();
|
|
53
|
+
if (!text || text === "*") return false;
|
|
54
|
+
const withoutPrefix = text.replace(/^(msteams|user):/i, "").trim();
|
|
55
|
+
return /\s/.test(withoutPrefix) || withoutPrefix.includes("@");
|
|
56
|
+
}
|
|
57
|
+
const collectMSTeamsMutableAllowlistWarnings = createDangerousNameMatchingMutableAllowlistWarningCollector({
|
|
58
|
+
channel: "msteams",
|
|
59
|
+
detector: isMSTeamsMutableAllowEntry,
|
|
60
|
+
collectLists: (scope) => [{
|
|
61
|
+
pathLabel: `${scope.prefix}.allowFrom`,
|
|
62
|
+
list: scope.account.allowFrom
|
|
63
|
+
}, {
|
|
64
|
+
pathLabel: `${scope.prefix}.groupAllowFrom`,
|
|
65
|
+
list: scope.account.groupAllowFrom
|
|
66
|
+
}]
|
|
67
|
+
});
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region extensions/msteams/src/presentation.ts
|
|
70
|
+
function buildMSTeamsPresentationCard(params) {
|
|
71
|
+
const body = [];
|
|
72
|
+
const text = normalizeOptionalString(params.text);
|
|
73
|
+
if (text) body.push({
|
|
74
|
+
type: "TextBlock",
|
|
75
|
+
text,
|
|
76
|
+
wrap: true
|
|
77
|
+
});
|
|
78
|
+
const { presentation } = params;
|
|
79
|
+
if (presentation.title) body.push({
|
|
80
|
+
type: "TextBlock",
|
|
81
|
+
text: presentation.title,
|
|
82
|
+
weight: "Bolder",
|
|
83
|
+
size: "Medium",
|
|
84
|
+
wrap: true
|
|
85
|
+
});
|
|
86
|
+
const actions = [];
|
|
87
|
+
for (const block of presentation.blocks) {
|
|
88
|
+
if (block.type === "text" || block.type === "context") {
|
|
89
|
+
body.push({
|
|
90
|
+
type: "TextBlock",
|
|
91
|
+
text: block.text,
|
|
92
|
+
wrap: true,
|
|
93
|
+
...block.type === "context" ? {
|
|
94
|
+
isSubtle: true,
|
|
95
|
+
size: "Small"
|
|
96
|
+
} : {}
|
|
97
|
+
});
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (block.type === "divider") {
|
|
101
|
+
body.push({
|
|
102
|
+
type: "TextBlock",
|
|
103
|
+
text: "---",
|
|
104
|
+
wrap: true,
|
|
105
|
+
isSubtle: true
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (block.type === "buttons") for (const button of block.buttons) {
|
|
110
|
+
if (button.url) {
|
|
111
|
+
actions.push({
|
|
112
|
+
type: "Action.OpenUrl",
|
|
113
|
+
title: button.label,
|
|
114
|
+
url: button.url
|
|
115
|
+
});
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (button.value) actions.push({
|
|
119
|
+
type: "Action.Submit",
|
|
120
|
+
title: button.label,
|
|
121
|
+
data: {
|
|
122
|
+
value: button.value,
|
|
123
|
+
label: button.label
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
type: "AdaptiveCard",
|
|
130
|
+
version: "1.4",
|
|
131
|
+
body,
|
|
132
|
+
...actions.length ? { actions } : {}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region extensions/msteams/src/session-route.ts
|
|
137
|
+
function resolveMSTeamsOutboundSessionRoute(params) {
|
|
138
|
+
let trimmed = stripChannelTargetPrefix(params.target, "msteams", "teams");
|
|
139
|
+
if (!trimmed) return null;
|
|
140
|
+
const isUser = normalizeLowercaseStringOrEmpty(trimmed).startsWith("user:");
|
|
141
|
+
const rawId = stripTargetKindPrefix(trimmed);
|
|
142
|
+
if (!rawId) return null;
|
|
143
|
+
const conversationId = rawId.split(";")[0] ?? rawId;
|
|
144
|
+
const isChannel = !isUser && /@thread\.tacv2/i.test(conversationId);
|
|
145
|
+
return buildChannelOutboundSessionRoute({
|
|
146
|
+
cfg: params.cfg,
|
|
147
|
+
agentId: params.agentId,
|
|
148
|
+
channel: "msteams",
|
|
149
|
+
accountId: params.accountId,
|
|
150
|
+
peer: {
|
|
151
|
+
kind: isUser ? "direct" : isChannel ? "channel" : "group",
|
|
152
|
+
id: conversationId
|
|
153
|
+
},
|
|
154
|
+
chatType: isUser ? "direct" : isChannel ? "channel" : "group",
|
|
155
|
+
from: isUser ? `msteams:${conversationId}` : isChannel ? `msteams:channel:${conversationId}` : `msteams:group:${conversationId}`,
|
|
156
|
+
to: isUser ? `user:${conversationId}` : `conversation:${conversationId}`
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region extensions/msteams/src/channel.ts
|
|
161
|
+
const meta = {
|
|
162
|
+
id: "msteams",
|
|
163
|
+
label: "Microsoft Teams",
|
|
164
|
+
selectionLabel: "Microsoft Teams (Bot Framework)",
|
|
165
|
+
docsPath: "/channels/msteams",
|
|
166
|
+
docsLabel: "msteams",
|
|
167
|
+
blurb: "Teams SDK; enterprise support.",
|
|
168
|
+
aliases: ["teams"],
|
|
169
|
+
order: 60
|
|
170
|
+
};
|
|
171
|
+
const TEAMS_GRAPH_PERMISSION_HINTS = {
|
|
172
|
+
"ChannelMessage.Read.All": "channel history",
|
|
173
|
+
"Chat.Read.All": "chat history",
|
|
174
|
+
"Channel.ReadBasic.All": "channel list",
|
|
175
|
+
"Team.ReadBasic.All": "team list",
|
|
176
|
+
"TeamsActivity.Read.All": "teams activity",
|
|
177
|
+
"Sites.Read.All": "files (SharePoint)",
|
|
178
|
+
"Files.Read.All": "files (OneDrive)"
|
|
179
|
+
};
|
|
180
|
+
const collectMSTeamsSecurityWarnings = createAllowlistProviderGroupPolicyWarningCollector({
|
|
181
|
+
providerConfigPresent: (cfg) => cfg.channels?.msteams !== void 0,
|
|
182
|
+
resolveGroupPolicy: ({ cfg }) => cfg.channels?.msteams?.groupPolicy,
|
|
183
|
+
collect: ({ groupPolicy }) => groupPolicy === "open" ? ["- MS Teams groups: groupPolicy=\"open\" allows any member to trigger (mention-gated). Set channels.msteams.groupPolicy=\"allowlist\" + channels.msteams.groupAllowFrom to restrict senders."] : []
|
|
184
|
+
});
|
|
185
|
+
const loadMSTeamsChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-BC1ruIfN.js"), "msTeamsChannelRuntime");
|
|
186
|
+
const resolveMSTeamsChannelConfig = (cfg) => ({
|
|
187
|
+
allowFrom: cfg.channels?.msteams?.allowFrom,
|
|
188
|
+
defaultTo: cfg.channels?.msteams?.defaultTo
|
|
189
|
+
});
|
|
190
|
+
const msteamsConfigAdapter = createTopLevelChannelConfigAdapter({
|
|
191
|
+
sectionKey: "msteams",
|
|
192
|
+
resolveAccount: (cfg) => ({
|
|
193
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
194
|
+
enabled: cfg.channels?.msteams?.enabled !== false,
|
|
195
|
+
configured: Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams))
|
|
196
|
+
}),
|
|
197
|
+
resolveAccessorAccount: ({ cfg }) => resolveMSTeamsChannelConfig(cfg),
|
|
198
|
+
resolveAllowFrom: (account) => account.allowFrom,
|
|
199
|
+
formatAllowFrom: (allowFrom) => formatAllowFromLowercase({ allowFrom }),
|
|
200
|
+
resolveDefaultTo: (account) => account.defaultTo
|
|
201
|
+
});
|
|
202
|
+
function jsonActionResult(data) {
|
|
203
|
+
return {
|
|
204
|
+
content: [{
|
|
205
|
+
type: "text",
|
|
206
|
+
text: JSON.stringify(data)
|
|
207
|
+
}],
|
|
208
|
+
details: data
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function jsonMSTeamsActionResult(action, data = {}) {
|
|
212
|
+
return jsonActionResult({
|
|
213
|
+
channel: "msteams",
|
|
214
|
+
action,
|
|
215
|
+
...data
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function jsonMSTeamsOkActionResult(action, data = {}) {
|
|
219
|
+
return jsonActionResult({
|
|
220
|
+
ok: true,
|
|
221
|
+
channel: "msteams",
|
|
222
|
+
action,
|
|
223
|
+
...data
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function jsonMSTeamsConversationResult(conversationId) {
|
|
227
|
+
return jsonActionResultWithDetails({
|
|
228
|
+
ok: true,
|
|
229
|
+
channel: "msteams",
|
|
230
|
+
conversationId
|
|
231
|
+
}, {
|
|
232
|
+
ok: true,
|
|
233
|
+
channel: "msteams"
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function jsonActionResultWithDetails(contentData, details) {
|
|
237
|
+
return {
|
|
238
|
+
content: [{
|
|
239
|
+
type: "text",
|
|
240
|
+
text: JSON.stringify(contentData)
|
|
241
|
+
}],
|
|
242
|
+
details
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const MSTEAMS_REACTION_TYPES = [
|
|
246
|
+
"like",
|
|
247
|
+
"heart",
|
|
248
|
+
"laugh",
|
|
249
|
+
"surprised",
|
|
250
|
+
"sad",
|
|
251
|
+
"angry"
|
|
252
|
+
];
|
|
253
|
+
function actionError(message) {
|
|
254
|
+
return {
|
|
255
|
+
isError: true,
|
|
256
|
+
content: [{
|
|
257
|
+
type: "text",
|
|
258
|
+
text: message
|
|
259
|
+
}],
|
|
260
|
+
details: { error: message }
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function resolveActionTarget(params, currentChannelId) {
|
|
264
|
+
return typeof params.to === "string" ? params.to.trim() : typeof params.target === "string" ? params.target.trim() : currentChannelId?.trim() ?? "";
|
|
265
|
+
}
|
|
266
|
+
function resolveGraphActionTarget(params, currentChannelId, currentGraphChannelId) {
|
|
267
|
+
return resolveActionTarget(params, currentGraphChannelId ?? currentChannelId);
|
|
268
|
+
}
|
|
269
|
+
function resolveActionMessageId(params) {
|
|
270
|
+
return normalizeOptionalString(params.messageId) ?? "";
|
|
271
|
+
}
|
|
272
|
+
function resolveActionPinnedMessageId(params) {
|
|
273
|
+
return typeof params.pinnedMessageId === "string" ? params.pinnedMessageId.trim() : typeof params.messageId === "string" ? params.messageId.trim() : "";
|
|
274
|
+
}
|
|
275
|
+
function resolveActionQuery(params) {
|
|
276
|
+
return normalizeOptionalString(params.query) ?? "";
|
|
277
|
+
}
|
|
278
|
+
function resolveActionContent(params) {
|
|
279
|
+
return typeof params.text === "string" ? params.text : typeof params.content === "string" ? params.content : typeof params.message === "string" ? params.message : "";
|
|
280
|
+
}
|
|
281
|
+
function readOptionalTrimmedString(params, key) {
|
|
282
|
+
return typeof params[key] === "string" ? params[key].trim() || void 0 : void 0;
|
|
283
|
+
}
|
|
284
|
+
function resolveActionUploadFilePath(params) {
|
|
285
|
+
for (const key of [
|
|
286
|
+
"filePath",
|
|
287
|
+
"path",
|
|
288
|
+
"media"
|
|
289
|
+
]) if (typeof params[key] === "string") {
|
|
290
|
+
const value = params[key];
|
|
291
|
+
if (value.trim()) return value;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function resolveRequiredActionTarget(params) {
|
|
295
|
+
const to = params.graphOnly ? resolveGraphActionTarget(params.toolParams, params.currentChannelId, params.currentGraphChannelId) : resolveActionTarget(params.toolParams, params.currentChannelId);
|
|
296
|
+
if (!to) return actionError(`${params.actionLabel} requires a target (to).`);
|
|
297
|
+
return to;
|
|
298
|
+
}
|
|
299
|
+
function resolveRequiredActionMessageTarget(params) {
|
|
300
|
+
const to = params.graphOnly ? resolveGraphActionTarget(params.toolParams, params.currentChannelId, params.currentGraphChannelId) : resolveActionTarget(params.toolParams, params.currentChannelId);
|
|
301
|
+
const messageId = resolveActionMessageId(params.toolParams);
|
|
302
|
+
if (!to || !messageId) return actionError(`${params.actionLabel} requires a target (to) and messageId.`);
|
|
303
|
+
return {
|
|
304
|
+
to,
|
|
305
|
+
messageId
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function resolveRequiredActionPinnedMessageTarget(params) {
|
|
309
|
+
const to = params.graphOnly ? resolveGraphActionTarget(params.toolParams, params.currentChannelId, params.currentGraphChannelId) : resolveActionTarget(params.toolParams, params.currentChannelId);
|
|
310
|
+
const pinnedMessageId = resolveActionPinnedMessageId(params.toolParams);
|
|
311
|
+
if (!to || !pinnedMessageId) return actionError(`${params.actionLabel} requires a target (to) and pinnedMessageId.`);
|
|
312
|
+
return {
|
|
313
|
+
to,
|
|
314
|
+
pinnedMessageId
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
async function runWithRequiredActionTarget(params) {
|
|
318
|
+
const to = resolveRequiredActionTarget({
|
|
319
|
+
actionLabel: params.actionLabel,
|
|
320
|
+
toolParams: params.toolParams,
|
|
321
|
+
currentChannelId: params.currentChannelId,
|
|
322
|
+
currentGraphChannelId: params.currentGraphChannelId,
|
|
323
|
+
graphOnly: params.graphOnly
|
|
324
|
+
});
|
|
325
|
+
if (typeof to !== "string") return to;
|
|
326
|
+
return await params.run(to);
|
|
327
|
+
}
|
|
328
|
+
async function runWithRequiredActionMessageTarget(params) {
|
|
329
|
+
const target = resolveRequiredActionMessageTarget({
|
|
330
|
+
actionLabel: params.actionLabel,
|
|
331
|
+
toolParams: params.toolParams,
|
|
332
|
+
currentChannelId: params.currentChannelId,
|
|
333
|
+
currentGraphChannelId: params.currentGraphChannelId,
|
|
334
|
+
graphOnly: params.graphOnly
|
|
335
|
+
});
|
|
336
|
+
if ("isError" in target) return target;
|
|
337
|
+
return await params.run(target);
|
|
338
|
+
}
|
|
339
|
+
async function runWithRequiredActionPinnedMessageTarget(params) {
|
|
340
|
+
const target = resolveRequiredActionPinnedMessageTarget({
|
|
341
|
+
actionLabel: params.actionLabel,
|
|
342
|
+
toolParams: params.toolParams,
|
|
343
|
+
currentChannelId: params.currentChannelId,
|
|
344
|
+
currentGraphChannelId: params.currentGraphChannelId,
|
|
345
|
+
graphOnly: params.graphOnly
|
|
346
|
+
});
|
|
347
|
+
if ("isError" in target) return target;
|
|
348
|
+
return await params.run(target);
|
|
349
|
+
}
|
|
350
|
+
function describeMSTeamsMessageTool({ cfg }) {
|
|
351
|
+
const enabled = cfg.channels?.msteams?.enabled !== false && Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams));
|
|
352
|
+
return {
|
|
353
|
+
actions: enabled ? [
|
|
354
|
+
"upload-file",
|
|
355
|
+
"poll",
|
|
356
|
+
"edit",
|
|
357
|
+
"delete",
|
|
358
|
+
"pin",
|
|
359
|
+
"unpin",
|
|
360
|
+
"list-pins",
|
|
361
|
+
"read",
|
|
362
|
+
"react",
|
|
363
|
+
"reactions",
|
|
364
|
+
"search",
|
|
365
|
+
"member-info",
|
|
366
|
+
"channel-list",
|
|
367
|
+
"channel-info",
|
|
368
|
+
"addParticipant",
|
|
369
|
+
"removeParticipant",
|
|
370
|
+
"renameGroup"
|
|
371
|
+
] : [],
|
|
372
|
+
capabilities: enabled ? ["presentation"] : [],
|
|
373
|
+
schema: enabled ? {
|
|
374
|
+
actions: ["unpin"],
|
|
375
|
+
properties: { pinnedMessageId: Type.Optional(Type.String({ description: "Pinned message resource ID for unpin (from pin or list-pins, not the chat message ID)." })) }
|
|
376
|
+
} : null
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const msteamsPlugin = createChatChannelPlugin({
|
|
380
|
+
base: {
|
|
381
|
+
id: "msteams",
|
|
382
|
+
meta: {
|
|
383
|
+
...meta,
|
|
384
|
+
aliases: [...meta.aliases]
|
|
385
|
+
},
|
|
386
|
+
setupWizard: msteamsSetupWizard,
|
|
387
|
+
capabilities: {
|
|
388
|
+
chatTypes: [
|
|
389
|
+
"direct",
|
|
390
|
+
"channel",
|
|
391
|
+
"thread"
|
|
392
|
+
],
|
|
393
|
+
polls: true,
|
|
394
|
+
threads: true,
|
|
395
|
+
media: true
|
|
396
|
+
},
|
|
397
|
+
streaming: { blockStreamingCoalesceDefaults: {
|
|
398
|
+
minChars: 1500,
|
|
399
|
+
idleMs: 1e3
|
|
400
|
+
} },
|
|
401
|
+
agentPrompt: { messageToolHints: () => ["- Adaptive Cards supported. Use `action=send` with `card={type,version,body}` to send rich cards.", "- MSTeams targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:ID` or `user:Display Name` (requires Graph API) for DMs, `conversation:19:...@thread.tacv2` for groups/channels. Prefer IDs over display names for speed."] },
|
|
402
|
+
groups: { resolveToolPolicy: resolveMSTeamsGroupToolPolicy },
|
|
403
|
+
reload: { configPrefixes: ["channels.msteams"] },
|
|
404
|
+
configSchema: MSTeamsChannelConfigSchema,
|
|
405
|
+
config: {
|
|
406
|
+
...msteamsConfigAdapter,
|
|
407
|
+
isConfigured: (_account, cfg) => Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams)),
|
|
408
|
+
describeAccount: (account) => describeAccountSnapshot({
|
|
409
|
+
account,
|
|
410
|
+
configured: account.configured
|
|
411
|
+
})
|
|
412
|
+
},
|
|
413
|
+
approvalCapability: msTeamsApprovalAuth,
|
|
414
|
+
doctor: {
|
|
415
|
+
dmAllowFromMode: "topOnly",
|
|
416
|
+
groupModel: "hybrid",
|
|
417
|
+
groupAllowFromFallbackToAllowFrom: false,
|
|
418
|
+
warnOnEmptyGroupSenderAllowlist: true,
|
|
419
|
+
collectMutableAllowlistWarnings: collectMSTeamsMutableAllowlistWarnings
|
|
420
|
+
},
|
|
421
|
+
setup: msteamsSetupAdapter,
|
|
422
|
+
messaging: {
|
|
423
|
+
targetPrefixes: ["msteams", "teams"],
|
|
424
|
+
normalizeTarget: normalizeMSTeamsMessagingTarget,
|
|
425
|
+
resolveOutboundSessionRoute: (params) => resolveMSTeamsOutboundSessionRoute(params),
|
|
426
|
+
targetResolver: {
|
|
427
|
+
looksLikeId: (raw) => looksLikeMSTeamsTargetId(raw),
|
|
428
|
+
hint: "<conversationId|user:ID|conversation:ID>"
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
directory: createChannelDirectoryAdapter({
|
|
432
|
+
self: async ({ cfg }) => {
|
|
433
|
+
const creds = resolveMSTeamsCredentials(cfg.channels?.msteams);
|
|
434
|
+
if (!creds) return null;
|
|
435
|
+
return {
|
|
436
|
+
kind: "user",
|
|
437
|
+
id: creds.appId,
|
|
438
|
+
name: creds.appId
|
|
439
|
+
};
|
|
440
|
+
},
|
|
441
|
+
listPeers: async ({ cfg, query, limit }) => listDirectoryEntriesFromSources({
|
|
442
|
+
kind: "user",
|
|
443
|
+
sources: [cfg.channels?.msteams?.allowFrom ?? [], Object.keys(cfg.channels?.msteams?.dms ?? {})],
|
|
444
|
+
query,
|
|
445
|
+
limit,
|
|
446
|
+
normalizeId: (raw) => {
|
|
447
|
+
const normalized = normalizeMSTeamsMessagingTarget(raw) ?? raw;
|
|
448
|
+
const lowered = normalized.toLowerCase();
|
|
449
|
+
if (lowered.startsWith("user:") || lowered.startsWith("conversation:")) return normalized;
|
|
450
|
+
return `user:${normalized}`;
|
|
451
|
+
}
|
|
452
|
+
}),
|
|
453
|
+
listGroups: async ({ cfg, query, limit }) => listDirectoryEntriesFromSources({
|
|
454
|
+
kind: "group",
|
|
455
|
+
sources: [Object.values(cfg.channels?.msteams?.teams ?? {}).flatMap((team) => Object.keys(team.channels ?? {}))],
|
|
456
|
+
query,
|
|
457
|
+
limit,
|
|
458
|
+
normalizeId: (raw) => `conversation:${raw.replace(/^conversation:/i, "").trim()}`
|
|
459
|
+
}),
|
|
460
|
+
...createRuntimeDirectoryLiveAdapter({
|
|
461
|
+
getRuntime: loadMSTeamsChannelRuntime,
|
|
462
|
+
listPeersLive: (runtime) => runtime.listMSTeamsDirectoryPeersLive,
|
|
463
|
+
listGroupsLive: (runtime) => runtime.listMSTeamsDirectoryGroupsLive
|
|
464
|
+
})
|
|
465
|
+
}),
|
|
466
|
+
resolver: { resolveTargets: async ({ cfg, inputs, kind, runtime }) => {
|
|
467
|
+
const results = inputs.map((input) => ({
|
|
468
|
+
input,
|
|
469
|
+
resolved: false,
|
|
470
|
+
id: void 0,
|
|
471
|
+
name: void 0,
|
|
472
|
+
note: void 0
|
|
473
|
+
}));
|
|
474
|
+
const stripPrefix = (value) => normalizeMSTeamsUserInput(value);
|
|
475
|
+
const markPendingLookupFailed = (pending) => {
|
|
476
|
+
pending.forEach(({ index }) => {
|
|
477
|
+
const entry = results[index];
|
|
478
|
+
if (entry) entry.note = "lookup failed";
|
|
479
|
+
});
|
|
480
|
+
};
|
|
481
|
+
const resolvePending = async (pending, resolveEntries, applyResolvedEntry) => {
|
|
482
|
+
if (pending.length === 0) return;
|
|
483
|
+
try {
|
|
484
|
+
(await resolveEntries(pending.map((entry) => entry.query))).forEach((entry, idx) => {
|
|
485
|
+
const target = results[pending[idx]?.index ?? -1];
|
|
486
|
+
if (!target) return;
|
|
487
|
+
applyResolvedEntry(target, entry);
|
|
488
|
+
});
|
|
489
|
+
} catch (err) {
|
|
490
|
+
runtime.error?.(`msteams resolve failed: ${String(err)}`);
|
|
491
|
+
markPendingLookupFailed(pending);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
if (kind === "user") {
|
|
495
|
+
const pending = [];
|
|
496
|
+
results.forEach((entry, index) => {
|
|
497
|
+
const trimmed = entry.input.trim();
|
|
498
|
+
if (!trimmed) {
|
|
499
|
+
entry.note = "empty input";
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const cleaned = stripPrefix(trimmed);
|
|
503
|
+
if (/^[0-9a-fA-F-]{16,}$/.test(cleaned) || cleaned.includes("@")) {
|
|
504
|
+
entry.resolved = true;
|
|
505
|
+
entry.id = cleaned;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
pending.push({
|
|
509
|
+
input: entry.input,
|
|
510
|
+
query: cleaned,
|
|
511
|
+
index
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
await resolvePending(pending, (entries) => resolveMSTeamsUserAllowlist({
|
|
515
|
+
cfg,
|
|
516
|
+
entries
|
|
517
|
+
}), (target, entry) => {
|
|
518
|
+
target.resolved = entry.resolved;
|
|
519
|
+
target.id = entry.id;
|
|
520
|
+
target.name = entry.name;
|
|
521
|
+
target.note = entry.note;
|
|
522
|
+
});
|
|
523
|
+
return results;
|
|
524
|
+
}
|
|
525
|
+
const pending = [];
|
|
526
|
+
results.forEach((entry, index) => {
|
|
527
|
+
const trimmed = entry.input.trim();
|
|
528
|
+
if (!trimmed) {
|
|
529
|
+
entry.note = "empty input";
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const conversationId = parseMSTeamsConversationId(trimmed);
|
|
533
|
+
if (conversationId !== null) {
|
|
534
|
+
entry.resolved = Boolean(conversationId);
|
|
535
|
+
entry.id = conversationId || void 0;
|
|
536
|
+
entry.note = conversationId ? "conversation id" : "empty conversation id";
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const parsed = parseMSTeamsTeamChannelInput(trimmed);
|
|
540
|
+
if (!parsed.team) {
|
|
541
|
+
entry.note = "missing team";
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const query = parsed.channel ? `${parsed.team}/${parsed.channel}` : parsed.team;
|
|
545
|
+
pending.push({
|
|
546
|
+
input: entry.input,
|
|
547
|
+
query,
|
|
548
|
+
index
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
await resolvePending(pending, (entries) => resolveMSTeamsChannelAllowlist({
|
|
552
|
+
cfg,
|
|
553
|
+
entries
|
|
554
|
+
}), (target, entry) => {
|
|
555
|
+
if (!entry.resolved || !entry.teamId) {
|
|
556
|
+
target.resolved = false;
|
|
557
|
+
target.note = entry.note;
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
target.resolved = true;
|
|
561
|
+
if (entry.channelId) {
|
|
562
|
+
target.id = `${entry.teamId}/${entry.channelId}`;
|
|
563
|
+
target.name = entry.channelName && entry.teamName ? `${entry.teamName}/${entry.channelName}` : entry.channelName ?? entry.teamName;
|
|
564
|
+
} else {
|
|
565
|
+
target.id = entry.teamId;
|
|
566
|
+
target.name = entry.teamName;
|
|
567
|
+
target.note = "team id";
|
|
568
|
+
}
|
|
569
|
+
if (entry.note) target.note = entry.note;
|
|
570
|
+
});
|
|
571
|
+
return results;
|
|
572
|
+
} },
|
|
573
|
+
actions: {
|
|
574
|
+
describeMessageTool: describeMSTeamsMessageTool,
|
|
575
|
+
handleAction: async (ctx) => {
|
|
576
|
+
const presentation = ctx.action === "send" ? normalizeMessagePresentation(ctx.params.presentation) : void 0;
|
|
577
|
+
if (ctx.action === "send" && presentation) {
|
|
578
|
+
const card = buildMSTeamsPresentationCard({
|
|
579
|
+
presentation,
|
|
580
|
+
text: resolveActionContent(ctx.params)
|
|
581
|
+
});
|
|
582
|
+
return await runWithRequiredActionTarget({
|
|
583
|
+
actionLabel: "Card send",
|
|
584
|
+
toolParams: ctx.params,
|
|
585
|
+
run: async (to) => {
|
|
586
|
+
const { sendAdaptiveCardMSTeams } = await loadMSTeamsChannelRuntime();
|
|
587
|
+
const result = await sendAdaptiveCardMSTeams({
|
|
588
|
+
cfg: ctx.cfg,
|
|
589
|
+
to,
|
|
590
|
+
card
|
|
591
|
+
});
|
|
592
|
+
return jsonActionResultWithDetails({
|
|
593
|
+
ok: true,
|
|
594
|
+
channel: "msteams",
|
|
595
|
+
messageId: result.messageId,
|
|
596
|
+
conversationId: result.conversationId
|
|
597
|
+
}, {
|
|
598
|
+
ok: true,
|
|
599
|
+
channel: "msteams",
|
|
600
|
+
messageId: result.messageId
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
if (ctx.action === "upload-file") {
|
|
606
|
+
const mediaUrl = resolveActionUploadFilePath(ctx.params);
|
|
607
|
+
if (!mediaUrl) return actionError("Upload-file requires media, filePath, or path.");
|
|
608
|
+
return await runWithRequiredActionTarget({
|
|
609
|
+
actionLabel: "Upload-file",
|
|
610
|
+
toolParams: ctx.params,
|
|
611
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
612
|
+
run: async (to) => {
|
|
613
|
+
const { sendMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
614
|
+
const result = await sendMessageMSTeams({
|
|
615
|
+
cfg: ctx.cfg,
|
|
616
|
+
to,
|
|
617
|
+
text: resolveActionContent(ctx.params),
|
|
618
|
+
mediaUrl,
|
|
619
|
+
filename: readOptionalTrimmedString(ctx.params, "filename") ?? readOptionalTrimmedString(ctx.params, "title"),
|
|
620
|
+
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
621
|
+
mediaReadFile: ctx.mediaReadFile
|
|
622
|
+
});
|
|
623
|
+
return jsonActionResultWithDetails({
|
|
624
|
+
ok: true,
|
|
625
|
+
channel: "msteams",
|
|
626
|
+
action: "upload-file",
|
|
627
|
+
messageId: result.messageId,
|
|
628
|
+
conversationId: result.conversationId,
|
|
629
|
+
...result.pendingUploadId ? { pendingUploadId: result.pendingUploadId } : {}
|
|
630
|
+
}, {
|
|
631
|
+
ok: true,
|
|
632
|
+
channel: "msteams",
|
|
633
|
+
messageId: result.messageId,
|
|
634
|
+
...result.pendingUploadId ? { pendingUploadId: result.pendingUploadId } : {}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
if (ctx.action === "edit") {
|
|
640
|
+
const content = resolveActionContent(ctx.params);
|
|
641
|
+
if (!content) return actionError("Edit requires content.");
|
|
642
|
+
return await runWithRequiredActionMessageTarget({
|
|
643
|
+
actionLabel: "Edit",
|
|
644
|
+
toolParams: ctx.params,
|
|
645
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
646
|
+
run: async (target) => {
|
|
647
|
+
const { editMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
648
|
+
return jsonMSTeamsConversationResult((await editMessageMSTeams({
|
|
649
|
+
cfg: ctx.cfg,
|
|
650
|
+
to: target.to,
|
|
651
|
+
activityId: target.messageId,
|
|
652
|
+
text: content
|
|
653
|
+
})).conversationId);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
if (ctx.action === "delete") return await runWithRequiredActionMessageTarget({
|
|
658
|
+
actionLabel: "Delete",
|
|
659
|
+
toolParams: ctx.params,
|
|
660
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
661
|
+
run: async (target) => {
|
|
662
|
+
const { deleteMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
663
|
+
return jsonMSTeamsConversationResult((await deleteMessageMSTeams({
|
|
664
|
+
cfg: ctx.cfg,
|
|
665
|
+
to: target.to,
|
|
666
|
+
activityId: target.messageId
|
|
667
|
+
})).conversationId);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
if (ctx.action === "read") return await runWithRequiredActionMessageTarget({
|
|
671
|
+
actionLabel: "Read",
|
|
672
|
+
toolParams: ctx.params,
|
|
673
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
674
|
+
currentGraphChannelId: ctx.toolContext?.currentGraphChannelId,
|
|
675
|
+
graphOnly: true,
|
|
676
|
+
run: async (target) => {
|
|
677
|
+
const { getMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
678
|
+
return jsonMSTeamsOkActionResult("read", { message: await getMessageMSTeams({
|
|
679
|
+
cfg: ctx.cfg,
|
|
680
|
+
to: target.to,
|
|
681
|
+
messageId: target.messageId
|
|
682
|
+
}) });
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
if (ctx.action === "pin") return await runWithRequiredActionMessageTarget({
|
|
686
|
+
actionLabel: "Pin",
|
|
687
|
+
toolParams: ctx.params,
|
|
688
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
689
|
+
currentGraphChannelId: ctx.toolContext?.currentGraphChannelId,
|
|
690
|
+
graphOnly: true,
|
|
691
|
+
run: async (target) => {
|
|
692
|
+
const { pinMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
693
|
+
return jsonMSTeamsActionResult("pin", await pinMessageMSTeams({
|
|
694
|
+
cfg: ctx.cfg,
|
|
695
|
+
to: target.to,
|
|
696
|
+
messageId: target.messageId
|
|
697
|
+
}));
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
if (ctx.action === "unpin") return await runWithRequiredActionPinnedMessageTarget({
|
|
701
|
+
actionLabel: "Unpin",
|
|
702
|
+
toolParams: ctx.params,
|
|
703
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
704
|
+
currentGraphChannelId: ctx.toolContext?.currentGraphChannelId,
|
|
705
|
+
graphOnly: true,
|
|
706
|
+
run: async (target) => {
|
|
707
|
+
const { unpinMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
708
|
+
return jsonMSTeamsActionResult("unpin", await unpinMessageMSTeams({
|
|
709
|
+
cfg: ctx.cfg,
|
|
710
|
+
to: target.to,
|
|
711
|
+
pinnedMessageId: target.pinnedMessageId
|
|
712
|
+
}));
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
if (ctx.action === "list-pins") return await runWithRequiredActionTarget({
|
|
716
|
+
actionLabel: "List-pins",
|
|
717
|
+
toolParams: ctx.params,
|
|
718
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
719
|
+
currentGraphChannelId: ctx.toolContext?.currentGraphChannelId,
|
|
720
|
+
graphOnly: true,
|
|
721
|
+
run: async (to) => {
|
|
722
|
+
const { listPinsMSTeams } = await loadMSTeamsChannelRuntime();
|
|
723
|
+
return jsonMSTeamsOkActionResult("list-pins", await listPinsMSTeams({
|
|
724
|
+
cfg: ctx.cfg,
|
|
725
|
+
to
|
|
726
|
+
}));
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
if (ctx.action === "react") return await runWithRequiredActionMessageTarget({
|
|
730
|
+
actionLabel: "React",
|
|
731
|
+
toolParams: ctx.params,
|
|
732
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
733
|
+
currentGraphChannelId: ctx.toolContext?.currentGraphChannelId,
|
|
734
|
+
graphOnly: true,
|
|
735
|
+
run: async (target) => {
|
|
736
|
+
const emoji = typeof ctx.params.emoji === "string" ? ctx.params.emoji.trim() : "";
|
|
737
|
+
const remove = typeof ctx.params.remove === "boolean" ? ctx.params.remove : false;
|
|
738
|
+
if (!emoji) return {
|
|
739
|
+
isError: true,
|
|
740
|
+
content: [{
|
|
741
|
+
type: "text",
|
|
742
|
+
text: `React requires an emoji (reaction type). Valid types: ${MSTEAMS_REACTION_TYPES.join(", ")}.`
|
|
743
|
+
}],
|
|
744
|
+
details: {
|
|
745
|
+
error: "React requires an emoji (reaction type).",
|
|
746
|
+
validTypes: [...MSTEAMS_REACTION_TYPES]
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
if (remove) {
|
|
750
|
+
const { unreactMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
751
|
+
return jsonMSTeamsActionResult("react", {
|
|
752
|
+
removed: true,
|
|
753
|
+
reactionType: emoji,
|
|
754
|
+
...await unreactMessageMSTeams({
|
|
755
|
+
cfg: ctx.cfg,
|
|
756
|
+
to: target.to,
|
|
757
|
+
messageId: target.messageId,
|
|
758
|
+
reactionType: emoji
|
|
759
|
+
})
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
const { reactMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
763
|
+
return jsonMSTeamsActionResult("react", {
|
|
764
|
+
reactionType: emoji,
|
|
765
|
+
...await reactMessageMSTeams({
|
|
766
|
+
cfg: ctx.cfg,
|
|
767
|
+
to: target.to,
|
|
768
|
+
messageId: target.messageId,
|
|
769
|
+
reactionType: emoji
|
|
770
|
+
})
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
if (ctx.action === "reactions") return await runWithRequiredActionMessageTarget({
|
|
775
|
+
actionLabel: "Reactions",
|
|
776
|
+
toolParams: ctx.params,
|
|
777
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
778
|
+
currentGraphChannelId: ctx.toolContext?.currentGraphChannelId,
|
|
779
|
+
graphOnly: true,
|
|
780
|
+
run: async (target) => {
|
|
781
|
+
const { listReactionsMSTeams } = await loadMSTeamsChannelRuntime();
|
|
782
|
+
return jsonMSTeamsOkActionResult("reactions", await listReactionsMSTeams({
|
|
783
|
+
cfg: ctx.cfg,
|
|
784
|
+
to: target.to,
|
|
785
|
+
messageId: target.messageId
|
|
786
|
+
}));
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
if (ctx.action === "search") return await runWithRequiredActionTarget({
|
|
790
|
+
actionLabel: "Search",
|
|
791
|
+
toolParams: ctx.params,
|
|
792
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
793
|
+
currentGraphChannelId: ctx.toolContext?.currentGraphChannelId,
|
|
794
|
+
graphOnly: true,
|
|
795
|
+
run: async (to) => {
|
|
796
|
+
const query = resolveActionQuery(ctx.params);
|
|
797
|
+
if (!query) return actionError("Search requires a target (to) and query.");
|
|
798
|
+
const limit = typeof ctx.params.limit === "number" ? ctx.params.limit : void 0;
|
|
799
|
+
const from = typeof ctx.params.from === "string" ? ctx.params.from.trim() : void 0;
|
|
800
|
+
const { searchMessagesMSTeams } = await loadMSTeamsChannelRuntime();
|
|
801
|
+
return jsonMSTeamsOkActionResult("search", await searchMessagesMSTeams({
|
|
802
|
+
cfg: ctx.cfg,
|
|
803
|
+
to,
|
|
804
|
+
query,
|
|
805
|
+
from: from || void 0,
|
|
806
|
+
limit
|
|
807
|
+
}));
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
if (ctx.action === "member-info") {
|
|
811
|
+
const userId = normalizeOptionalString(ctx.params.userId) ?? "";
|
|
812
|
+
if (!userId) return actionError("member-info requires a userId.");
|
|
813
|
+
const { getMemberInfoMSTeams } = await loadMSTeamsChannelRuntime();
|
|
814
|
+
return jsonMSTeamsOkActionResult("member-info", await getMemberInfoMSTeams({
|
|
815
|
+
cfg: ctx.cfg,
|
|
816
|
+
userId
|
|
817
|
+
}));
|
|
818
|
+
}
|
|
819
|
+
if (ctx.action === "channel-list") {
|
|
820
|
+
const teamId = normalizeOptionalString(ctx.params.teamId) ?? "";
|
|
821
|
+
if (!teamId) return actionError("channel-list requires a teamId.");
|
|
822
|
+
const { listChannelsMSTeams } = await loadMSTeamsChannelRuntime();
|
|
823
|
+
return jsonMSTeamsOkActionResult("channel-list", await listChannelsMSTeams({
|
|
824
|
+
cfg: ctx.cfg,
|
|
825
|
+
teamId
|
|
826
|
+
}));
|
|
827
|
+
}
|
|
828
|
+
if (ctx.action === "channel-info") {
|
|
829
|
+
const teamId = normalizeOptionalString(ctx.params.teamId) ?? "";
|
|
830
|
+
const channelId = normalizeOptionalString(ctx.params.channelId) ?? "";
|
|
831
|
+
if (!teamId || !channelId) return actionError("channel-info requires teamId and channelId.");
|
|
832
|
+
const { getChannelInfoMSTeams } = await loadMSTeamsChannelRuntime();
|
|
833
|
+
return jsonMSTeamsOkActionResult("channel-info", { channelInfo: (await getChannelInfoMSTeams({
|
|
834
|
+
cfg: ctx.cfg,
|
|
835
|
+
teamId,
|
|
836
|
+
channelId
|
|
837
|
+
})).channel });
|
|
838
|
+
}
|
|
839
|
+
if (ctx.action === "addParticipant") {
|
|
840
|
+
const userId = typeof ctx.params.userId === "string" ? ctx.params.userId.trim() : "";
|
|
841
|
+
if (!userId) return actionError("addParticipant requires a userId.");
|
|
842
|
+
return await runWithRequiredActionTarget({
|
|
843
|
+
actionLabel: "addParticipant",
|
|
844
|
+
toolParams: ctx.params,
|
|
845
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
846
|
+
run: async (to) => {
|
|
847
|
+
const role = readOptionalTrimmedString(ctx.params, "role");
|
|
848
|
+
const { addParticipantMSTeams } = await loadMSTeamsChannelRuntime();
|
|
849
|
+
return jsonMSTeamsOkActionResult("addParticipant", await addParticipantMSTeams({
|
|
850
|
+
cfg: ctx.cfg,
|
|
851
|
+
to,
|
|
852
|
+
userId,
|
|
853
|
+
role
|
|
854
|
+
}));
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
if (ctx.action === "removeParticipant") {
|
|
859
|
+
const userId = typeof ctx.params.userId === "string" ? ctx.params.userId.trim() : "";
|
|
860
|
+
if (!userId) return actionError("removeParticipant requires a userId.");
|
|
861
|
+
return await runWithRequiredActionTarget({
|
|
862
|
+
actionLabel: "removeParticipant",
|
|
863
|
+
toolParams: ctx.params,
|
|
864
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
865
|
+
run: async (to) => {
|
|
866
|
+
const { removeParticipantMSTeams } = await loadMSTeamsChannelRuntime();
|
|
867
|
+
return jsonMSTeamsOkActionResult("removeParticipant", await removeParticipantMSTeams({
|
|
868
|
+
cfg: ctx.cfg,
|
|
869
|
+
to,
|
|
870
|
+
userId
|
|
871
|
+
}));
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
if (ctx.action === "renameGroup") {
|
|
876
|
+
const name = typeof ctx.params.name === "string" ? ctx.params.name.trim() : "";
|
|
877
|
+
if (!name) return actionError("renameGroup requires a name.");
|
|
878
|
+
return await runWithRequiredActionTarget({
|
|
879
|
+
actionLabel: "renameGroup",
|
|
880
|
+
toolParams: ctx.params,
|
|
881
|
+
currentChannelId: ctx.toolContext?.currentChannelId,
|
|
882
|
+
run: async (to) => {
|
|
883
|
+
const { renameGroupMSTeams } = await loadMSTeamsChannelRuntime();
|
|
884
|
+
return jsonMSTeamsOkActionResult("renameGroup", await renameGroupMSTeams({
|
|
885
|
+
cfg: ctx.cfg,
|
|
886
|
+
to,
|
|
887
|
+
name
|
|
888
|
+
}));
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
status: createComputedAccountStatusAdapter({
|
|
896
|
+
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, { port: null }),
|
|
897
|
+
buildChannelSummary: ({ snapshot }) => buildProbeChannelStatusSummary(snapshot, { port: snapshot.port ?? null }),
|
|
898
|
+
probeAccount: async ({ cfg }) => await (await loadMSTeamsChannelRuntime()).probeMSTeams(cfg.channels?.msteams),
|
|
899
|
+
formatCapabilitiesProbe: ({ probe }) => {
|
|
900
|
+
const teamsProbe = probe;
|
|
901
|
+
const lines = [];
|
|
902
|
+
const appId = typeof teamsProbe?.appId === "string" ? teamsProbe.appId.trim() : "";
|
|
903
|
+
if (appId) lines.push({ text: `App: ${appId}` });
|
|
904
|
+
const graph = teamsProbe?.graph;
|
|
905
|
+
if (graph) {
|
|
906
|
+
const roles = Array.isArray(graph.roles) ? graph.roles.map((role) => role.trim()).filter(Boolean) : [];
|
|
907
|
+
const scopes = Array.isArray(graph.scopes) ? graph.scopes.map((scope) => scope.trim()).filter(Boolean) : [];
|
|
908
|
+
const formatPermission = (permission) => {
|
|
909
|
+
const hint = TEAMS_GRAPH_PERMISSION_HINTS[permission];
|
|
910
|
+
return hint ? `${permission} (${hint})` : permission;
|
|
911
|
+
};
|
|
912
|
+
if (!graph.ok) lines.push({
|
|
913
|
+
text: `Graph: ${graph.error ?? "failed"}`,
|
|
914
|
+
tone: "error"
|
|
915
|
+
});
|
|
916
|
+
else if (roles.length > 0 || scopes.length > 0) {
|
|
917
|
+
if (roles.length > 0) lines.push({ text: `Graph roles: ${roles.map(formatPermission).join(", ")}` });
|
|
918
|
+
if (scopes.length > 0) lines.push({ text: `Graph scopes: ${scopes.map(formatPermission).join(", ")}` });
|
|
919
|
+
} else if (graph.ok) lines.push({ text: "Graph: ok" });
|
|
920
|
+
}
|
|
921
|
+
return lines;
|
|
922
|
+
},
|
|
923
|
+
resolveAccountSnapshot: ({ account, runtime }) => ({
|
|
924
|
+
accountId: account.accountId,
|
|
925
|
+
enabled: account.enabled,
|
|
926
|
+
configured: account.configured,
|
|
927
|
+
extra: { port: runtime?.port ?? null }
|
|
928
|
+
})
|
|
929
|
+
}),
|
|
930
|
+
gateway: { startAccount: async (ctx) => {
|
|
931
|
+
const { monitorMSTeamsProvider } = await import("./src-CFp1QpFd.js");
|
|
932
|
+
const port = ctx.cfg.channels?.msteams?.webhook?.port ?? 3978;
|
|
933
|
+
ctx.setStatus({
|
|
934
|
+
accountId: ctx.accountId,
|
|
935
|
+
port
|
|
936
|
+
});
|
|
937
|
+
ctx.log?.info(`starting provider (port ${port})`);
|
|
938
|
+
return monitorMSTeamsProvider({
|
|
939
|
+
cfg: ctx.cfg,
|
|
940
|
+
runtime: ctx.runtime,
|
|
941
|
+
abortSignal: ctx.abortSignal
|
|
942
|
+
});
|
|
943
|
+
} }
|
|
944
|
+
},
|
|
945
|
+
security: { collectWarnings: projectConfigWarningCollector(collectMSTeamsSecurityWarnings) },
|
|
946
|
+
pairing: { text: {
|
|
947
|
+
idLabel: "msteamsUserId",
|
|
948
|
+
message: PAIRING_APPROVED_MESSAGE,
|
|
949
|
+
normalizeAllowEntry: createPairingPrefixStripper(/^(msteams|user):/i),
|
|
950
|
+
notify: async ({ cfg, id, message }) => {
|
|
951
|
+
const { sendMessageMSTeams } = await loadMSTeamsChannelRuntime();
|
|
952
|
+
await sendMessageMSTeams({
|
|
953
|
+
cfg,
|
|
954
|
+
to: id,
|
|
955
|
+
text: message
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
} },
|
|
959
|
+
threading: { buildToolContext: ({ context, hasRepliedRef }) => {
|
|
960
|
+
const nativeChannelId = context.NativeChannelId?.trim();
|
|
961
|
+
const hasChannelRoute = Boolean(nativeChannelId && nativeChannelId.includes("/"));
|
|
962
|
+
return {
|
|
963
|
+
currentChannelId: normalizeOptionalString(context.To),
|
|
964
|
+
currentGraphChannelId: hasChannelRoute ? nativeChannelId : void 0,
|
|
965
|
+
currentThreadTs: context.ReplyToId,
|
|
966
|
+
hasRepliedRef
|
|
967
|
+
};
|
|
968
|
+
} },
|
|
969
|
+
outbound: {
|
|
970
|
+
deliveryMode: "direct",
|
|
971
|
+
chunker: chunkTextForOutbound,
|
|
972
|
+
chunkerMode: "markdown",
|
|
973
|
+
textChunkLimit: 4e3,
|
|
974
|
+
pollMaxOptions: 12,
|
|
975
|
+
...createRuntimeOutboundDelegates({
|
|
976
|
+
getRuntime: loadMSTeamsChannelRuntime,
|
|
977
|
+
sendText: { resolve: (runtime) => runtime.msteamsOutbound.sendText },
|
|
978
|
+
sendMedia: { resolve: (runtime) => runtime.msteamsOutbound.sendMedia },
|
|
979
|
+
sendPoll: { resolve: (runtime) => runtime.msteamsOutbound.sendPoll }
|
|
980
|
+
})
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
//#endregion
|
|
984
|
+
export { msteamsPlugin as t };
|