@cuylabs/agent-channel-teams 0.10.0 → 0.12.0
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/README.md +223 -6
- package/dist/chunk-B5KRH22J.js +12 -0
- package/dist/extensions.d.ts +1 -0
- package/dist/extensions.js +10 -0
- package/dist/index.d.ts +150 -26
- package/dist/index.js +336 -27
- package/docs/README.md +33 -0
- package/docs/architecture.md +71 -0
- package/docs/handler-model.md +165 -0
- package/docs/teams-helpers.md +91 -0
- package/package.json +14 -9
package/dist/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TeamsAttachmentDownloader,
|
|
3
|
+
TeamsInfo,
|
|
4
|
+
parseTeamsChannelData
|
|
5
|
+
} from "./chunk-B5KRH22J.js";
|
|
6
|
+
|
|
1
7
|
// src/adapter.ts
|
|
2
8
|
import {
|
|
3
9
|
createM365ChannelAdapter,
|
|
@@ -38,6 +44,86 @@ function detectSurface(conversationType, meetingId, teamId) {
|
|
|
38
44
|
if (conversationType === "groupChat") return "group";
|
|
39
45
|
return "unknown";
|
|
40
46
|
}
|
|
47
|
+
function includesName(names, candidate) {
|
|
48
|
+
return !!candidate && names.includes(candidate);
|
|
49
|
+
}
|
|
50
|
+
function parseTypedChannelData(channelData) {
|
|
51
|
+
if (!channelData) return void 0;
|
|
52
|
+
return parseTeamsChannelData(channelData);
|
|
53
|
+
}
|
|
54
|
+
var TEAMS_CONFIG_FETCH_NAMES = ["config/fetch"];
|
|
55
|
+
var TEAMS_CONFIG_SUBMIT_NAMES = ["config/submit"];
|
|
56
|
+
var TEAMS_FILE_CONSENT_NAMES = ["fileConsent/invoke"];
|
|
57
|
+
var TEAMS_ACTIONABLE_MESSAGE_NAMES = [
|
|
58
|
+
"actionableMessage/executeAction"
|
|
59
|
+
];
|
|
60
|
+
var TEAMS_TASK_MODULE_FETCH_NAMES = [
|
|
61
|
+
"task/fetch",
|
|
62
|
+
"composeExtension/fetchTask"
|
|
63
|
+
];
|
|
64
|
+
var TEAMS_TASK_MODULE_SUBMIT_NAMES = ["task/submit"];
|
|
65
|
+
var TEAMS_TAB_FETCH_NAMES = ["tab/fetch"];
|
|
66
|
+
var TEAMS_TAB_SUBMIT_NAMES = ["tab/submit"];
|
|
67
|
+
var TEAMS_MESSAGE_EXTENSION_QUERY_NAMES = [
|
|
68
|
+
"composeExtension/query",
|
|
69
|
+
"composeExtension/queryLink",
|
|
70
|
+
"composeExtension/anonymousQueryLink"
|
|
71
|
+
];
|
|
72
|
+
var TEAMS_MESSAGE_EXTENSION_SUBMIT_ACTION_NAMES = [
|
|
73
|
+
"composeExtension/submitAction"
|
|
74
|
+
];
|
|
75
|
+
var TEAMS_MESSAGE_EXTENSION_FETCH_TASK_NAMES = [
|
|
76
|
+
"composeExtension/fetchTask"
|
|
77
|
+
];
|
|
78
|
+
var TEAMS_MESSAGE_EXTENSION_QUERY_SETTING_URL_NAMES = [
|
|
79
|
+
"composeExtension/querySettingUrl"
|
|
80
|
+
];
|
|
81
|
+
var TEAMS_MESSAGE_EXTENSION_SETTING_NAMES = [
|
|
82
|
+
"composeExtension/setting"
|
|
83
|
+
];
|
|
84
|
+
var TEAMS_MESSAGE_EXTENSION_CARD_BUTTON_CLICKED_NAMES = [
|
|
85
|
+
"composeExtension/onCardButtonClicked"
|
|
86
|
+
];
|
|
87
|
+
var TEAMS_MESSAGE_EXTENSION_SELECT_NAMES = [
|
|
88
|
+
"composeExtension/selectItem"
|
|
89
|
+
];
|
|
90
|
+
var TEAMS_CARD_ACTION_SUBMIT_NAMES = ["message/submitAction"];
|
|
91
|
+
var TEAMS_CONVERSATION_UPDATE_EVENT_NAMES = {
|
|
92
|
+
channelCreated: "channel-created",
|
|
93
|
+
channelDeleted: "channel-deleted",
|
|
94
|
+
channelRenamed: "channel-renamed",
|
|
95
|
+
channelRestored: "channel-restored",
|
|
96
|
+
channelShared: "channel-shared",
|
|
97
|
+
channelUnshared: "channel-unshared",
|
|
98
|
+
teamRenamed: "team-renamed",
|
|
99
|
+
teamArchived: "team-archived",
|
|
100
|
+
teamUnarchived: "team-unarchived",
|
|
101
|
+
teamDeleted: "team-deleted",
|
|
102
|
+
teamHardDeleted: "team-hard-deleted",
|
|
103
|
+
teamRestored: "team-restored"
|
|
104
|
+
};
|
|
105
|
+
var TEAMS_MEETING_EVENT_NAMES = {
|
|
106
|
+
"application/vnd.microsoft.readReceipt": "read-receipt",
|
|
107
|
+
"application/vnd.microsoft.meetingStart": "meeting-start",
|
|
108
|
+
"application/vnd.microsoft.meetingEnd": "meeting-end",
|
|
109
|
+
"application/vnd.microsoft.meetingParticipantJoin": "participants-join",
|
|
110
|
+
"application/vnd.microsoft.meetingParticipantLeave": "participants-leave",
|
|
111
|
+
"application/vnd.microsoft.meetingRoomJoin": "meeting-room-join",
|
|
112
|
+
"application/vnd.microsoft.meetingRoomLeave": "meeting-room-leave",
|
|
113
|
+
"application/vnd.microsoft.meetingReaction": "meeting-reaction",
|
|
114
|
+
"application/vnd.microsoft.meetingPollResponse": "meeting-poll-response",
|
|
115
|
+
"application/vnd.microsoft.meetingAppsInstalled": "meeting-apps-installed",
|
|
116
|
+
"application/vnd.microsoft.meetingAppsUninstalled": "meeting-apps-uninstalled",
|
|
117
|
+
"application/vnd.microsoft.meetingRecordingStarted": "meeting-recording-started",
|
|
118
|
+
"application/vnd.microsoft.meetingRecordingStopped": "meeting-recording-stopped",
|
|
119
|
+
"application/vnd.microsoft.meetingFocusChange": "meeting-focus-change",
|
|
120
|
+
"application/vnd.microsoft.meetingScreenShareStart": "meeting-screen-share-start",
|
|
121
|
+
"application/vnd.microsoft.meetingScreenShareStop": "meeting-screen-share-stop"
|
|
122
|
+
};
|
|
123
|
+
var TEAMS_MEETING_INVOKE_NAMES = {
|
|
124
|
+
"application/vnd.microsoft.meetingStageView": "meeting-stage-view",
|
|
125
|
+
"application/vnd.microsoft.meetingSmartReply": "meeting-smart-reply"
|
|
126
|
+
};
|
|
41
127
|
function detectKind(context, eventType) {
|
|
42
128
|
const { activity } = context;
|
|
43
129
|
const payload = asObject(activity.value);
|
|
@@ -48,6 +134,15 @@ function detectKind(context, eventType) {
|
|
|
48
134
|
case ActivityTypes2.MessageReaction:
|
|
49
135
|
return "reaction";
|
|
50
136
|
case ActivityTypes2.ConversationUpdate:
|
|
137
|
+
if (Array.isArray(activity.membersAdded) && activity.membersAdded.length > 0) {
|
|
138
|
+
return "members-added";
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(activity.membersRemoved) && activity.membersRemoved.length > 0) {
|
|
141
|
+
return "members-removed";
|
|
142
|
+
}
|
|
143
|
+
if (eventType && eventType in TEAMS_CONVERSATION_UPDATE_EVENT_NAMES) {
|
|
144
|
+
return TEAMS_CONVERSATION_UPDATE_EVENT_NAMES[eventType];
|
|
145
|
+
}
|
|
51
146
|
return "conversation-update";
|
|
52
147
|
case ActivityTypes2.MessageUpdate:
|
|
53
148
|
if (eventType === "editMessage") return "message-edit";
|
|
@@ -56,16 +151,71 @@ function detectKind(context, eventType) {
|
|
|
56
151
|
case ActivityTypes2.MessageDelete:
|
|
57
152
|
if (eventType === "softDeleteMessage") return "message-delete";
|
|
58
153
|
return "other";
|
|
154
|
+
case ActivityTypes2.Event:
|
|
155
|
+
if (invokeName && invokeName in TEAMS_MEETING_EVENT_NAMES) {
|
|
156
|
+
return TEAMS_MEETING_EVENT_NAMES[invokeName];
|
|
157
|
+
}
|
|
158
|
+
return "other";
|
|
59
159
|
case ActivityTypes2.Invoke:
|
|
60
|
-
if (invokeName
|
|
61
|
-
return
|
|
160
|
+
if (invokeName && invokeName in TEAMS_MEETING_INVOKE_NAMES) {
|
|
161
|
+
return TEAMS_MEETING_INVOKE_NAMES[invokeName];
|
|
162
|
+
}
|
|
163
|
+
if (includesName(TEAMS_CONFIG_FETCH_NAMES, invokeName)) {
|
|
164
|
+
return "config-fetch";
|
|
165
|
+
}
|
|
166
|
+
if (includesName(TEAMS_CONFIG_SUBMIT_NAMES, invokeName)) {
|
|
167
|
+
return "config-submit";
|
|
168
|
+
}
|
|
169
|
+
if (includesName(TEAMS_FILE_CONSENT_NAMES, invokeName)) {
|
|
170
|
+
return "file-consent";
|
|
171
|
+
}
|
|
172
|
+
if (includesName(TEAMS_ACTIONABLE_MESSAGE_NAMES, invokeName)) {
|
|
173
|
+
return "actionable-message-execute-action";
|
|
174
|
+
}
|
|
175
|
+
if (includesName(TEAMS_TASK_MODULE_FETCH_NAMES, invokeName)) {
|
|
176
|
+
return invokeName === "composeExtension/fetchTask" ? "message-extension-fetch-task" : "task-fetch";
|
|
177
|
+
}
|
|
178
|
+
if (includesName(TEAMS_TASK_MODULE_SUBMIT_NAMES, invokeName)) {
|
|
179
|
+
return "task-submit";
|
|
180
|
+
}
|
|
181
|
+
if (includesName(TEAMS_TAB_FETCH_NAMES, invokeName)) {
|
|
182
|
+
return "tab-fetch";
|
|
62
183
|
}
|
|
63
|
-
if (invokeName
|
|
64
|
-
|
|
65
|
-
return "search-command";
|
|
184
|
+
if (includesName(TEAMS_TAB_SUBMIT_NAMES, invokeName)) {
|
|
185
|
+
return "tab-submit";
|
|
66
186
|
}
|
|
67
|
-
if (invokeName
|
|
68
|
-
|
|
187
|
+
if (includesName(TEAMS_MESSAGE_EXTENSION_QUERY_NAMES, invokeName)) {
|
|
188
|
+
switch (invokeName) {
|
|
189
|
+
case "composeExtension/queryLink":
|
|
190
|
+
return "message-extension-query-link";
|
|
191
|
+
case "composeExtension/anonymousQueryLink":
|
|
192
|
+
return "message-extension-anonymous-query-link";
|
|
193
|
+
default:
|
|
194
|
+
return "message-extension-query";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (includesName(TEAMS_MESSAGE_EXTENSION_SELECT_NAMES, invokeName)) {
|
|
198
|
+
return "message-extension-select-item";
|
|
199
|
+
}
|
|
200
|
+
if (includesName(TEAMS_MESSAGE_EXTENSION_SUBMIT_ACTION_NAMES, invokeName)) {
|
|
201
|
+
return "message-extension-submit-action";
|
|
202
|
+
}
|
|
203
|
+
if (includesName(
|
|
204
|
+
TEAMS_MESSAGE_EXTENSION_QUERY_SETTING_URL_NAMES,
|
|
205
|
+
invokeName
|
|
206
|
+
)) {
|
|
207
|
+
return "message-extension-query-setting-url";
|
|
208
|
+
}
|
|
209
|
+
if (includesName(TEAMS_MESSAGE_EXTENSION_SETTING_NAMES, invokeName)) {
|
|
210
|
+
return "message-extension-setting";
|
|
211
|
+
}
|
|
212
|
+
if (includesName(
|
|
213
|
+
TEAMS_MESSAGE_EXTENSION_CARD_BUTTON_CLICKED_NAMES,
|
|
214
|
+
invokeName
|
|
215
|
+
)) {
|
|
216
|
+
return "message-extension-card-button-clicked";
|
|
217
|
+
}
|
|
218
|
+
if (includesName(TEAMS_CARD_ACTION_SUBMIT_NAMES, invokeName)) {
|
|
69
219
|
return payload?.actionName === "feedback" ? "feedback" : "card-submit";
|
|
70
220
|
}
|
|
71
221
|
return "invoke";
|
|
@@ -94,14 +244,15 @@ function parseActors(channelData) {
|
|
|
94
244
|
function parseTeamsActivity(context) {
|
|
95
245
|
const { activity } = context;
|
|
96
246
|
const channelData = asObject(activity.channelData);
|
|
247
|
+
const parsedChannelData = parseTypedChannelData(channelData);
|
|
97
248
|
const team = asObject(channelData?.team);
|
|
98
249
|
const channel = asObject(channelData?.channel);
|
|
99
250
|
const tenant = asObject(channelData?.tenant);
|
|
100
251
|
const meeting = asObject(channelData?.meeting);
|
|
101
|
-
const eventType = readString(channelData, "eventType");
|
|
252
|
+
const eventType = parsedChannelData?.eventType ?? readString(channelData, "eventType");
|
|
102
253
|
const conversationType = typeof activity.conversation?.conversationType === "string" ? activity.conversation.conversationType : void 0;
|
|
103
|
-
const teamId = readString(team, "aadGroupId") ?? readString(team, "id");
|
|
104
|
-
const meetingId = readString(meeting, "id");
|
|
254
|
+
const teamId = parsedChannelData?.team?.aadGroupId ?? parsedChannelData?.team?.id ?? readString(team, "aadGroupId") ?? readString(team, "id");
|
|
255
|
+
const meetingId = parsedChannelData?.meeting?.id ?? readString(meeting, "id");
|
|
105
256
|
const surface = detectSurface(
|
|
106
257
|
conversationType,
|
|
107
258
|
meetingId,
|
|
@@ -115,9 +266,10 @@ function parseTeamsActivity(context) {
|
|
|
115
266
|
replyToId: activity.replyToId ?? void 0,
|
|
116
267
|
invokeName: activity.name ?? void 0,
|
|
117
268
|
eventType,
|
|
118
|
-
tenantId: readString(tenant, "id") ?? activity.conversation?.tenantId,
|
|
269
|
+
tenantId: parsedChannelData?.tenant?.id ?? readString(tenant, "id") ?? activity.conversation?.tenantId,
|
|
119
270
|
teamId,
|
|
120
|
-
|
|
271
|
+
channelData: parsedChannelData,
|
|
272
|
+
channelThreadId: parsedChannelData?.channel?.id ?? readString(channel, "id"),
|
|
121
273
|
meetingId,
|
|
122
274
|
surface,
|
|
123
275
|
kind: detectKind(context, eventType),
|
|
@@ -131,13 +283,18 @@ function createTeamsChannelAdapter(options) {
|
|
|
131
283
|
prepareTurn,
|
|
132
284
|
handlers,
|
|
133
285
|
onInvoke: fallbackInvoke,
|
|
286
|
+
onTurn: onTurnHook,
|
|
134
287
|
...baseOptions
|
|
135
288
|
} = options;
|
|
136
289
|
const base = createM365ChannelAdapter({
|
|
137
290
|
...baseOptions,
|
|
138
291
|
prepareTurn: async (request) => {
|
|
139
292
|
const teams = parseTeamsActivity(request.turnContext);
|
|
140
|
-
const prepared = prepareTurn ? await prepareTurn({
|
|
293
|
+
const prepared = prepareTurn ? await prepareTurn({
|
|
294
|
+
...request,
|
|
295
|
+
teams,
|
|
296
|
+
channelData: teams.channelData
|
|
297
|
+
}) : void 0;
|
|
141
298
|
return {
|
|
142
299
|
...prepared ?? {},
|
|
143
300
|
scopeName: prepared?.scopeName?.trim() || "teams-activity",
|
|
@@ -160,34 +317,45 @@ function createTeamsChannelAdapter(options) {
|
|
|
160
317
|
},
|
|
161
318
|
onInvoke: async (context) => {
|
|
162
319
|
const teams = parseTeamsActivity(context);
|
|
163
|
-
const
|
|
320
|
+
const dispatch = await dispatchTeamsHandler(
|
|
164
321
|
handlers,
|
|
165
322
|
teams.kind,
|
|
166
323
|
await createHandlerContext(options, base, context, teams)
|
|
167
324
|
);
|
|
168
|
-
if (!handled && fallbackInvoke) {
|
|
325
|
+
if (!dispatch.handled && fallbackInvoke) {
|
|
169
326
|
await fallbackInvoke(context);
|
|
327
|
+
} else if (!dispatch.handled) {
|
|
328
|
+
await sendTeamsInvoke(context, void 0, 501);
|
|
170
329
|
}
|
|
171
330
|
}
|
|
172
331
|
});
|
|
173
|
-
async function
|
|
332
|
+
async function coreHandler(context) {
|
|
174
333
|
const teams = parseTeamsActivity(context);
|
|
175
334
|
if (teams.channelId !== "msteams") {
|
|
176
335
|
await base.handler(context);
|
|
177
336
|
return;
|
|
178
337
|
}
|
|
179
|
-
|
|
180
|
-
|
|
338
|
+
const isTeamsSpecificEvent = context.activity.type === ActivityTypes3.Event && teams.kind !== "other";
|
|
339
|
+
const isTeamsSpecificActivity = context.activity.type === ActivityTypes3.ConversationUpdate || context.activity.type === ActivityTypes3.MessageUpdate || context.activity.type === ActivityTypes3.MessageDelete || context.activity.type === ActivityTypes3.MessageReaction || isTeamsSpecificEvent;
|
|
340
|
+
if (isTeamsSpecificActivity) {
|
|
341
|
+
const dispatch = await dispatchTeamsHandler(
|
|
181
342
|
handlers,
|
|
182
343
|
teams.kind,
|
|
183
344
|
await createHandlerContext(options, base, context, teams)
|
|
184
345
|
);
|
|
185
|
-
if (handled) {
|
|
346
|
+
if (dispatch.handled) {
|
|
186
347
|
return;
|
|
187
348
|
}
|
|
188
349
|
}
|
|
189
350
|
await base.handler(context);
|
|
190
351
|
}
|
|
352
|
+
async function handler(context) {
|
|
353
|
+
if (onTurnHook) {
|
|
354
|
+
await onTurnHook(context, () => coreHandler(context));
|
|
355
|
+
} else {
|
|
356
|
+
await coreHandler(context);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
191
359
|
return {
|
|
192
360
|
...base,
|
|
193
361
|
handler,
|
|
@@ -208,10 +376,11 @@ async function createHandlerContext(options, base, turnContext, teams = parseTea
|
|
|
208
376
|
teams,
|
|
209
377
|
context: { teams }
|
|
210
378
|
};
|
|
211
|
-
|
|
379
|
+
const context = {
|
|
212
380
|
turnContext,
|
|
213
381
|
user,
|
|
214
382
|
teams,
|
|
383
|
+
channelData: teams.channelData,
|
|
215
384
|
sessionId,
|
|
216
385
|
source,
|
|
217
386
|
runAgent(message, runOptions) {
|
|
@@ -224,20 +393,60 @@ async function createHandlerContext(options, base, turnContext, teams = parseTea
|
|
|
224
393
|
);
|
|
225
394
|
},
|
|
226
395
|
async sendInvoke(body, status = 200) {
|
|
396
|
+
setInvokeResponseSent(context, true);
|
|
227
397
|
await sendTeamsInvoke(turnContext, body, status);
|
|
228
398
|
}
|
|
229
399
|
};
|
|
400
|
+
setInvokeResponseSent(context, false);
|
|
401
|
+
return context;
|
|
230
402
|
}
|
|
231
403
|
async function dispatchTeamsHandler(handlers, kind, context) {
|
|
232
404
|
const handler = resolveHandler(handlers, kind);
|
|
233
405
|
if (!handler) {
|
|
234
|
-
return false;
|
|
406
|
+
return { handled: false };
|
|
235
407
|
}
|
|
236
408
|
const result = await handler(context);
|
|
237
|
-
|
|
409
|
+
if (context.turnContext.activity.type === ActivityTypes3.Invoke && result !== "continue") {
|
|
410
|
+
if (!getInvokeResponseSent(context) && isTeamsInvokeResult(result)) {
|
|
411
|
+
await context.sendInvoke(result.body, result.status);
|
|
412
|
+
} else if (!getInvokeResponseSent(context) && result === void 0) {
|
|
413
|
+
await context.sendInvoke();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return { handled: result !== "continue" };
|
|
238
417
|
}
|
|
239
418
|
function resolveHandler(handlers, kind) {
|
|
240
419
|
switch (kind) {
|
|
420
|
+
case "conversation-update":
|
|
421
|
+
return handlers?.conversationUpdate;
|
|
422
|
+
case "members-added":
|
|
423
|
+
return handlers?.membersAdded ?? handlers?.conversationUpdate;
|
|
424
|
+
case "members-removed":
|
|
425
|
+
return handlers?.membersRemoved ?? handlers?.conversationUpdate;
|
|
426
|
+
case "channel-created":
|
|
427
|
+
return handlers?.channelCreated ?? handlers?.conversationUpdate;
|
|
428
|
+
case "channel-deleted":
|
|
429
|
+
return handlers?.channelDeleted ?? handlers?.conversationUpdate;
|
|
430
|
+
case "channel-renamed":
|
|
431
|
+
return handlers?.channelRenamed ?? handlers?.conversationUpdate;
|
|
432
|
+
case "channel-restored":
|
|
433
|
+
return handlers?.channelRestored ?? handlers?.conversationUpdate;
|
|
434
|
+
case "channel-shared":
|
|
435
|
+
return handlers?.channelShared ?? handlers?.conversationUpdate;
|
|
436
|
+
case "channel-unshared":
|
|
437
|
+
return handlers?.channelUnshared ?? handlers?.conversationUpdate;
|
|
438
|
+
case "team-renamed":
|
|
439
|
+
return handlers?.teamRenamed ?? handlers?.conversationUpdate;
|
|
440
|
+
case "team-archived":
|
|
441
|
+
return handlers?.teamArchived ?? handlers?.conversationUpdate;
|
|
442
|
+
case "team-unarchived":
|
|
443
|
+
return handlers?.teamUnarchived ?? handlers?.conversationUpdate;
|
|
444
|
+
case "team-deleted":
|
|
445
|
+
return handlers?.teamDeleted ?? handlers?.conversationUpdate;
|
|
446
|
+
case "team-hard-deleted":
|
|
447
|
+
return handlers?.teamHardDeleted ?? handlers?.conversationUpdate;
|
|
448
|
+
case "team-restored":
|
|
449
|
+
return handlers?.teamRestored ?? handlers?.conversationUpdate;
|
|
241
450
|
case "message-edit":
|
|
242
451
|
return handlers?.messageEdited;
|
|
243
452
|
case "message-delete":
|
|
@@ -246,6 +455,16 @@ function resolveHandler(handlers, kind) {
|
|
|
246
455
|
return handlers?.messageRestored;
|
|
247
456
|
case "reaction":
|
|
248
457
|
return handlers?.reaction;
|
|
458
|
+
case "read-receipt":
|
|
459
|
+
return handlers?.readReceipt;
|
|
460
|
+
case "config-fetch":
|
|
461
|
+
return handlers?.configFetch ?? handlers?.invoke;
|
|
462
|
+
case "config-submit":
|
|
463
|
+
return handlers?.configSubmit ?? handlers?.invoke;
|
|
464
|
+
case "file-consent":
|
|
465
|
+
return handlers?.fileConsent ?? handlers?.invoke;
|
|
466
|
+
case "actionable-message-execute-action":
|
|
467
|
+
return handlers?.actionableMessageExecuteAction ?? handlers?.invoke;
|
|
249
468
|
case "feedback":
|
|
250
469
|
return handlers?.feedbackSubmit ?? handlers?.cardSubmit;
|
|
251
470
|
case "card-submit":
|
|
@@ -256,14 +475,83 @@ function resolveHandler(handlers, kind) {
|
|
|
256
475
|
return handlers?.dialogSubmit;
|
|
257
476
|
case "search-command":
|
|
258
477
|
return handlers?.searchCommand;
|
|
478
|
+
case "message-extension-query":
|
|
479
|
+
return handlers?.messageExtensionQuery ?? handlers?.searchCommand;
|
|
480
|
+
case "message-extension-query-link":
|
|
481
|
+
return handlers?.messageExtensionQueryLink ?? handlers?.searchCommand;
|
|
482
|
+
case "message-extension-anonymous-query-link":
|
|
483
|
+
return handlers?.messageExtensionAnonymousQueryLink ?? handlers?.searchCommand;
|
|
259
484
|
case "select-result":
|
|
260
485
|
return handlers?.selectResult;
|
|
486
|
+
case "message-extension-select-item":
|
|
487
|
+
return handlers?.messageExtensionSelectItem ?? handlers?.selectResult;
|
|
488
|
+
case "message-extension-submit-action":
|
|
489
|
+
return handlers?.messageExtensionSubmitAction ?? handlers?.cardSubmit;
|
|
490
|
+
case "message-extension-fetch-task":
|
|
491
|
+
return handlers?.messageExtensionFetchTask ?? handlers?.dialogOpen;
|
|
492
|
+
case "message-extension-query-setting-url":
|
|
493
|
+
return handlers?.messageExtensionQuerySettingUrl ?? handlers?.invoke;
|
|
494
|
+
case "message-extension-setting":
|
|
495
|
+
return handlers?.messageExtensionSetting ?? handlers?.invoke;
|
|
496
|
+
case "message-extension-card-button-clicked":
|
|
497
|
+
return handlers?.messageExtensionCardButtonClicked ?? handlers?.invoke;
|
|
498
|
+
case "task-fetch":
|
|
499
|
+
return handlers?.taskFetch ?? handlers?.dialogOpen;
|
|
500
|
+
case "task-submit":
|
|
501
|
+
return handlers?.taskSubmit ?? handlers?.dialogSubmit;
|
|
502
|
+
case "tab-fetch":
|
|
503
|
+
return handlers?.tabFetch ?? handlers?.invoke;
|
|
504
|
+
case "tab-submit":
|
|
505
|
+
return handlers?.tabSubmit ?? handlers?.invoke;
|
|
261
506
|
case "invoke":
|
|
262
507
|
return handlers?.invoke;
|
|
508
|
+
case "meeting-start":
|
|
509
|
+
return handlers?.meetingStart;
|
|
510
|
+
case "meeting-end":
|
|
511
|
+
return handlers?.meetingEnd;
|
|
512
|
+
case "participants-join":
|
|
513
|
+
return handlers?.participantsJoin;
|
|
514
|
+
case "participants-leave":
|
|
515
|
+
return handlers?.participantsLeave;
|
|
516
|
+
case "meeting-room-join":
|
|
517
|
+
return handlers?.meetingRoomJoin;
|
|
518
|
+
case "meeting-room-leave":
|
|
519
|
+
return handlers?.meetingRoomLeave;
|
|
520
|
+
case "meeting-stage-view":
|
|
521
|
+
return handlers?.meetingStageView;
|
|
522
|
+
case "meeting-smart-reply":
|
|
523
|
+
return handlers?.meetingSmartReply;
|
|
524
|
+
case "meeting-reaction":
|
|
525
|
+
return handlers?.meetingReaction;
|
|
526
|
+
case "meeting-poll-response":
|
|
527
|
+
return handlers?.meetingPollResponse;
|
|
528
|
+
case "meeting-apps-installed":
|
|
529
|
+
return handlers?.meetingAppsInstalled;
|
|
530
|
+
case "meeting-apps-uninstalled":
|
|
531
|
+
return handlers?.meetingAppsUninstalled;
|
|
532
|
+
case "meeting-recording-started":
|
|
533
|
+
return handlers?.meetingRecordingStarted;
|
|
534
|
+
case "meeting-recording-stopped":
|
|
535
|
+
return handlers?.meetingRecordingStopped;
|
|
536
|
+
case "meeting-focus-change":
|
|
537
|
+
return handlers?.meetingFocusChange;
|
|
538
|
+
case "meeting-screen-share-start":
|
|
539
|
+
return handlers?.meetingScreenShareStart;
|
|
540
|
+
case "meeting-screen-share-stop":
|
|
541
|
+
return handlers?.meetingScreenShareStop;
|
|
263
542
|
default:
|
|
264
543
|
return void 0;
|
|
265
544
|
}
|
|
266
545
|
}
|
|
546
|
+
function isTeamsInvokeResult(value) {
|
|
547
|
+
return !!value && typeof value === "object" && "status" in value;
|
|
548
|
+
}
|
|
549
|
+
function getInvokeResponseSent(context) {
|
|
550
|
+
return context.__invokeResponseSent === true;
|
|
551
|
+
}
|
|
552
|
+
function setInvokeResponseSent(context, value) {
|
|
553
|
+
context.__invokeResponseSent = value;
|
|
554
|
+
}
|
|
267
555
|
function resolveSource(options) {
|
|
268
556
|
if (options.source) return options.source;
|
|
269
557
|
if (options.agent) return options.agent;
|
|
@@ -329,9 +617,7 @@ async function mountTeamsAgent(source, options = {}) {
|
|
|
329
617
|
app: providedApp,
|
|
330
618
|
...adapterOptions
|
|
331
619
|
} = options;
|
|
332
|
-
const authConfig = hostingModule.getAuthConfigWithDefaults(
|
|
333
|
-
providedAuthConfig
|
|
334
|
-
);
|
|
620
|
+
const authConfig = hostingModule.getAuthConfigWithDefaults(providedAuthConfig);
|
|
335
621
|
const cloudAdapter = providedAdapter ?? new hostingModule.CloudAdapter(authConfig);
|
|
336
622
|
const expressApp = providedApp ?? expressModule.default();
|
|
337
623
|
const channelAdapter = createTeamsChannelAdapter({
|
|
@@ -354,8 +640,9 @@ async function mountTeamsAgent(source, options = {}) {
|
|
|
354
640
|
let server;
|
|
355
641
|
if (port !== null) {
|
|
356
642
|
const onListen = () => {
|
|
357
|
-
|
|
358
|
-
`Teams agent listening on port ${port} \u2014 appId: ${authConfig.clientId ?? "(not set)"}
|
|
643
|
+
process.stdout.write(
|
|
644
|
+
`Teams agent listening on port ${port} \u2014 appId: ${authConfig.clientId ?? "(not set)"}
|
|
645
|
+
`
|
|
359
646
|
);
|
|
360
647
|
};
|
|
361
648
|
server = host ? expressApp.listen(port, host, onListen) : expressApp.listen(port, onListen);
|
|
@@ -446,6 +733,27 @@ function createTeamsDialogMessage(message) {
|
|
|
446
733
|
};
|
|
447
734
|
}
|
|
448
735
|
export {
|
|
736
|
+
TEAMS_ACTIONABLE_MESSAGE_NAMES,
|
|
737
|
+
TEAMS_CARD_ACTION_SUBMIT_NAMES,
|
|
738
|
+
TEAMS_CONFIG_FETCH_NAMES,
|
|
739
|
+
TEAMS_CONFIG_SUBMIT_NAMES,
|
|
740
|
+
TEAMS_CONVERSATION_UPDATE_EVENT_NAMES,
|
|
741
|
+
TEAMS_FILE_CONSENT_NAMES,
|
|
742
|
+
TEAMS_MEETING_EVENT_NAMES,
|
|
743
|
+
TEAMS_MEETING_INVOKE_NAMES,
|
|
744
|
+
TEAMS_MESSAGE_EXTENSION_CARD_BUTTON_CLICKED_NAMES,
|
|
745
|
+
TEAMS_MESSAGE_EXTENSION_FETCH_TASK_NAMES,
|
|
746
|
+
TEAMS_MESSAGE_EXTENSION_QUERY_NAMES,
|
|
747
|
+
TEAMS_MESSAGE_EXTENSION_QUERY_SETTING_URL_NAMES,
|
|
748
|
+
TEAMS_MESSAGE_EXTENSION_SELECT_NAMES,
|
|
749
|
+
TEAMS_MESSAGE_EXTENSION_SETTING_NAMES,
|
|
750
|
+
TEAMS_MESSAGE_EXTENSION_SUBMIT_ACTION_NAMES,
|
|
751
|
+
TEAMS_TAB_FETCH_NAMES,
|
|
752
|
+
TEAMS_TAB_SUBMIT_NAMES,
|
|
753
|
+
TEAMS_TASK_MODULE_FETCH_NAMES,
|
|
754
|
+
TEAMS_TASK_MODULE_SUBMIT_NAMES,
|
|
755
|
+
TeamsAttachmentDownloader,
|
|
756
|
+
TeamsInfo,
|
|
449
757
|
createTeamsChannelAdapter,
|
|
450
758
|
createTeamsDialog,
|
|
451
759
|
createTeamsDialogMessage,
|
|
@@ -455,6 +763,7 @@ export {
|
|
|
455
763
|
currentTeamsTurnContext,
|
|
456
764
|
mountTeamsAgent,
|
|
457
765
|
parseTeamsActivity,
|
|
766
|
+
parseTeamsChannelData,
|
|
458
767
|
runWithTeamsTurnContext,
|
|
459
768
|
sendTeamsInvoke
|
|
460
769
|
};
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @cuylabs/agent-channel-teams Docs
|
|
2
|
+
|
|
3
|
+
Use this doc set when you are building Teams-native workflows on top of the
|
|
4
|
+
generic M365 transport layer and want to stay on the cuylabs execution model
|
|
5
|
+
instead of adopting Microsoft `AgentApplication`.
|
|
6
|
+
|
|
7
|
+
## Doc Map
|
|
8
|
+
|
|
9
|
+
| Document | Purpose |
|
|
10
|
+
| -------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
11
|
+
| [architecture.md](./architecture.md) | Layering between `agent-core`, `agent-channel-m365`, and `agent-channel-teams` |
|
|
12
|
+
| [handler-model.md](./handler-model.md) | Handler groups, meeting routing, invoke flows, and `onTurn` middleware |
|
|
13
|
+
| [teams-helpers.md](./teams-helpers.md) | `TeamsInfo`, typed channel data, safe DTOs, and when to use the `./extensions` subpath |
|
|
14
|
+
|
|
15
|
+
## Source Map
|
|
16
|
+
|
|
17
|
+
- `src/adapter.ts`
|
|
18
|
+
- `createTeamsChannelAdapter()`, Teams-specific dispatch, `onTurn`, Teams handler context
|
|
19
|
+
- `src/teams-activity.ts`
|
|
20
|
+
- parsed Teams metadata, event-name constants, typed channel data projection
|
|
21
|
+
- `src/types.ts`
|
|
22
|
+
- public Teams handler groups, context contracts, Teams-specific options
|
|
23
|
+
- `src/extensions.ts`
|
|
24
|
+
- curated Teams helper and DTO bridge from `@microsoft/agents-hosting-extensions-teams`
|
|
25
|
+
- `src/responses.ts`
|
|
26
|
+
- Teams dialog and message-extension response builders
|
|
27
|
+
|
|
28
|
+
## Suggested Reading Order
|
|
29
|
+
|
|
30
|
+
1. Start with the package [README.md](../README.md) for quick usage.
|
|
31
|
+
2. Read [architecture.md](./architecture.md) to understand the bridge boundary.
|
|
32
|
+
3. Read [handler-model.md](./handler-model.md) when wiring handlers.
|
|
33
|
+
4. Read [teams-helpers.md](./teams-helpers.md) when you need Teams REST helpers or typed channel data.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Teams Bridge Architecture
|
|
2
|
+
|
|
3
|
+
`@cuylabs/agent-channel-teams` is the Teams-native facade that sits on top of
|
|
4
|
+
`@cuylabs/agent-channel-m365`.
|
|
5
|
+
|
|
6
|
+
It exists so you can add Teams concepts such as meeting events, message
|
|
7
|
+
extensions, task modules, typed channel data, and Teams helper APIs without
|
|
8
|
+
moving your app to Microsoft `AgentApplication` as the runtime model.
|
|
9
|
+
|
|
10
|
+
## Layer Split
|
|
11
|
+
|
|
12
|
+
| Layer | Package | Responsibility |
|
|
13
|
+
| ------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
14
|
+
| Execution | `@cuylabs/agent-core` | Agentic loop, tools, sessions, inference, middleware |
|
|
15
|
+
| Generic transport | `@cuylabs/agent-channel-m365` | Activity Protocol transport, `TurnContext` bridge, session mapping, event streaming |
|
|
16
|
+
| Teams-native facade | `@cuylabs/agent-channel-teams` | Teams parsing, Teams handlers, Teams response builders, Teams helper bridge |
|
|
17
|
+
| SDK helpers | `@microsoft/agents-hosting-extensions-teams` | Teams REST helpers, typed channel data, DTOs |
|
|
18
|
+
|
|
19
|
+
## Architectural Boundary
|
|
20
|
+
|
|
21
|
+
The key rule is:
|
|
22
|
+
|
|
23
|
+
- use Microsoft Teams extensions as implementation building blocks
|
|
24
|
+
- do not expose Microsoft `AgentApplication` router classes as the app-facing model
|
|
25
|
+
|
|
26
|
+
That means `agent-channel-teams` can safely re-expose:
|
|
27
|
+
|
|
28
|
+
- `TeamsInfo`
|
|
29
|
+
- `parseTeamsChannelData`
|
|
30
|
+
- typed Teams DTOs
|
|
31
|
+
- Teams event-name mappings
|
|
32
|
+
|
|
33
|
+
But it intentionally avoids making these part of the public cuylabs programming
|
|
34
|
+
model:
|
|
35
|
+
|
|
36
|
+
- `TeamsAgentExtension`
|
|
37
|
+
- `Meeting`
|
|
38
|
+
- `MessageExtension`
|
|
39
|
+
- `TaskModule`
|
|
40
|
+
- other `AgentApplication` or `ActivityHandler`-oriented abstractions
|
|
41
|
+
|
|
42
|
+
## Request Flow
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
Teams
|
|
46
|
+
-> @microsoft/agents-hosting
|
|
47
|
+
-> @cuylabs/agent-channel-m365
|
|
48
|
+
-> @cuylabs/agent-channel-teams
|
|
49
|
+
-> @cuylabs/agent-core
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## What `agent-channel-teams` Adds
|
|
53
|
+
|
|
54
|
+
Compared with `agent-channel-m365`, this package adds:
|
|
55
|
+
|
|
56
|
+
- Teams-specific parsed metadata via `parseTeamsActivity()`
|
|
57
|
+
- typed `channelData` projection into handler and `prepareTurn()` contexts
|
|
58
|
+
- Teams-native handler groups for meetings, task modules, message extensions,
|
|
59
|
+
feedback, and message edits
|
|
60
|
+
- Teams invoke response helpers and response builders
|
|
61
|
+
- a curated Teams helper facade built around `TeamsInfo`
|
|
62
|
+
|
|
63
|
+
## Why This Is The Enterprise Boundary
|
|
64
|
+
|
|
65
|
+
This layering lets you:
|
|
66
|
+
|
|
67
|
+
- keep `agent-core` as the only execution engine
|
|
68
|
+
- keep `agent-channel-m365` generic and reusable across Activity clients
|
|
69
|
+
- add Teams-specific capabilities without forking the runtime model
|
|
70
|
+
- consume Microsoft Teams helper code without leaking Microsoft routing
|
|
71
|
+
abstractions into app code
|