@cuylabs/agent-channel-teams 0.10.0 → 0.11.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 CHANGED
@@ -24,25 +24,42 @@ Teams
24
24
  The value is:
25
25
 
26
26
  - parsed Teams activity metadata instead of raw `activity.channelData`
27
- - Teams-specific hooks for dialog open/submit, search-style invokes, card submit, feedback, edits, deletes, and reactions
27
+ - curated Teams helpers (`TeamsInfo`, `parseTeamsChannelData`) exposed through the cuylabs package
28
+ - Teams-specific hooks for conversation lifecycle, dialog/task modules, message extensions, tabs, config, card submits, feedback, edits, deletes, reactions, and meeting events
28
29
  - Teams response builders for dialog and search-style invoke payloads
29
30
  - invoke response helpers for Teams handlers, plus fallback access to generic M365 `onInvoke`
31
+ - attachment download helpers exposed through the curated Teams helper surface
30
32
  - ambient Teams turn context that tools and middleware can read
31
33
  - a way for Teams-specific handlers to still call back into `agent-core`
32
34
 
33
35
  ## What It Does Not Do
34
36
 
35
37
  - it does not replace `agent-channel-m365`
36
- - it does not require `@microsoft/agents-hosting-extensions-teams`
37
38
  - it does not copy Microsoft's routing model
38
39
  - it does not turn `agent-core` into a Teams-only framework
39
40
 
40
41
  ## Install
41
42
 
42
43
  ```bash
43
- pnpm add @cuylabs/agent-channel-teams @cuylabs/agent-channel-m365 @microsoft/agents-hosting express
44
+ pnpm add @cuylabs/agent-channel-teams @cuylabs/agent-channel-m365 @microsoft/agents-hosting @microsoft/agents-hosting-extensions-teams express
44
45
  ```
45
46
 
47
+ `agent-channel-teams` depends on `@microsoft/agents-hosting-extensions-teams`
48
+ because Teams helpers and typed channel data are part of the supported bridge
49
+ contract, not an optional add-on.
50
+
51
+ ## Bridge Design
52
+
53
+ `agent-channel-teams` is the Teams-native facade for the cuylabs stack:
54
+
55
+ - `agent-core` still owns the agentic loop, tools, and inference
56
+ - `agent-channel-m365` still owns generic M365 transport and `TurnContext` bridging
57
+ - `agent-channel-teams` adds Teams-specific dispatch, typed metadata, and helper access
58
+ - Microsoft `AgentApplication` routing abstractions are intentionally not the public programming model here
59
+
60
+ That means you can use Microsoft Teams helpers where they fit, while keeping the
61
+ application architecture centered on cuylabs handlers and `agent-core`.
62
+
46
63
  ## Example
47
64
 
