@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/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 === "task/fetch" || invokeName === "composeExtension/fetchTask") {
61
- return "dialog-open";
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 === "task/submit") return "dialog-submit";
64
- if (invokeName === "composeExtension/query" || invokeName === "composeExtension/queryLink" || invokeName === "composeExtension/anonymousQueryLink") {
65
- return "search-command";
184
+ if (includesName(TEAMS_TAB_SUBMIT_NAMES, invokeName)) {
185
+ return "tab-submit";
66
186
  }
67
- if (invokeName === "composeExtension/selectItem") return "select-result";
68
- if (invokeName === "message/submitAction" || invokeName === "composeExtension/submitAction") {
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
- channelThreadId: readString(channel, "id"),
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({ ...request, teams }) : void 0;
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 handled = await dispatchTeamsHandler(
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 handler(context) {
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
- if (context.activity.type === ActivityTypes3.MessageUpdate || context.activity.type === ActivityTypes3.MessageDelete || context.activity.type === ActivityTypes3.MessageReaction) {
180
- const handled = await dispatchTeamsHandler(
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
- return {
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
- return result !== "continue";
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
- console.log(
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