48
65
  ```ts
@@ -64,9 +81,11 @@ const teams = createTeamsChannelAdapter({
64
81
  },
65
82
  }),
66
83
  handlers: {
67
- async dialogSubmit(ctx) {
84
+ async taskSubmit(ctx) {
68
85
  const prompt = JSON.stringify(ctx.turnContext.activity.value);
69
- const events = ctx.runAgent(`Handle this Teams dialog payload:\n${prompt}`);
86
+ const events = ctx.runAgent(
87
+ `Handle this Teams dialog payload:\n${prompt}`,
88
+ );
70
89
 
71
90
  let text = "";
72
91
  for await (const event of events) {
@@ -75,7 +94,10 @@ const teams = createTeamsChannelAdapter({
75
94
  }
76
95
  }
77
96
 
78
- await ctx.sendInvoke(createTeamsDialogMessage(text));
97
+ return {
98
+ status: 200,
99
+ body: createTeamsDialogMessage(text),
100
+ };
79
101
  },
80
102
  },
81
103
  });
@@ -103,11 +125,200 @@ Read Teams metadata from tools or middleware during a turn.
103
125
  Send a Teams invoke response body without manually constructing an
104
126
  `invokeResponse` activity.
105
127
 
128
+ Invoke handlers can also return a `TeamsInvokeResult` directly. If a handled
129
+ invoke returns nothing, the adapter sends an empty `200` acknowledgement. If no
130
+ Teams handler handles the invoke, the adapter returns an explicit `501`.
131
+
106
132
  ### `createTeamsDialog(...)` and `createTeamsSearchResult(...)`
107
133
 
108
134
  Build common Teams dialog/search invoke bodies without making callers hand-roll
109
135
  raw payload objects.
110
136
 
137
+ ## TeamsInfo and Typed Channel Data
138
+
139
+ The curated Teams helper surface is available directly from the main barrel. If
140
+ you prefer a grouped import path, the same exports are also available from the
141
+ `./extensions` subpath.
142
+
143
+ These helpers do not require `AgentApplication`; they work with the same
144
+ `TurnContext` already used by the cuylabs adapter layer.
145
+
146
+ ```ts
147
+ import {
148
+ TeamsInfo,
149
+ TeamsAttachmentDownloader,
150
+ parseTeamsChannelData,
151
+ } from "@cuylabs/agent-channel-teams";
152
+
153
+ const teams = createTeamsChannelAdapter({
154
+ agent,
155
+ handlers: {
156
+ async dialogSubmit(ctx) {
157
+ const members = await TeamsInfo.getPagedMembers(ctx.turnContext);
158
+ const names = members.members.map((m) => m.name).join(", ");
159
+ await ctx.sendInvoke(createTeamsDialogMessage(`Team members: ${names}`));
160
+ },
161
+ },
162
+ });
163
+
164
+ // Zod-validated typed channel data
165
+ const channelData = parseTeamsChannelData(ctx.turnContext.activity.channelData);
166
+ console.log(channelData.team?.id, channelData.tenant?.id);
167
+ ```
168
+
169
+ `parseTeamsChannelData()` is intentionally strict. If the incoming Teams payload
170
+ does not match the expected SDK shape, parsing fails fast instead of silently
171
+ dropping typed data.
172
+
173
+ The same helpers are also grouped under:
174
+
175
+ ```ts
176
+ import {
177
+ TeamsInfo,
178
+ parseTeamsChannelData,
179
+ } from "@cuylabs/agent-channel-teams/extensions";
180
+ ```
181
+
182
+ Type-only imports are available from the main barrel:
183
+
184
+ ```ts
185
+ import type {
186
+ TeamDetails,
187
+ TeamsChannelData,
188
+ TeamsMeetingInfo,
189
+ } from "@cuylabs/agent-channel-teams";
190
+ ```
191
+
192
+ Handler and `prepareTurn()` contexts also expose parsed channel data directly:
193
+
194
+ ```ts
195
+ const teams = createTeamsChannelAdapter({
196
+ agent,
197
+ prepareTurn: ({ teams, channelData }) => ({
198
+ system: `Tenant: ${channelData?.tenant?.id ?? teams.tenantId ?? "unknown"}`,
199
+ }),
200
+ handlers: {
201
+ meetingStart(ctx) {
202
+ console.log(ctx.channelData?.meeting?.id ?? ctx.teams.meetingId);
203
+ },
204
+ },
205
+ });
206
+ ```
207
+
208
+ ## Meeting Handlers
209
+
210
+ Meeting events are dispatched through the same `handlers` object as other Teams activities. Meeting events are `Event`-type activities (except `meetingStageView` and `meetingSmartReply` which are `Invoke`-type).
211
+
212
+ ```ts
213
+ const teams = createTeamsChannelAdapter({
214
+ agent,
215
+ handlers: {
216
+ meetingStart(ctx) {
217
+ console.log("Meeting started:", ctx.teams.meetingId);
218
+ },
219
+ meetingEnd(ctx) {
220
+ console.log("Meeting ended:", ctx.teams.meetingId);
221
+ },
222
+ participantsJoin(ctx) {
223
+ console.log("Participants joined");
224
+ },
225
+ participantsLeave(ctx) {
226
+ console.log("Participants left");
227
+ },
228
+ },
229
+ });
230
+ ```
231
+
232
+ All 17 meeting event kinds from Microsoft's SDK are supported:
233
+
234
+ | Handler | Activity type | Teams event name |
235
+ | ------------------------- | ------------- | ------------------------- |
236
+ | `meetingStart` | Event | `meetingStart` |
237
+ | `meetingEnd` | Event | `meetingEnd` |
238
+ | `participantsJoin` | Event | `meetingParticipantJoin` |
239
+ | `participantsLeave` | Event | `meetingParticipantLeave` |
240
+ | `meetingRoomJoin` | Event | `meetingRoomJoin` |
241
+ | `meetingRoomLeave` | Event | `meetingRoomLeave` |
242
+ | `meetingReaction` | Event | `meetingReaction` |
243
+ | `meetingPollResponse` | Event | `meetingPollResponse` |
244
+ | `meetingAppsInstalled` | Event | `meetingAppsInstalled` |
245
+ | `meetingAppsUninstalled` | Event | `meetingAppsUninstalled` |
246
+ | `meetingRecordingStarted` | Event | `meetingRecordingStarted` |
247
+ | `meetingRecordingStopped` | Event | `meetingRecordingStopped` |
248
+ | `meetingFocusChange` | Event | `meetingFocusChange` |
249
+ | `meetingScreenShareStart` | Event | `meetingScreenShareStart` |
250
+ | `meetingScreenShareStop` | Event | `meetingScreenShareStop` |
251
+ | `meetingStageView` | Invoke | `meetingStageView` |
252
+ | `meetingSmartReply` | Invoke | `meetingSmartReply` |
253
+
254
+ The event-name mappings are exported for hosts that need explicit routing or
255
+ observability constants:
256
+
257
+ ```ts
258
+ import {
259
+ TEAMS_MEETING_EVENT_NAMES,
260
+ TEAMS_MEETING_INVOKE_NAMES,
261
+ } from "@cuylabs/agent-channel-teams";
262
+ ```
263
+
264
+ ## Handler Capability Groups
265
+
266
+ For larger apps, the handler interfaces are also grouped by capability:
267
+
268
+ - `TeamsMessageActivityHandlers`
269
+ - `TeamsConversationUpdateHandlers`
270
+ - `TeamsCardActionHandlers`
271
+ - `TeamsConfigHandlers`
272
+ - `TeamsTaskModuleHandlers`
273
+ - `TeamsMessageExtensionHandlers`
274
+ - `TeamsTabHandlers`
275
+ - `TeamsMeetingHandlers`
276
+
277
+ `TeamsActivityHandlers` composes these groups into the full Teams surface.
278
+
279
+ ## `onTurn` Middleware Hook
280
+
281
+ For advanced use cases where you need raw `TurnContext` access before cuylabs processing — for example, to use Microsoft SDK helpers directly or to short-circuit certain activity types — use the `onTurn` hook:
282
+
283
+ ```ts
284
+ const teams = createTeamsChannelAdapter({
285
+ agent,
286
+ onTurn: async (context, next) => {
287
+ console.log(
288
+ `Activity: ${context.activity.type} / ${context.activity.name}`,
289
+ );
290
+
291
+ // Call next() to continue into the normal handler pipeline
292
+ await next();
293
+ },
294
+ handlers: {
295
+ // ...
296
+ },
297
+ });
298
+ ```
299
+
300
+ You can short-circuit by not calling `next()`:
301
+
302
+ ```ts
303
+ onTurn: async (context, next) => {
304
+ if (shouldSkip(context)) {
305
+ // Don't call next() — cuylabs processing is skipped entirely
306
+ return;
307
+ }
308
+ await next();
309
+ },
310
+ ```
311
+
312
+ ## Docs
313
+
314
+ The package README is the quick-start view. For the focused concept guides, use
315
+ the docs set:
316
+
317
+ - [docs/README.md](docs/README.md) for the documentation index
318
+ - [docs/architecture.md](docs/architecture.md) for the bridge boundary and layering
319
+ - [docs/handler-model.md](docs/handler-model.md) for handler groups, meeting routing, and `onTurn`
320
+ - [docs/teams-helpers.md](docs/teams-helpers.md) for `TeamsInfo`, typed channel data, and the curated helper surface
321
+
111
322
  ## Why This Package Exists
112
323
 
113
324
  This package is the bridge between:
@@ -119,3 +330,9 @@ This package is the bridge between:
119
330
  That means you can start with ordinary Teams bot chat now, and then add
120
331
  dialogs, message actions, or search-oriented Teams invokes without changing
121
332
  your core agent architecture.
333
+
334
+ The important boundary is:
335
+
336
+ - Microsoft SDK helpers and DTOs are available where they fit naturally
337
+ - `agent-core` remains the only execution engine
338
+ - `AgentApplication`, `Meeting`, `TaskModule`, and similar Microsoft router classes are intentionally not part of the public cuylabs model
@@ -0,0 +1,12 @@
1
+ // src/extensions.ts
2
+ import {
3
+ TeamsAttachmentDownloader,
4
+ TeamsInfo
5
+ } from "@microsoft/agents-hosting-extensions-teams";
6
+ import { parseTeamsChannelData } from "@microsoft/agents-hosting-extensions-teams";
7
+
8
+ export {
9
+ TeamsAttachmentDownloader,
10
+ TeamsInfo,
11
+ parseTeamsChannelData
12
+ };
@@ -0,0 +1 @@
1
+ export { BatchFailedEntriesResponse, BatchOperationResponse, BatchOperationStateResponse, CancelOperationResponse, ChannelInfo, FeedbackLoopData, MeetingNotification, NotificationInfo, OnBehalfOf, TeamDetails, TeamInfo, TeamsAttachmentDownloader, TeamsChannelAccount, TeamsChannelData, TeamsChannelDataSettings, TeamsInfo, TeamsMeetingInfo, TeamsMember, TeamsPagedMembersResult, TenantInfo, parseTeamsChannelData } from '@microsoft/agents-hosting-extensions-teams';
@@ -0,0 +1,10 @@
1
+ import {
2
+ TeamsAttachmentDownloader,
3
+ TeamsInfo,
4
+ parseTeamsChannelData
5
+ } from "./chunk-B5KRH22J.js";
6
+ export {
7
+ TeamsAttachmentDownloader,
8
+ TeamsInfo,
9
+ parseTeamsChannelData
10
+ };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,21 @@
1
1
  import { AgentEvent } from '@cuylabs/agent-core';
2
2
  import { M365ChannelOptions, M365TurnRequestContext, M365TurnPreparation, M365UserIdentity, M365TurnSource, M365ChannelAdapter, M365AmbientTurnContext } from '@cuylabs/agent-channel-m365';
3
3
  import { TurnContext, AuthConfiguration, CloudAdapter } from '@microsoft/agents-hosting';
4
+ import { TeamsChannelData } from '@microsoft/agents-hosting-extensions-teams';
5
+ export { BatchFailedEntriesResponse, BatchOperationResponse, BatchOperationStateResponse, CancelOperationResponse, ChannelInfo, FeedbackLoopData, MeetingNotification, NotificationInfo, OnBehalfOf, TeamDetails, TeamInfo, TeamsAttachmentDownloader, TeamsChannelAccount, TeamsChannelData, TeamsChannelDataSettings, TeamsInfo, TeamsMeetingInfo, TeamsMember, TeamsPagedMembersResult, TenantInfo, parseTeamsChannelData } from '@microsoft/agents-hosting-extensions-teams';
6
+ import { Activity, Attachment } from '@microsoft/agents-activity';
4
7
  import { Server } from 'node:http';
5
8
  import { Application } from 'express';
6
- import { Activity, Attachment } from '@microsoft/agents-activity';
9
+
10
+ interface TeamsInvokeResult<T = unknown> {
11
+ status: number;
12
+ body?: T;
13
+ }
14
+ declare function createTeamsInvokeActivity<T = unknown>(result: TeamsInvokeResult<T>): Activity;
15
+ declare function sendTeamsInvoke<T = unknown>(context: TurnContext, body?: T, status?: number): Promise<void>;
7
16
 
8
17
  type TeamsSurface = "personal" | "channel" | "group" | "meeting" | "unknown";
9
- type TeamsActivityKind = "message" | "message-edit" | "message-delete" | "message-restore" | "reaction" | "card-submit" | "feedback" | "dialog-open" | "dialog-submit" | "search-command" | "select-result" | "invoke" | "conversation-update" | "other";
18
+ type TeamsActivityKind = "message" | "conversation-update" | "members-added" | "members-removed" | "channel-created" | "channel-deleted" | "channel-renamed" | "channel-restored" | "channel-shared" | "channel-unshared" | "team-renamed" | "team-archived" | "team-unarchived" | "team-deleted" | "team-hard-deleted" | "team-restored" | "message-edit" | "message-delete" | "message-restore" | "reaction" | "read-receipt" | "config-fetch" | "config-submit" | "file-consent" | "actionable-message-execute-action" | "card-submit" | "feedback" | "dialog-open" | "dialog-submit" | "task-fetch" | "task-submit" | "search-command" | "message-extension-query" | "message-extension-query-link" | "message-extension-anonymous-query-link" | "select-result" | "message-extension-select-item" | "message-extension-submit-action" | "message-extension-fetch-task" | "message-extension-query-setting-url" | "message-extension-setting" | "message-extension-card-button-clicked" | "tab-fetch" | "tab-submit" | "invoke" | "meeting-start" | "meeting-end" | "participants-join" | "participants-leave" | "meeting-room-join" | "meeting-room-leave" | "meeting-stage-view" | "meeting-smart-reply" | "meeting-reaction" | "meeting-poll-response" | "meeting-apps-installed" | "meeting-apps-uninstalled" | "meeting-recording-started" | "meeting-recording-stopped" | "meeting-focus-change" | "meeting-screen-share-start" | "meeting-screen-share-stop" | "other";
10
19
  interface TeamsActorReference {
11
20
  id?: string;
12
21
  displayName?: string;
@@ -25,19 +34,21 @@ interface TeamsActivityInfo {
25
34
  teamId?: string;
26
35
  channelThreadId?: string;
27
36
  meetingId?: string;
37
+ channelData?: TeamsChannelData;
28
38
  surface: TeamsSurface;
29
39
  kind: TeamsActivityKind;
30
40
  actors: TeamsActorReference[];
31
41
  }
32
42
  interface TeamsTurnRequestContext extends M365TurnRequestContext {
33
43
  teams: TeamsActivityInfo;
44
+ channelData?: TeamsChannelData;
34
45
  }
35
46
  type TeamsTurnPreparation = M365TurnPreparation;
36
- type TeamsHandlerDecision = void | "continue";
37
47
  interface TeamsHandlerContext {
38
48
  turnContext: TurnContext;
39
49
  user: M365UserIdentity;
40
50
  teams: TeamsActivityInfo;
51
+ channelData?: TeamsChannelData;
41
52
  sessionId: string;
42
53
  source: M365TurnSource;
43
54
  /**
@@ -55,18 +66,109 @@ interface TeamsHandlerContext {
55
66
  */
56
67
  sendInvoke(body?: unknown, status?: number): Promise<void>;
57
68
  }
58
- interface TeamsActivityHandlers {
59
- messageEdited?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
60
- messageDeleted?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
61
- messageRestored?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
62
- reaction?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
63
- cardSubmit?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
64
- feedbackSubmit?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
65
- dialogOpen?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
66
- dialogSubmit?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
67
- searchCommand?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
68
- selectResult?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
69
- invoke?: (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
69
+ type TeamsHandlerDecision = void | "continue" | TeamsInvokeResult;
70
+ type TeamsActivityHandler = (context: TeamsHandlerContext) => TeamsHandlerDecision | Promise<TeamsHandlerDecision>;
71
+ interface TeamsConversationUpdateHandlers {
72
+ conversationUpdate?: TeamsActivityHandler;
73
+ membersAdded?: TeamsActivityHandler;
74
+ membersRemoved?: TeamsActivityHandler;
75
+ channelCreated?: TeamsActivityHandler;
76
+ channelDeleted?: TeamsActivityHandler;
77
+ channelRenamed?: TeamsActivityHandler;
78
+ channelRestored?: TeamsActivityHandler;
79
+ channelShared?: TeamsActivityHandler;
80
+ channelUnshared?: TeamsActivityHandler;
81
+ teamRenamed?: TeamsActivityHandler;
82
+ teamArchived?: TeamsActivityHandler;
83
+ teamUnarchived?: TeamsActivityHandler;
84
+ teamDeleted?: TeamsActivityHandler;
85
+ teamHardDeleted?: TeamsActivityHandler;
86
+ teamRestored?: TeamsActivityHandler;
87
+ }
88
+ interface TeamsMessageActivityHandlers {
89
+ messageEdited?: TeamsActivityHandler;
90
+ messageDeleted?: TeamsActivityHandler;
91
+ messageRestored?: TeamsActivityHandler;
92
+ reaction?: TeamsActivityHandler;
93
+ readReceipt?: TeamsActivityHandler;
94
+ }
95
+ interface TeamsCardActionHandlers {
96
+ cardSubmit?: TeamsActivityHandler;
97
+ feedbackSubmit?: TeamsActivityHandler;
98
+ fileConsent?: TeamsActivityHandler;
99
+ actionableMessageExecuteAction?: TeamsActivityHandler;
100
+ }
101
+ interface TeamsConfigHandlers {
102
+ configFetch?: TeamsActivityHandler;
103
+ configSubmit?: TeamsActivityHandler;
104
+ }
105
+ interface TeamsTaskModuleHandlers {
106
+ dialogOpen?: TeamsActivityHandler;
107
+ dialogSubmit?: TeamsActivityHandler;
108
+ taskFetch?: TeamsActivityHandler;
109
+ taskSubmit?: TeamsActivityHandler;
110
+ }
111
+ interface TeamsMessageExtensionHandlers {
112
+ searchCommand?: TeamsActivityHandler;
113
+ selectResult?: TeamsActivityHandler;
114
+ messageExtensionQuery?: TeamsActivityHandler;
115
+ messageExtensionQueryLink?: TeamsActivityHandler;
116
+ messageExtensionAnonymousQueryLink?: TeamsActivityHandler;
117
+ messageExtensionSelectItem?: TeamsActivityHandler;
118
+ messageExtensionSubmitAction?: TeamsActivityHandler;
119
+ messageExtensionFetchTask?: TeamsActivityHandler;
120
+ messageExtensionQuerySettingUrl?: TeamsActivityHandler;
121
+ messageExtensionSetting?: TeamsActivityHandler;
122
+ messageExtensionCardButtonClicked?: TeamsActivityHandler;
123
+ }
124
+ interface TeamsTabHandlers {
125
+ tabFetch?: TeamsActivityHandler;
126
+ tabSubmit?: TeamsActivityHandler;
127
+ }
128
+ interface TeamsMeetingHandlers {
129
+ /** Fired when a Teams meeting starts. */
130
+ meetingStart?: TeamsActivityHandler;
131
+ /** Fired when a Teams meeting ends. */
132
+ meetingEnd?: TeamsActivityHandler;
133
+ /** Fired when participants join a Teams meeting. */
134
+ participantsJoin?: TeamsActivityHandler;
135
+ /** Fired when participants leave a Teams meeting. */
136
+ participantsLeave?: TeamsActivityHandler;
137
+ /** Fired when a physical meeting room joins a Teams meeting. */
138
+ meetingRoomJoin?: TeamsActivityHandler;
139
+ /** Fired when a physical meeting room leaves a Teams meeting. */
140
+ meetingRoomLeave?: TeamsActivityHandler;
141
+ /** Fired when the app is viewed in the meeting stage (invoke). */
142
+ meetingStageView?: TeamsActivityHandler;
143
+ /** Fired for smart reply recommendations during a meeting (invoke). */
144
+ meetingSmartReply?: TeamsActivityHandler;
145
+ /** Fired for emoji reactions during a meeting. */
146
+ meetingReaction?: TeamsActivityHandler;
147
+ /** Fired when a poll response is submitted during a meeting. */
148
+ meetingPollResponse?: TeamsActivityHandler;
149
+ /** Fired when apps are installed during a meeting. */
150
+ meetingAppsInstalled?: TeamsActivityHandler;
151
+ /** Fired when apps are uninstalled during a meeting. */
152
+ meetingAppsUninstalled?: TeamsActivityHandler;
153
+ /** Fired when meeting recording starts. */
154
+ meetingRecordingStarted?: TeamsActivityHandler;
155
+ /** Fired when meeting recording stops. */
156
+ meetingRecordingStopped?: TeamsActivityHandler;
157
+ /** Fired when the meeting focus changes. */
158
+ meetingFocusChange?: TeamsActivityHandler;
159
+ /** Fired when screen sharing starts in a meeting. */
160
+ meetingScreenShareStart?: TeamsActivityHandler;
161
+ /** Fired when screen sharing stops in a meeting. */
162
+ meetingScreenShareStop?: TeamsActivityHandler;
163
+ }
164
+ /**
165
+ * Aggregate Teams handler surface grouped by capability.
166
+ *
167
+ * This keeps the public API centered on Teams concepts without exposing
168
+ * Microsoft's `AgentApplication` routing types.
169
+ */
170
+ interface TeamsActivityHandlers extends TeamsConversationUpdateHandlers, TeamsMessageActivityHandlers, TeamsCardActionHandlers, TeamsConfigHandlers, TeamsTaskModuleHandlers, TeamsMessageExtensionHandlers, TeamsTabHandlers, TeamsMeetingHandlers {
171
+ invoke?: TeamsActivityHandler;
70
172
  }
71
173
  interface TeamsChannelOptions extends Omit<M365ChannelOptions, "prepareTurn"> {
72
174
  /**
@@ -77,11 +179,21 @@ interface TeamsChannelOptions extends Omit<M365ChannelOptions, "prepareTurn"> {
77
179
  /**
78
180
  * Teams-specific activity hooks that run before generic M365 handling.
79
181
  *
80
- * Note: the `"feedback"` kind falls through to `cardSubmit` when
81
- * `feedbackSubmit` is not defined. Define `feedbackSubmit` explicitly
82
- * if you need to distinguish feedback actions from card submits.
182
+ * Notes:
183
+ * - the `"feedback"` kind falls through to `cardSubmit` when
184
+ * `feedbackSubmit` is not defined
185
+ * - specialized Teams invoke kinds fall back to grouped handlers such as
186
+ * `dialogOpen`, `searchCommand`, or `selectResult` when the narrow handler
187
+ * is not provided
83
188
  */
84
189
  handlers?: TeamsActivityHandlers;
190
+ /**
191
+ * General-purpose middleware that runs on every turn before cuylabs
192
+ * processing. Gives raw `TurnContext` access for Microsoft SDK helpers
193
+ * or custom logic. Call `next()` to continue into the normal handler
194
+ * pipeline, or return without calling it to short-circuit.
195
+ */
196
+ onTurn?: (context: TurnContext, next: () => Promise<void>) => Promise<void>;
85
197
  }
86
198
  interface TeamsChannelAdapter extends M365ChannelAdapter {
87
199
  /**
@@ -114,13 +226,6 @@ interface TeamsAmbientTurnContext extends M365AmbientTurnContext {
114
226
  declare function currentTeamsTurnContext(): Readonly<TeamsAmbientTurnContext> | undefined;
115
227
  declare function runWithTeamsTurnContext<T>(value: TeamsAmbientTurnContext, fn: () => T | Promise<T>): Promise<T>;
116
228
 
117
- interface TeamsInvokeResult<T = unknown> {
118
- status: number;
119
- body?: T;
120
- }
121
- declare function createTeamsInvokeActivity<T = unknown>(result: TeamsInvokeResult<T>): Activity;
122
- declare function sendTeamsInvoke<T = unknown>(context: TurnContext, body?: T, status?: number): Promise<void>;
123
-
124
229
  type TeamsSearchLayout = "list" | "grid";
125
230
  type TeamsSearchResultType = "result" | "auth" | "config" | "message" | "botMessagePreview" | "silentAuth";
126
231
  interface TeamsSearchAttachment extends Attachment {
@@ -177,6 +282,25 @@ interface TeamsDialogMessageBody {
177
282
  declare function createTeamsDialog(dialog: TeamsDialogDefinition): TeamsDialogResponse;
178
283
  declare function createTeamsDialogMessage(message: string): TeamsDialogMessageBody;
179
284
 
285
+ declare const TEAMS_CONFIG_FETCH_NAMES: readonly ["config/fetch"];
286
+ declare const TEAMS_CONFIG_SUBMIT_NAMES: readonly ["config/submit"];
287
+ declare const TEAMS_FILE_CONSENT_NAMES: readonly ["fileConsent/invoke"];
288
+ declare const TEAMS_ACTIONABLE_MESSAGE_NAMES: readonly ["actionableMessage/executeAction"];
289
+ declare const TEAMS_TASK_MODULE_FETCH_NAMES: readonly ["task/fetch", "composeExtension/fetchTask"];
290
+ declare const TEAMS_TASK_MODULE_SUBMIT_NAMES: readonly ["task/submit"];
291
+ declare const TEAMS_TAB_FETCH_NAMES: readonly ["tab/fetch"];
292
+ declare const TEAMS_TAB_SUBMIT_NAMES: readonly ["tab/submit"];
293
+ declare const TEAMS_MESSAGE_EXTENSION_QUERY_NAMES: readonly ["composeExtension/query", "composeExtension/queryLink", "composeExtension/anonymousQueryLink"];
294
+ declare const TEAMS_MESSAGE_EXTENSION_SUBMIT_ACTION_NAMES: readonly ["composeExtension/submitAction"];
295
+ declare const TEAMS_MESSAGE_EXTENSION_FETCH_TASK_NAMES: readonly ["composeExtension/fetchTask"];
296
+ declare const TEAMS_MESSAGE_EXTENSION_QUERY_SETTING_URL_NAMES: readonly ["composeExtension/querySettingUrl"];
297
+ declare const TEAMS_MESSAGE_EXTENSION_SETTING_NAMES: readonly ["composeExtension/setting"];
298
+ declare const TEAMS_MESSAGE_EXTENSION_CARD_BUTTON_CLICKED_NAMES: readonly ["composeExtension/onCardButtonClicked"];
299
+ declare const TEAMS_MESSAGE_EXTENSION_SELECT_NAMES: readonly ["composeExtension/selectItem"];
300
+ declare const TEAMS_CARD_ACTION_SUBMIT_NAMES: readonly ["message/submitAction"];
301
+ declare const TEAMS_CONVERSATION_UPDATE_EVENT_NAMES: Readonly<Record<string, TeamsActivityKind>>;
302
+ declare const TEAMS_MEETING_EVENT_NAMES: Readonly<Record<string, TeamsActivityKind>>;
303
+ declare const TEAMS_MEETING_INVOKE_NAMES: Readonly<Record<string, TeamsActivityKind>>;
180
304
  declare function parseTeamsActivity(context: TurnContext): TeamsActivityInfo;
181
305
 
182
- export { type MountTeamsAgentOptions, type MountTeamsAgentResult, type TeamsActivityHandlers, type TeamsActivityInfo, type TeamsActivityKind, type TeamsActorReference, type TeamsAmbientTurnContext, type TeamsChannelAdapter, type TeamsChannelOptions, type TeamsDialogDefinition, type TeamsDialogMessageBody, type TeamsDialogResponse, type TeamsDialogSize, type TeamsHandlerContext, type TeamsHandlerDecision, type TeamsInvokeResult, type TeamsSearchAttachment, type TeamsSearchLayout, type TeamsSearchMessageBody, type TeamsSearchResponse, type TeamsSearchResult, type TeamsSearchResultBody, type TeamsSearchResultType, type TeamsSurface, type TeamsTurnPreparation, type TeamsTurnRequestContext, createTeamsChannelAdapter, createTeamsDialog, createTeamsDialogMessage, createTeamsInvokeActivity, createTeamsSearchMessage, createTeamsSearchResult, currentTeamsTurnContext, mountTeamsAgent, parseTeamsActivity, runWithTeamsTurnContext, sendTeamsInvoke };
306
+ export { type MountTeamsAgentOptions, type MountTeamsAgentResult, TEAMS_ACTIONABLE_MESSAGE_NAMES, TEAMS_CARD_ACTION_SUBMIT_NAMES, TEAMS_CONFIG_FETCH_NAMES, TEAMS_CONFIG_SUBMIT_NAMES, TEAMS_CONVERSATION_UPDATE_EVENT_NAMES, TEAMS_FILE_CONSENT_NAMES, TEAMS_MEETING_EVENT_NAMES, TEAMS_MEETING_INVOKE_NAMES, TEAMS_MESSAGE_EXTENSION_CARD_BUTTON_CLICKED_NAMES, TEAMS_MESSAGE_EXTENSION_FETCH_TASK_NAMES, TEAMS_MESSAGE_EXTENSION_QUERY_NAMES, TEAMS_MESSAGE_EXTENSION_QUERY_SETTING_URL_NAMES, TEAMS_MESSAGE_EXTENSION_SELECT_NAMES, TEAMS_MESSAGE_EXTENSION_SETTING_NAMES, TEAMS_MESSAGE_EXTENSION_SUBMIT_ACTION_NAMES, TEAMS_TAB_FETCH_NAMES, TEAMS_TAB_SUBMIT_NAMES, TEAMS_TASK_MODULE_FETCH_NAMES, TEAMS_TASK_MODULE_SUBMIT_NAMES, type TeamsActivityHandler, type TeamsActivityHandlers, type TeamsActivityInfo, type TeamsActivityKind, type TeamsActorReference, type TeamsAmbientTurnContext, type TeamsCardActionHandlers, type TeamsChannelAdapter, type TeamsChannelOptions, type TeamsConfigHandlers, type TeamsConversationUpdateHandlers, type TeamsDialogDefinition, type TeamsDialogMessageBody, type TeamsDialogResponse, type TeamsDialogSize, type TeamsHandlerContext, type TeamsHandlerDecision, type TeamsInvokeResult, type TeamsMeetingHandlers, type TeamsMessageActivityHandlers, type TeamsMessageExtensionHandlers, type TeamsSearchAttachment, type TeamsSearchLayout, type TeamsSearchMessageBody, type TeamsSearchResponse, type TeamsSearchResult, type TeamsSearchResultBody, type TeamsSearchResultType, type TeamsSurface, type TeamsTabHandlers, type TeamsTaskModuleHandlers, type TeamsTurnPreparation, type TeamsTurnRequestContext, createTeamsChannelAdapter, createTeamsDialog, createTeamsDialogMessage, createTeamsInvokeActivity, createTeamsSearchMessage, createTeamsSearchResult, currentTeamsTurnContext, mountTeamsAgent, parseTeamsActivity, runWithTeamsTurnContext, sendTeamsInvoke };